@mack1ch/fingerprint-js 0.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/index.js ADDED
@@ -0,0 +1,999 @@
1
+ // src/fingerprint/defaults.ts
2
+ var DEFAULT_MODE = "balanced";
3
+ var DEFAULT_COLLECT_FLAGS = {
4
+ network: true,
5
+ webrtc: true,
6
+ permissions: true,
7
+ mediaDevices: true,
8
+ battery: false,
9
+ uaHints: true,
10
+ keyboard: false,
11
+ adBlock: true,
12
+ screenDetails: false,
13
+ storageEstimate: true
14
+ };
15
+ var MODE_PERFORMANCE = {
16
+ fast: {
17
+ mode: "fast",
18
+ timeoutMs: 1200,
19
+ maxConcurrency: 2,
20
+ cacheTtlMs: 6e4
21
+ },
22
+ balanced: {
23
+ mode: "balanced",
24
+ timeoutMs: 2500,
25
+ maxConcurrency: 4,
26
+ cacheTtlMs: 18e4
27
+ },
28
+ accurate: {
29
+ mode: "accurate",
30
+ timeoutMs: 4e3,
31
+ maxConcurrency: 6,
32
+ cacheTtlMs: 3e5
33
+ }
34
+ };
35
+ function resolvePerformance(options) {
36
+ const mode = (options == null ? void 0 : options.mode) ?? DEFAULT_MODE;
37
+ const base = MODE_PERFORMANCE[mode];
38
+ return {
39
+ mode,
40
+ timeoutMs: (options == null ? void 0 : options.timeoutMs) ?? base.timeoutMs,
41
+ maxConcurrency: (options == null ? void 0 : options.maxConcurrency) ?? base.maxConcurrency,
42
+ cacheTtlMs: (options == null ? void 0 : options.cacheTtlMs) ?? base.cacheTtlMs
43
+ };
44
+ }
45
+
46
+ // src/utils/collectUserData.ts
47
+ import { md5 } from "js-md5";
48
+ function item(category, key, label, value) {
49
+ return { category, key, label, value: String(value ?? "\u2014") };
50
+ }
51
+ function getWebGLInfo() {
52
+ var _a;
53
+ try {
54
+ const c = document.createElement("canvas");
55
+ const gl = c.getContext("webgl") || c.getContext("experimental-webgl");
56
+ if (!gl) return null;
57
+ const debugExt = gl.getExtension("WEBGL_debug_renderer_info");
58
+ const vendor = debugExt ? gl.getParameter(debugExt.UNMASKED_VENDOR_WEBGL) : null;
59
+ const renderer = debugExt ? gl.getParameter(debugExt.UNMASKED_RENDERER_WEBGL) : null;
60
+ const extList = [];
61
+ const EXTENSIONS = 7937;
62
+ try {
63
+ const supported = gl.getParameter(EXTENSIONS);
64
+ if (supported && typeof supported === "string") extList.push(...supported.split(" "));
65
+ else if (Array.isArray(supported)) extList.push(...supported);
66
+ } catch {
67
+ }
68
+ const params = {};
69
+ try {
70
+ params.MAX_TEXTURE_SIZE = gl.getParameter(gl.MAX_TEXTURE_SIZE);
71
+ params.MAX_VIEWPORT_DIMS = gl.getParameter(gl.MAX_VIEWPORT_DIMS).join("x");
72
+ const lineRange = gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE);
73
+ if (lineRange == null ? void 0 : lineRange.length) params.ALIASED_LINE_WIDTH_RANGE = `${lineRange[0]}..${lineRange[1]}`;
74
+ const VERSION = 7938;
75
+ const v = gl.getParameter(VERSION);
76
+ if (v) params.VERSION = v;
77
+ params.MAX_RENDERBUFFER_SIZE = gl.getParameter(34024);
78
+ params.MAX_CUBE_MAP_TEXTURE_SIZE = gl.getParameter(34076);
79
+ params.MAX_VERTEX_ATTRIBS = gl.getParameter(34921);
80
+ const vuv = gl.getParameter(36347);
81
+ if (vuv != null) params.MAX_VERTEX_UNIFORM_VECTORS = vuv;
82
+ const fuv = gl.getParameter(36349);
83
+ if (fuv != null) params.MAX_FRAGMENT_UNIFORM_VECTORS = fuv;
84
+ } catch {
85
+ }
86
+ return {
87
+ vendor: vendor ?? "\u2014",
88
+ renderer: renderer ?? "\u2014",
89
+ extensions: extList.length ? extList : ((_a = gl.getParameter(EXTENSIONS)) == null ? void 0 : _a.split(" ")) ?? [],
90
+ params
91
+ };
92
+ } catch {
93
+ return null;
94
+ }
95
+ }
96
+ function getCanvasFingerprint() {
97
+ try {
98
+ const c = document.createElement("canvas");
99
+ c.width = 220;
100
+ c.height = 30;
101
+ const ctx = c.getContext("2d");
102
+ if (!ctx) return "\u2014";
103
+ const txt = "BrowserLeaks,com <canvas> 1.0";
104
+ ctx.textBaseline = "top";
105
+ ctx.font = "14px 'Arial'";
106
+ ctx.textBaseline = "alphabetic";
107
+ ctx.fillStyle = "#f60";
108
+ ctx.fillRect(125, 1, 62, 20);
109
+ ctx.fillStyle = "#069";
110
+ ctx.fillText(txt, 2, 15);
111
+ ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
112
+ ctx.fillText(txt, 4, 17);
113
+ const dataUrl = c.toDataURL("image/png");
114
+ return md5(dataUrl).toUpperCase();
115
+ } catch {
116
+ return "\u2014";
117
+ }
118
+ }
119
+ function getAudioFingerprint() {
120
+ try {
121
+ const ctx = new (window.AudioContext || window.webkitAudioContext)();
122
+ const sampleRate = ctx.sampleRate;
123
+ const buffer = ctx.createBuffer(1, 4096, sampleRate);
124
+ const data = buffer.getChannelData(0);
125
+ for (let i = 0; i < data.length; i++) data[i] = Math.sin(i * 0.1);
126
+ const offline = new OfflineAudioContext(1, 4096, sampleRate);
127
+ const src = offline.createBufferSource();
128
+ src.buffer = buffer;
129
+ src.connect(offline.destination);
130
+ src.start(0);
131
+ return offline.sampleRate + "-" + buffer.length;
132
+ } catch {
133
+ try {
134
+ const ctx = new AudioContext();
135
+ let hash = 0;
136
+ hash = (hash << 5) - hash + (ctx.sampleRate || 0) | 0;
137
+ hash = (hash << 5) - hash + (ctx.baseLatency || 0) * 1e3 | 0;
138
+ return (hash >>> 0).toString(16);
139
+ } catch {
140
+ return "\u2014";
141
+ }
142
+ }
143
+ }
144
+ var FONT_SAMPLE_LIST = [
145
+ "Arial",
146
+ "Arial Black",
147
+ "Courier New",
148
+ "Georgia",
149
+ "Times New Roman",
150
+ "Verdana",
151
+ "Helvetica",
152
+ "Comic Sans MS",
153
+ "Impact",
154
+ "Trebuchet MS",
155
+ "Lucida Console",
156
+ "Palatino Linotype",
157
+ "Tahoma",
158
+ "Cambria",
159
+ "Calibri",
160
+ "Consolas",
161
+ "Monaco",
162
+ "Menlo",
163
+ "Roboto",
164
+ "Open Sans",
165
+ "Segoe UI",
166
+ "system-ui"
167
+ ];
168
+ function getFontsPresent() {
169
+ const present = [];
170
+ if (typeof document === "undefined" || !document.body) return present;
171
+ const test = document.createElement("span");
172
+ test.style.position = "absolute";
173
+ test.style.left = "-9999px";
174
+ test.style.fontSize = "72px";
175
+ test.textContent = "mmmmmmmmmmlli";
176
+ document.body.appendChild(test);
177
+ const baseWidth = test.offsetWidth;
178
+ for (const font of FONT_SAMPLE_LIST) {
179
+ test.style.fontFamily = `'${font}', monospace`;
180
+ if (test.offsetWidth !== baseWidth) present.push(font);
181
+ }
182
+ document.body.removeChild(test);
183
+ return present;
184
+ }
185
+ function getPreferredColorScheme() {
186
+ if (typeof window === "undefined" || !window.matchMedia) return "\u2014";
187
+ const dark = window.matchMedia("(prefers-color-scheme: dark)").matches;
188
+ const light = window.matchMedia("(prefers-color-scheme: light)").matches;
189
+ if (dark) return "dark";
190
+ if (light) return "light";
191
+ return "no preference";
192
+ }
193
+ function collectUserData() {
194
+ var _a, _b, _c, _d, _e;
195
+ const nav = typeof navigator !== "undefined" ? navigator : null;
196
+ const screen = typeof window !== "undefined" ? window.screen : null;
197
+ const doc = typeof document !== "undefined" ? document : null;
198
+ const loc = typeof location !== "undefined" ? location : null;
199
+ const data = [];
200
+ if (nav) {
201
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "userAgent", "User Agent", nav.userAgent));
202
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "appVersion", "App Version", nav.appVersion ?? "\u2014"));
203
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "appName", "App Name", nav.appName ?? "\u2014"));
204
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "appCodeName", "App Code Name", nav.appCodeName ?? "\u2014"));
205
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "product", "Product", nav.product ?? "\u2014"));
206
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "productSub", "Product Sub", nav.productSub ?? "\u2014"));
207
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "language", "\u042F\u0437\u044B\u043A", nav.language));
208
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "languages", "\u042F\u0437\u044B\u043A\u0438", ((_a = nav.languages) == null ? void 0 : _a.join(", ")) ?? "\u2014"));
209
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "platform", "\u041F\u043B\u0430\u0442\u0444\u043E\u0440\u043C\u0430", nav.platform));
210
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "cookieEnabled", "Cookies \u0432\u043A\u043B\u044E\u0447\u0435\u043D\u044B", nav.cookieEnabled));
211
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "doNotTrack", "Do Not Track", nav.doNotTrack ?? "\u2014"));
212
+ data.push(item("\u0410\u043F\u043F\u0430\u0440\u0430\u0442\u043D\u044B\u0435", "hardwareConcurrency", "\u041B\u043E\u0433\u0438\u0447\u0435\u0441\u043A\u0438\u0445 \u043F\u0440\u043E\u0446\u0435\u0441\u0441\u043E\u0440\u043E\u0432 (\u044F\u0434\u0440\u0430)", nav.hardwareConcurrency ?? "\u2014"));
213
+ data.push(item("\u0410\u043F\u043F\u0430\u0440\u0430\u0442\u043D\u044B\u0435", "deviceMemory", "\u041E\u0417\u0423 (GB, \u043F\u0440\u0438\u0431\u043B\u0438\u0437\u0438\u0442\u0435\u043B\u044C\u043D\u043E)", nav.deviceMemory ?? "\u2014"));
214
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "maxTouchPoints", "\u0422\u043E\u0447\u0435\u043A \u043A\u0430\u0441\u0430\u043D\u0438\u044F", nav.maxTouchPoints));
215
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "vendor", "\u0412\u0435\u043D\u0434\u043E\u0440", nav.vendor));
216
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "pdfViewerEnabled", "PDF \u0432 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435", nav.pdfViewerEnabled ?? "\u2014"));
217
+ data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "onLine", "\u041E\u043D\u043B\u0430\u0439\u043D", nav.onLine));
218
+ const plugs = nav.plugins;
219
+ if (plugs) data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "pluginsCount", "\u041F\u043B\u0430\u0433\u0438\u043D\u043E\u0432 (plugins.length)", plugs.length));
220
+ const mimes = nav.mimeTypes;
221
+ if (mimes) data.push(item("\u0411\u0440\u0430\u0443\u0437\u0435\u0440", "mimeTypesCount", "MIME-\u0442\u0438\u043F\u043E\u0432 (mimeTypes.length)", mimes.length));
222
+ }
223
+ const webgl = getWebGLInfo();
224
+ if (webgl) {
225
+ data.push(item("GPU (WebGL)", "unmaskedVendor", "Unmasked Vendor", webgl.vendor));
226
+ data.push(item("GPU (WebGL)", "unmaskedRenderer", "Unmasked Renderer", webgl.renderer));
227
+ const extStr = webgl.extensions.length ? webgl.extensions.join(", ") : "\u2014";
228
+ if (webgl.extensions.length > 0) {
229
+ data.push(item("GPU (WebGL)", "extensionsCount", "\u041A\u043E\u043B-\u0432\u043E \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u0439 WebGL", webgl.extensions.length));
230
+ data.push(item("GPU (WebGL)", "extensions", "\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043D\u0438\u044F WebGL", extStr.length > 300 ? extStr.slice(0, 300) + "\u2026" : extStr));
231
+ }
232
+ Object.entries(webgl.params).forEach(([k, v]) => {
233
+ data.push(item("WebGL \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B", k, k, v));
234
+ });
235
+ }
236
+ try {
237
+ const actx = new (window.AudioContext || window.webkitAudioContext)();
238
+ data.push(item("\u0410\u0443\u0434\u0438\u043E", "sampleRate", "\u0427\u0430\u0441\u0442\u043E\u0442\u0430 \u0434\u0438\u0441\u043A\u0440\u0435\u0442\u0438\u0437\u0430\u0446\u0438\u0438", actx.sampleRate));
239
+ data.push(item("\u0410\u0443\u0434\u0438\u043E", "state", "\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 AudioContext", actx.state));
240
+ data.push(item("\u0410\u0443\u0434\u0438\u043E", "baseLatency", "\u0411\u0430\u0437\u043E\u0432\u0430\u044F \u0437\u0430\u0434\u0435\u0440\u0436\u043A\u0430 (\u0441)", actx.baseLatency));
241
+ if (actx.close) actx.close();
242
+ } catch {
243
+ data.push(item("\u0410\u0443\u0434\u0438\u043E", "audioContext", "AudioContext", "\u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D"));
244
+ }
245
+ const sensors = {
246
+ DeviceMotionEvent: typeof DeviceMotionEvent !== "undefined",
247
+ DeviceOrientationEvent: typeof DeviceOrientationEvent !== "undefined",
248
+ AmbientLightSensor: typeof window.AmbientLightSensor !== "undefined"
249
+ };
250
+ data.push(item("\u0414\u0430\u0442\u0447\u0438\u043A\u0438", "deviceMotion", "\u0410\u043A\u0441\u0435\u043B\u0435\u0440\u043E\u043C\u0435\u0442\u0440 (DeviceMotion)", sensors.DeviceMotionEvent));
251
+ data.push(item("\u0414\u0430\u0442\u0447\u0438\u043A\u0438", "deviceOrientation", "\u0413\u0438\u0440\u043E\u0441\u043A\u043E\u043F/\u043E\u0440\u0438\u0435\u043D\u0442\u0430\u0446\u0438\u044F (DeviceOrientation)", sensors.DeviceOrientationEvent));
252
+ data.push(item("\u0414\u0430\u0442\u0447\u0438\u043A\u0438", "ambientLight", "\u0414\u0430\u0442\u0447\u0438\u043A \u043E\u0441\u0432\u0435\u0449\u0451\u043D\u043D\u043E\u0441\u0442\u0438 (AmbientLightSensor)", sensors.AmbientLightSensor));
253
+ if (screen) {
254
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "width", "\u0428\u0438\u0440\u0438\u043D\u0430 \u044D\u043A\u0440\u0430\u043D\u0430", screen.width));
255
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "height", "\u0412\u044B\u0441\u043E\u0442\u0430 \u044D\u043A\u0440\u0430\u043D\u0430", screen.height));
256
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "availWidth", "\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u0430\u044F \u0448\u0438\u0440\u0438\u043D\u0430", screen.availWidth));
257
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "availHeight", "\u0414\u043E\u0441\u0442\u0443\u043F\u043D\u0430\u044F \u0432\u044B\u0441\u043E\u0442\u0430", screen.availHeight));
258
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "availTop", "\u041E\u0442\u0441\u0442\u0443\u043F \u0441\u0432\u0435\u0440\u0445\u0443", screen.availTop ?? "\u2014"));
259
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "availLeft", "\u041E\u0442\u0441\u0442\u0443\u043F \u0441\u043B\u0435\u0432\u0430", screen.availLeft ?? "\u2014"));
260
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "colorDepth", "\u0413\u043B\u0443\u0431\u0438\u043D\u0430 \u0446\u0432\u0435\u0442\u0430", screen.colorDepth));
261
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "pixelDepth", "\u0413\u043B\u0443\u0431\u0438\u043D\u0430 \u043F\u0438\u043A\u0441\u0435\u043B\u044F", screen.pixelDepth));
262
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "orientation", "\u041E\u0440\u0438\u0435\u043D\u0442\u0430\u0446\u0438\u044F", ((_b = screen.orientation) == null ? void 0 : _b.type) ?? "\u2014"));
263
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "devicePixelRatio", "\u041C\u0430\u0441\u0448\u0442\u0430\u0431\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0435 \u041E\u0421 (DPR)", typeof window !== "undefined" ? window.devicePixelRatio : "\u2014"));
264
+ }
265
+ if (typeof window !== "undefined" && window.matchMedia) {
266
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "colorGamutP3", "\u0426\u0432\u0435\u0442\u043E\u0432\u043E\u0439 \u043E\u0445\u0432\u0430\u0442 P3", window.matchMedia("(color-gamut: p3)").matches));
267
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "colorGamutRec2020", "\u0426\u0432\u0435\u0442\u043E\u0432\u043E\u0439 \u043E\u0445\u0432\u0430\u0442 Rec.2020", window.matchMedia("(color-gamut: rec2020)").matches));
268
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "colorGamutSrgb", "sRGB", window.matchMedia("(color-gamut: srgb)").matches));
269
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "hdr", "HDR (dynamic-range: high)", window.matchMedia("(dynamic-range: high)").matches));
270
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "invertedColors", "\u0418\u043D\u0432\u0435\u0440\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u043D\u044B\u0435 \u0446\u0432\u0435\u0442\u0430", window.matchMedia("(inverted-colors: inverted)").matches));
271
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "forcedColors", "\u041F\u0440\u0438\u043D\u0443\u0434\u0438\u0442\u0435\u043B\u044C\u043D\u044B\u0435 \u0446\u0432\u0435\u0442\u0430 (forced-colors)", window.matchMedia("(forced-colors: active)").matches));
272
+ }
273
+ if (screen && screen.isExtended !== void 0) {
274
+ data.push(item("\u042D\u043A\u0440\u0430\u043D", "isExtended", "\u041D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u043C\u043E\u043D\u0438\u0442\u043E\u0440\u043E\u0432 (isExtended)", screen.isExtended ?? false));
275
+ }
276
+ if (typeof window !== "undefined") {
277
+ data.push(item("\u041E\u043A\u043D\u043E", "innerWidth", "\u0428\u0438\u0440\u0438\u043D\u0430 \u043E\u043A\u043D\u0430", window.innerWidth));
278
+ data.push(item("\u041E\u043A\u043D\u043E", "innerHeight", "\u0412\u044B\u0441\u043E\u0442\u0430 \u043E\u043A\u043D\u0430", window.innerHeight));
279
+ data.push(item("\u041E\u043A\u043D\u043E", "outerWidth", "\u0412\u043D\u0435\u0448\u043D\u044F\u044F \u0448\u0438\u0440\u0438\u043D\u0430", window.outerWidth));
280
+ data.push(item("\u041E\u043A\u043D\u043E", "outerHeight", "\u0412\u043D\u0435\u0448\u043D\u044F\u044F \u0432\u044B\u0441\u043E\u0442\u0430", window.outerHeight));
281
+ data.push(item("\u041E\u043A\u043D\u043E", "screenX", "\u041F\u043E\u0437\u0438\u0446\u0438\u044F X (screenX)", window.screenX));
282
+ data.push(item("\u041E\u043A\u043D\u043E", "screenY", "\u041F\u043E\u0437\u0438\u0446\u0438\u044F Y (screenY)", window.screenY));
283
+ data.push(item("\u041E\u043A\u043D\u043E", "screenLeft", "\u041F\u043E\u0437\u0438\u0446\u0438\u044F \u0441\u043B\u0435\u0432\u0430 (screenLeft)", window.screenLeft ?? window.screenX));
284
+ data.push(item("\u041E\u043A\u043D\u043E", "screenTop", "\u041F\u043E\u0437\u0438\u0446\u0438\u044F \u0441\u0432\u0435\u0440\u0445\u0443 (screenTop)", window.screenTop ?? window.screenY));
285
+ let scrollbarW = "\u2014";
286
+ try {
287
+ if (document.documentElement) {
288
+ const w2 = document.documentElement.offsetWidth - document.documentElement.clientWidth;
289
+ if (w2 >= 0 && w2 <= 30) scrollbarW = w2;
290
+ }
291
+ } catch {
292
+ }
293
+ data.push(item("\u041E\u043A\u043D\u043E", "scrollbarWidth", "\u0428\u0438\u0440\u0438\u043D\u0430 \u0441\u043A\u0440\u043E\u043B\u043B\u0431\u0430\u0440\u0430 (\u043F\u0440\u0438\u0431\u043B.)", scrollbarW));
294
+ data.push(item("\u041E\u043A\u043D\u043E", "frameCount", "\u041A\u043E\u043B-\u0432\u043E \u0444\u0440\u0435\u0439\u043C\u043E\u0432 (window.length)", window.length));
295
+ data.push(item("\u041E\u043A\u043D\u043E", "windowName", "\u0418\u043C\u044F \u043E\u043A\u043D\u0430 (window.name)", window.name || "" || "\u2014"));
296
+ data.push(item("\u041E\u043A\u043D\u043E", "inIframe", "\u0412 iframe", window.self !== window.top));
297
+ }
298
+ try {
299
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
300
+ data.push(item("\u0412\u0440\u0435\u043C\u044F", "timeZone", "\u0427\u0430\u0441\u043E\u0432\u043E\u0439 \u043F\u043E\u044F\u0441", tz));
301
+ data.push(item("\u0412\u0440\u0435\u043C\u044F", "timeZoneOffset", "\u0421\u043C\u0435\u0449\u0435\u043D\u0438\u0435 (\u043C\u0438\u043D)", (/* @__PURE__ */ new Date()).getTimezoneOffset()));
302
+ data.push(item("\u0412\u0440\u0435\u043C\u044F", "localTime", "\u041B\u043E\u043A\u0430\u043B\u044C\u043D\u043E\u0435 \u0432\u0440\u0435\u043C\u044F", (/* @__PURE__ */ new Date()).toLocaleString()));
303
+ } catch {
304
+ data.push(item("\u0412\u0440\u0435\u043C\u044F", "timeZone", "\u0427\u0430\u0441\u043E\u0432\u043E\u0439 \u043F\u043E\u044F\u0441", "\u2014"));
305
+ }
306
+ if (typeof window !== "undefined" && window.matchMedia) {
307
+ data.push(item("\u041F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F", "colorScheme", "\u0426\u0432\u0435\u0442\u043E\u0432\u0430\u044F \u0441\u0445\u0435\u043C\u0430", getPreferredColorScheme()));
308
+ data.push(item("\u041F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F", "reducedMotion", "\u0423\u043C\u0435\u043D\u044C\u0448\u0435\u043D\u043D\u043E\u0435 \u0434\u0432\u0438\u0436\u0435\u043D\u0438\u0435", window.matchMedia("(prefers-reduced-motion: reduce)").matches ? "\u0434\u0430" : "\u043D\u0435\u0442"));
309
+ data.push(item("\u041F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F", "reducedTransparency", "\u0423\u043C\u0435\u043D\u044C\u0448\u0435\u043D\u043D\u0430\u044F \u043F\u0440\u043E\u0437\u0440\u0430\u0447\u043D\u043E\u0441\u0442\u044C", window.matchMedia("(prefers-reduced-transparency: reduce)").matches ? "\u0434\u0430" : "\u043D\u0435\u0442"));
310
+ data.push(item("\u041F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F", "reducedData", "\u041C\u0435\u043D\u044C\u0448\u0435 \u0434\u0430\u043D\u043D\u044B\u0445 (prefers-reduced-data)", window.matchMedia("(prefers-reduced-data: reduce)").matches ? "\u0434\u0430" : "\u043D\u0435\u0442"));
311
+ data.push(item("\u041F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F", "prefersContrast", "\u041A\u043E\u043D\u0442\u0440\u0430\u0441\u0442 (prefers-contrast)", window.matchMedia("(prefers-contrast: more)").matches ? "more" : window.matchMedia("(prefers-contrast: less)").matches ? "less" : "no-preference"));
312
+ data.push(item("\u041F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F", "hoverNone", "\u0411\u0435\u0437 \u043D\u0430\u0432\u0435\u0434\u0435\u043D\u0438\u044F (hover: none)", window.matchMedia("(hover: none)").matches));
313
+ data.push(item("\u041F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F", "pointerCoarse", "\u0423\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C \u0433\u0440\u0443\u0431\u044B\u0439 (touch)", window.matchMedia("(pointer: coarse)").matches));
314
+ data.push(item("\u041F\u0440\u0435\u0434\u043F\u043E\u0447\u0442\u0435\u043D\u0438\u044F", "pointerFine", "\u0423\u043A\u0430\u0437\u0430\u0442\u0435\u043B\u044C \u0442\u043E\u0447\u043D\u044B\u0439 (\u043C\u044B\u0448\u044C)", window.matchMedia("(pointer: fine)").matches));
315
+ }
316
+ try {
317
+ const ls = typeof localStorage !== "undefined";
318
+ const ss = typeof sessionStorage !== "undefined";
319
+ const idb = typeof indexedDB !== "undefined";
320
+ data.push(item("\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435", "localStorage", "localStorage", ls ? "\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E" : "\u043D\u0435\u0442"));
321
+ data.push(item("\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435", "sessionStorage", "sessionStorage", ss ? "\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E" : "\u043D\u0435\u0442"));
322
+ data.push(item("\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435", "indexedDB", "IndexedDB", idb ? "\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E" : "\u043D\u0435\u0442"));
323
+ if (ls) {
324
+ let count = 0;
325
+ try {
326
+ count = localStorage.length;
327
+ } catch {
328
+ count = -1;
329
+ }
330
+ data.push(item("\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435", "localStorageKeys", "\u041A\u043B\u044E\u0447\u0435\u0439 \u0432 localStorage", count >= 0 ? count : "\u2014"));
331
+ }
332
+ } catch {
333
+ data.push(item("\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435", "storage", "\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435", "\u2014"));
334
+ }
335
+ if (typeof history !== "undefined") {
336
+ data.push(item("\u0418\u0441\u0442\u043E\u0440\u0438\u044F", "historyLength", "\u0417\u0430\u043F\u0438\u0441\u0435\u0439 \u0432 history", history.length));
337
+ }
338
+ const conn = typeof navigator !== "undefined" && navigator.connection;
339
+ if (conn) {
340
+ data.push(item("\u0421\u0435\u0442\u044C", "effectiveType", "\u0422\u0438\u043F \u0441\u043E\u0435\u0434\u0438\u043D\u0435\u043D\u0438\u044F (4G/WiFi \u0438 \u0442.\u0434.)", conn.effectiveType ?? "\u2014"));
341
+ data.push(item("\u0421\u0435\u0442\u044C", "downlink", "\u0421\u043A\u043E\u0440\u043E\u0441\u0442\u044C (Mbps)", conn.downlink ?? "\u2014"));
342
+ data.push(item("\u0421\u0435\u0442\u044C", "rtt", "RTT (\u043C\u0441)", conn.rtt ?? "\u2014"));
343
+ data.push(item("\u0421\u0435\u0442\u044C", "saveData", "\u0420\u0435\u0436\u0438\u043C \u044D\u043A\u043E\u043D\u043E\u043C\u0438\u0438 \u0434\u0430\u043D\u043D\u044B\u0445", conn.saveData ?? false));
344
+ }
345
+ try {
346
+ const link = document.createElement("link");
347
+ link.rel = "dns-prefetch";
348
+ data.push(item("\u0421\u0435\u0442\u044C", "dnsPrefetch", "\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0430 DNS Prefetch", link.rel === "dns-prefetch"));
349
+ } catch {
350
+ data.push(item("\u0421\u0435\u0442\u044C", "dnsPrefetch", "\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0430 DNS Prefetch", "\u2014"));
351
+ }
352
+ data.push(item("\u041E\u0442\u043F\u0435\u0447\u0430\u0442\u043E\u043A", "canvasHash", "Canvas Fingerprint", getCanvasFingerprint()));
353
+ data.push(item("\u041E\u0442\u043F\u0435\u0447\u0430\u0442\u043E\u043A", "audioFingerprint", "Audio Fingerprint", getAudioFingerprint()));
354
+ try {
355
+ const fonts = getFontsPresent();
356
+ data.push(item("\u041E\u0442\u043F\u0435\u0447\u0430\u0442\u043E\u043A", "fontsDetected", "\u041E\u0431\u043D\u0430\u0440\u0443\u0436\u0435\u043D\u043E \u0448\u0440\u0438\u0444\u0442\u043E\u0432 (\u0432\u044B\u0431\u043E\u0440\u043A\u0430)", fonts.length));
357
+ if (fonts.length > 0) data.push(item("\u041E\u0442\u043F\u0435\u0447\u0430\u0442\u043E\u043A", "fontsList", "\u0428\u0440\u0438\u0444\u0442\u044B", fonts.join(", ")));
358
+ } catch {
359
+ data.push(item("\u041E\u0442\u043F\u0435\u0447\u0430\u0442\u043E\u043A", "fontsDetected", "\u0428\u0440\u0438\u0444\u0442\u044B", "\u2014"));
360
+ }
361
+ data.push(item("\u0412\u0432\u043E\u0434", "maxTouchPoints", "\u0422\u043E\u0447\u0435\u043A \u043A\u0430\u0441\u0430\u043D\u0438\u044F", (nav == null ? void 0 : nav.maxTouchPoints) ?? "\u2014"));
362
+ data.push(item("\u0412\u0432\u043E\u0434", "touchSupport", "\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u043A\u0430 touch ('ontouchstart' in window)", typeof window !== "undefined" && "ontouchstart" in window));
363
+ data.push(item("\u0412\u0432\u043E\u0434", "pointerEvents", "Pointer Events", typeof window !== "undefined" && typeof PointerEvent !== "undefined"));
364
+ if (typeof navigator !== "undefined" && navigator.getGamepads) {
365
+ const pads = navigator.getGamepads();
366
+ const connected = pads ? Array.from(pads).filter((p) => p != null && p.connected) : [];
367
+ data.push(item("\u0412\u0432\u043E\u0434", "gamepadsCount", "\u041F\u043E\u0434\u043A\u043B\u044E\u0447\u0451\u043D\u043D\u044B\u0445 \u0433\u0435\u0439\u043C\u043F\u0430\u0434\u043E\u0432", connected.length));
368
+ connected.forEach((p, i) => {
369
+ if (p) data.push(item("\u0412\u0432\u043E\u0434", `gamepad_${i}`, `\u0413\u0435\u0439\u043C\u043F\u0430\u0434 ${i} (ID)`, p.id || "\u2014"));
370
+ });
371
+ }
372
+ const w = typeof window !== "undefined" ? window : null;
373
+ const docGlobal = typeof document !== "undefined" ? document : null;
374
+ const winBot = w;
375
+ const nd = navigator;
376
+ const hasPlaywright = !!((winBot == null ? void 0 : winBot.__playwright) ?? (typeof w !== "undefined" && w !== null && w["__playwright"] != null));
377
+ let hasPwPrefix = false;
378
+ try {
379
+ if (w && typeof Object.keys === "function") hasPwPrefix = Object.keys(w).some((k) => k.startsWith("__pw_"));
380
+ } catch {
381
+ }
382
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "webdriver", "navigator.webdriver (\u044F\u0432\u043D\u044B\u0439 \u0444\u043B\u0430\u0433)", !!nd.webdriver));
383
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "playwright", "Playwright (__playwright / __pw_*)", hasPlaywright || hasPwPrefix));
384
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "puppeteer", "Puppeteer (puppeteer / __puppeteer_eval*)", !!((winBot == null ? void 0 : winBot.puppeteer) ?? (winBot == null ? void 0 : winBot.__puppeteer_evaluation_script__) ?? (winBot == null ? void 0 : winBot.__puppeteer_evaluation_script_rejected__))));
385
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "phantom", "Phantom (callPhantom / _phantom / __phantom)", !!((winBot == null ? void 0 : winBot.callPhantom) ?? (winBot == null ? void 0 : winBot._phantom) ?? (winBot == null ? void 0 : winBot.__phantom))));
386
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "nightmare", "Nightmare / Phantomas", !!((winBot == null ? void 0 : winBot.__nightmare) ?? (winBot == null ? void 0 : winBot.__phantomas))));
387
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "selenium", "Selenium (driver / __driver_* / _selenium)", !!(nd.__driver_evaluate ?? nd.__webdriver_evaluate ?? nd.__selenium_unwrapped ?? nd._selenium ?? nd.driver)));
388
+ let hasChromeCdc = false;
389
+ try {
390
+ if (docGlobal && typeof Object.keys === "function") hasChromeCdc = Object.keys(docGlobal).some((k) => k.startsWith("$cdc_") || k.startsWith("$chrome_"));
391
+ } catch {
392
+ }
393
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "chromeCdc", "Chrome CDC (document.$cdc_* / $chrome_*)", hasChromeCdc));
394
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "pluginsZero", "\u041F\u043B\u0430\u0433\u0438\u043D\u043E\u0432 0 (\u0442\u0438\u043F\u0438\u0447\u043D\u043E \u0434\u043B\u044F headless)", !!(nd.plugins && nd.plugins.length === 0)));
395
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "languagesEmpty", "\u042F\u0437\u044B\u043A\u043E\u0432 0 \u0438\u043B\u0438 1 (\u043F\u043E\u0434\u043E\u0437\u0440\u0438\u0442\u0435\u043B\u044C\u043D\u043E)", !!(nd.languages && (nd.languages.length === 0 || nd.languages.length === 1))));
396
+ const noChromeRt = typeof w !== "undefined" && !((_c = w.chrome) == null ? void 0 : _c.runtime) && /Chrome/.test(navigator.userAgent);
397
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "noChromeRuntime", "\u041D\u0435\u0442 window.chrome.runtime (Chrome headless)", noChromeRt));
398
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "screenTinyOrZero", "\u042D\u043A\u0440\u0430\u043D 0x0 \u0438\u043B\u0438 800x600 (headless)", !!(screen && (screen.width === 0 || screen.width === 800 && screen.height === 600))));
399
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "noOuterChrome", "\u041D\u0435\u0442 \xAB\u0440\u0430\u043C\u043A\u0438\xBB \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 (outer\u2248inner)", !!(typeof window !== "undefined" && Math.abs(window.outerWidth - window.innerWidth) <= 16 && Math.abs(window.outerHeight - window.innerHeight) <= 16)));
400
+ if (webgl) {
401
+ const r = (webgl.renderer || "").toLowerCase();
402
+ const softRender = r.includes("swiftshader") || r.includes("mesa") || r.includes("llvmpipe") || r.includes("software");
403
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "webglSoftware", "WebGL \u0441\u043E\u0444\u0442-\u0440\u0435\u043D\u0434\u0435\u0440 (SwiftShader/Mesa)", softRender));
404
+ }
405
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "noPermissionsAPI", "\u041D\u0435\u0442 Permissions API", typeof navigator !== "undefined" && navigator.permissions == null));
406
+ const botFlags = [
407
+ !!nd.webdriver,
408
+ hasPlaywright || hasPwPrefix,
409
+ !!((winBot == null ? void 0 : winBot.puppeteer) ?? (winBot == null ? void 0 : winBot.__puppeteer_evaluation_script__)),
410
+ !!((winBot == null ? void 0 : winBot.callPhantom) ?? (winBot == null ? void 0 : winBot._phantom)),
411
+ !!((winBot == null ? void 0 : winBot.__nightmare) ?? (winBot == null ? void 0 : winBot.__phantomas)),
412
+ !!(nd.__driver_evaluate ?? nd.__webdriver_evaluate ?? nd._selenium ?? nd.driver),
413
+ hasChromeCdc
414
+ ].filter(Boolean).length;
415
+ const weakFlags = [
416
+ ((_d = nd.plugins) == null ? void 0 : _d.length) === 0,
417
+ nd.languages && (nd.languages.length === 0 || nd.languages.length === 1),
418
+ screen && (screen.width === 0 || screen.width === 800 && screen.height === 600),
419
+ typeof window !== "undefined" && Math.abs(window.outerWidth - window.innerWidth) <= 16,
420
+ webgl ? /swiftshader|mesa|llvmpipe|software/.test((webgl.renderer || "").toLowerCase()) : false
421
+ ].filter(Boolean).length;
422
+ let botSummary = "\u043D\u0438\u0437\u043A\u0430\u044F";
423
+ if (botFlags >= 1) botSummary = "\u0432\u044B\u0441\u043E\u043A\u0430\u044F (\u0435\u0441\u0442\u044C \u044F\u0432\u043D\u044B\u0435 \u0444\u043B\u0430\u0433\u0438 \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u0438)";
424
+ else if (botFlags === 0 && weakFlags >= 3) botSummary = "\u0441\u0440\u0435\u0434\u043D\u044F\u044F (\u043D\u0435\u0441\u043A\u043E\u043B\u044C\u043A\u043E \u043A\u043E\u0441\u0432\u0435\u043D\u043D\u044B\u0445 \u043F\u0440\u0438\u0437\u043D\u0430\u043A\u043E\u0432)";
425
+ else if (weakFlags >= 1) botSummary = "\u043D\u0438\u0437\u043A\u0430\u044F (1\u20132 \u043A\u043E\u0441\u0432\u0435\u043D\u043D\u044B\u0445 \u043F\u0440\u0438\u0437\u043D\u0430\u043A\u0430)";
426
+ data.push(item("\u0411\u043E\u0442 / \u0430\u0432\u0442\u043E\u043C\u0430\u0442\u0438\u0437\u0430\u0446\u0438\u044F", "botRiskSummary", "\u0412\u0435\u0440\u043E\u044F\u0442\u043D\u043E\u0441\u0442\u044C \u0431\u043E\u0442\u0430 (\u044D\u0432\u0440\u0438\u0441\u0442\u0438\u043A\u0430)", botSummary));
427
+ if (typeof window !== "undefined" && window.matchMedia) {
428
+ const standalone = window.matchMedia("(display-mode: standalone)").matches || navigator.standalone === true;
429
+ data.push(item("\u041E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435", "pwaStandalone", "PWA (\u0440\u0435\u0436\u0438\u043C \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F)", standalone));
430
+ }
431
+ try {
432
+ const sinVal = Math.sin(1e10);
433
+ const cosVal = Math.cos(1e10);
434
+ data.push(item("\u041C\u0438\u0437\u0435\u0440\u043D\u044B\u0435", "mathSinPrecision", "Math.sin(1e10)", sinVal));
435
+ data.push(item("\u041C\u0438\u0437\u0435\u0440\u043D\u044B\u0435", "mathCosPrecision", "Math.cos(1e10)", cosVal));
436
+ data.push(item("\u041C\u0438\u0437\u0435\u0440\u043D\u044B\u0435", "mathTanPrecision", "Math.tan(1e10)", Math.tan(1e10)));
437
+ } catch {
438
+ data.push(item("\u041C\u0438\u0437\u0435\u0440\u043D\u044B\u0435", "mathPrecision", "Math precision", "\u2014"));
439
+ }
440
+ if (typeof window !== "undefined" && window.performance) {
441
+ data.push(item("\u041F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C", "timeOrigin", "Performance timeOrigin", window.performance.timeOrigin));
442
+ const mem = window.performance.memory;
443
+ if (mem) {
444
+ data.push(item("\u041F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C", "jsHeapSizeLimit", "JS Heap Limit (\u0431\u0430\u0439\u0442)", mem.jsHeapSizeLimit ?? "\u2014"));
445
+ data.push(item("\u041F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0435\u043B\u044C\u043D\u043E\u0441\u0442\u044C", "usedJSHeapSize", "Used JS Heap (\u0431\u0430\u0439\u0442)", mem.usedJSHeapSize ?? "\u2014"));
446
+ }
447
+ }
448
+ try {
449
+ data.push(item("Intl", "defaultLocale", "\u041B\u043E\u043A\u0430\u043B\u044C \u043F\u043E \u0443\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044E", Intl.DateTimeFormat().resolvedOptions().locale));
450
+ if (typeof Intl.supportedValuesOf === "function") {
451
+ const calendars = Intl.supportedValuesOf("calendar");
452
+ data.push(item("Intl", "calendarsCount", "\u041F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u043C\u044B\u0445 \u043A\u0430\u043B\u0435\u043D\u0434\u0430\u0440\u0435\u0439", (calendars == null ? void 0 : calendars.length) ?? "\u2014"));
453
+ }
454
+ } catch {
455
+ data.push(item("Intl", "locale", "Intl", "\u2014"));
456
+ }
457
+ if (doc) {
458
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "title", "\u0417\u0430\u0433\u043E\u043B\u043E\u0432\u043E\u043A", doc.title));
459
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "referrer", "Referrer", doc.referrer || "\u2014"));
460
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "characterSet", "\u041A\u043E\u0434\u0438\u0440\u043E\u0432\u043A\u0430", doc.characterSet ?? "\u2014"));
461
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "contentType", "Content-Type", doc.contentType ?? "\u2014"));
462
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "lastModified", "Last-Modified", doc.lastModified || "\u2014"));
463
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "readyState", "readyState", doc.readyState));
464
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "hidden", "\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430 \u0441\u043A\u0440\u044B\u0442\u0430 (hidden)", doc.hidden));
465
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "visibilityState", "visibilityState", doc.visibilityState ?? "\u2014"));
466
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "compatMode", "\u0420\u0435\u0436\u0438\u043C \u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C\u043E\u0441\u0442\u0438 (compatMode)", doc.compatMode));
467
+ if (doc.documentElement) {
468
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "documentClientWidth", "\u0428\u0438\u0440\u0438\u043D\u0430 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430 (clientWidth)", doc.documentElement.clientWidth));
469
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "documentClientHeight", "\u0412\u044B\u0441\u043E\u0442\u0430 \u0434\u043E\u043A\u0443\u043C\u0435\u043D\u0442\u0430 (clientHeight)", doc.documentElement.clientHeight));
470
+ }
471
+ try {
472
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "styleSheetsCount", "\u041A\u043E\u043B-\u0432\u043E \u0442\u0430\u0431\u043B\u0438\u0446 \u0441\u0442\u0438\u043B\u0435\u0439", ((_e = doc.styleSheets) == null ? void 0 : _e.length) ?? 0));
473
+ } catch {
474
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "styleSheetsCount", "\u041A\u043E\u043B-\u0432\u043E \u0442\u0430\u0431\u043B\u0438\u0446 \u0441\u0442\u0438\u043B\u0435\u0439", "\u2014"));
475
+ }
476
+ }
477
+ if (loc) {
478
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "href", "URL", loc.href));
479
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "host", "\u0425\u043E\u0441\u0442", loc.host));
480
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "hostname", "Hostname", loc.hostname));
481
+ data.push(item("\u0421\u0442\u0440\u0430\u043D\u0438\u0446\u0430", "protocol", "\u041F\u0440\u043E\u0442\u043E\u043A\u043E\u043B", loc.protocol));
482
+ }
483
+ return data;
484
+ }
485
+ async function fetchStorageEstimate() {
486
+ var _a;
487
+ const category = "\u0425\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0435";
488
+ const nav = navigator;
489
+ if (!((_a = nav.storage) == null ? void 0 : _a.estimate)) return [item(category, "storageEstimate", "Storage.estimate", "\u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F")];
490
+ try {
491
+ const e = await nav.storage.estimate();
492
+ const out = [];
493
+ if (e.quota != null) out.push(item(category, "storageQuota", "\u041A\u0432\u043E\u0442\u0430 \u0445\u0440\u0430\u043D\u0438\u043B\u0438\u0449\u0430 (\u0431\u0430\u0439\u0442)", e.quota));
494
+ if (e.usage != null) out.push(item(category, "storageUsage", "\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u043D\u043E (\u0431\u0430\u0439\u0442)", e.usage));
495
+ return out.length ? out : [item(category, "storageEstimate", "Storage.estimate", "\u2014")];
496
+ } catch {
497
+ return [item(category, "storageEstimate", "Storage.estimate", "\u043E\u0448\u0438\u0431\u043A\u0430")];
498
+ }
499
+ }
500
+ async function fetchBattery() {
501
+ const category = "\u0410\u043F\u043F\u0430\u0440\u0430\u0442\u043D\u044B\u0435 (\u0411\u0430\u0442\u0430\u0440\u0435\u044F)";
502
+ try {
503
+ const nav = navigator;
504
+ if (!nav.getBattery) return [item(category, "battery", "Battery API", "\u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F")];
505
+ const bat = await nav.getBattery();
506
+ const out = [];
507
+ out.push(item(category, "level", "\u0423\u0440\u043E\u0432\u0435\u043D\u044C \u0437\u0430\u0440\u044F\u0434\u0430 (%)", Math.round(bat.level * 100)));
508
+ out.push(item(category, "charging", "\u0417\u0430\u0440\u044F\u0436\u0430\u0435\u0442\u0441\u044F", bat.charging));
509
+ out.push(item(category, "chargingTime", "\u0412\u0440\u0435\u043C\u044F \u0434\u043E \u043F\u043E\u043B\u043D\u043E\u0439 \u0437\u0430\u0440\u044F\u0434\u043A\u0438 (\u0441)", bat.chargingTime === Infinity ? "\u221E" : bat.chargingTime));
510
+ out.push(item(category, "dischargingTime", "\u0412\u0440\u0435\u043C\u044F \u0434\u043E \u0440\u0430\u0437\u0440\u044F\u0434\u043A\u0438 (\u0441)", bat.dischargingTime === Infinity ? "\u221E" : bat.dischargingTime));
511
+ return out;
512
+ } catch {
513
+ return [item(category, "battery", "Battery API", "\u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u0438\u043B\u0438 \u0437\u0430\u043F\u0440\u0435\u0449\u0451\u043D")];
514
+ }
515
+ }
516
+ async function fetchMediaDevices() {
517
+ const category = "\u041C\u0435\u0434\u0438\u0430-\u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0430";
518
+ try {
519
+ const devices = await navigator.mediaDevices.enumerateDevices();
520
+ const video = devices.filter((d) => d.kind === "videoinput");
521
+ const audioIn = devices.filter((d) => d.kind === "audioinput");
522
+ const audioOut = devices.filter((d) => d.kind === "audiooutput");
523
+ const out = [];
524
+ out.push(item(category, "cameras", "\u041A\u0430\u043C\u0435\u0440", video.length));
525
+ out.push(item(category, "microphones", "\u041C\u0438\u043A\u0440\u043E\u0444\u043E\u043D\u043E\u0432", audioIn.length));
526
+ out.push(item(category, "audioOutputs", "\u0410\u0443\u0434\u0438\u043E\u0432\u044B\u0445\u043E\u0434\u043E\u0432", audioOut.length));
527
+ return out;
528
+ } catch {
529
+ return [item(category, "mediaDevices", "\u041C\u0435\u0434\u0438\u0430-\u0443\u0441\u0442\u0440\u043E\u0439\u0441\u0442\u0432\u0430", "\u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u044B")];
530
+ }
531
+ }
532
+ async function fetchUserAgentHints() {
533
+ var _a;
534
+ const category = "UA Client Hints";
535
+ const ua = navigator.userAgentData;
536
+ if (!(ua == null ? void 0 : ua.getHighEntropyValues)) return [item(category, "hints", "Client Hints", "\u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F")];
537
+ try {
538
+ const v = await ua.getHighEntropyValues(["platform", "architecture", "bitness", "fullVersionList", "fullVersion"]);
539
+ const out = [];
540
+ if (v.platform != null) out.push(item(category, "platform", "\u041F\u043B\u0430\u0442\u0444\u043E\u0440\u043C\u0430", v.platform));
541
+ if (v.architecture != null) out.push(item(category, "architecture", "\u0410\u0440\u0445\u0438\u0442\u0435\u043A\u0442\u0443\u0440\u0430 (x86/ARM)", v.architecture));
542
+ if (v.bitness != null) out.push(item(category, "bitness", "\u0411\u0438\u0442\u043D\u043E\u0441\u0442\u044C \u0441\u0438\u0441\u0442\u0435\u043C\u044B", v.bitness));
543
+ if (v.fullVersion != null) out.push(item(category, "fullVersion", "\u041F\u043E\u043B\u043D\u0430\u044F \u0432\u0435\u0440\u0441\u0438\u044F", v.fullVersion));
544
+ if ((_a = v.fullVersionList) == null ? void 0 : _a.length) out.push(item(category, "fullVersionList", "\u0412\u0435\u0440\u0441\u0438\u0438 \u0431\u0440\u0435\u043D\u0434\u043E\u0432", v.fullVersionList.map((b) => `${b.brand} ${b.version}`).join(", ")));
545
+ return out.length ? out : [item(category, "hints", "Client Hints", "\u2014")];
546
+ } catch {
547
+ return [item(category, "hints", "Client Hints", "\u043E\u0448\u0438\u0431\u043A\u0430 \u0438\u043B\u0438 \u0437\u0430\u043F\u0440\u0435\u0449\u0435\u043D\u043E")];
548
+ }
549
+ }
550
+ async function fetchPermissions() {
551
+ var _a;
552
+ const category = "\u0420\u0430\u0437\u0440\u0435\u0448\u0435\u043D\u0438\u044F (Permissions API)";
553
+ if (!((_a = navigator.permissions) == null ? void 0 : _a.query)) return [item(category, "permissions", "Permissions API", "\u043D\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u0435\u0442\u0441\u044F")];
554
+ const out = [];
555
+ for (const name of ["camera", "geolocation", "microphone", "notifications"]) {
556
+ try {
557
+ const s = await navigator.permissions.query({ name });
558
+ out.push(item(category, name, name, s.state));
559
+ } catch {
560
+ out.push(item(category, name, name, "\u2014"));
561
+ }
562
+ }
563
+ return out;
564
+ }
565
+ async function fetchKeyboardLayout() {
566
+ const category = "\u0412\u0432\u043E\u0434 (\u041A\u043B\u0430\u0432\u0438\u0430\u0442\u0443\u0440\u0430)";
567
+ const kb = navigator.keyboard;
568
+ if (!(kb == null ? void 0 : kb.getLayoutMap)) return [item(category, "keyboardMap", "\u0420\u0430\u0441\u043A\u043B\u0430\u0434\u043A\u0430", "API \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430")];
569
+ try {
570
+ const map = await kb.getLayoutMap();
571
+ const sample = [];
572
+ for (const [code, key] of map.entries()) {
573
+ if (sample.length >= 15) break;
574
+ sample.push(`${code}:${key}`);
575
+ }
576
+ const str = sample.length ? sample.join("; ") : "\u2014";
577
+ return [item(category, "keyboardLayout", "\u0420\u0430\u0441\u043A\u043B\u0430\u0434\u043A\u0430 (\u0432\u044B\u0431\u043E\u0440\u043A\u0430)", str)];
578
+ } catch {
579
+ return [item(category, "keyboardLayout", "\u0420\u0430\u0441\u043A\u043B\u0430\u0434\u043A\u0430", "\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F \u0432\u0437\u0430\u0438\u043C\u043E\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0438\u043B\u0438 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u043E")];
580
+ }
581
+ }
582
+ async function fetchAdBlockDetect() {
583
+ const category = "\u041E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435";
584
+ try {
585
+ const r = await fetch("https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js", { method: "HEAD", mode: "no-cors" }).catch(() => null);
586
+ const blocked = r === null || r.type === "opaque" && r.status === 0;
587
+ return [item(category, "adBlockSuspected", "AdBlock (\u043F\u043E\u0434\u043E\u0437\u0440\u0435\u043D\u0438\u0435)", blocked ? "\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D\u043E" : "\u0437\u0430\u043F\u0440\u043E\u0441 \u043F\u0440\u043E\u0448\u0451\u043B")];
588
+ } catch {
589
+ return [item(category, "adBlockSuspected", "AdBlock", "\u043D\u0435 \u043F\u0440\u043E\u0432\u0435\u0440\u0435\u043D\u043E")];
590
+ }
591
+ }
592
+ async function fetchScreenDetails() {
593
+ var _a, _b;
594
+ const category = "\u042D\u043A\u0440\u0430\u043D";
595
+ const w = window;
596
+ if (!w.getScreenDetails) return [item(category, "refreshRate", "\u0427\u0430\u0441\u0442\u043E\u0442\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F (Hz)", "API \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0430 (\u0442\u043E\u043B\u044C\u043A\u043E Chrome)")];
597
+ try {
598
+ const details = await w.getScreenDetails();
599
+ const rate = (_b = (_a = details.screens) == null ? void 0 : _a[0]) == null ? void 0 : _b.refreshRate;
600
+ return [item(category, "refreshRate", "\u0427\u0430\u0441\u0442\u043E\u0442\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F (Hz)", rate ?? "\u2014")];
601
+ } catch {
602
+ return [item(category, "refreshRate", "\u0427\u0430\u0441\u0442\u043E\u0442\u0430 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u044F", "\u2014")];
603
+ }
604
+ }
605
+ async function fetchIpAndGeo() {
606
+ const category = "IP \u0438 \u0433\u0435\u043E\u043B\u043E\u043A\u0430\u0446\u0438\u044F";
607
+ const browserTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
608
+ try {
609
+ const res = await fetch("https://ipapi.co/json/");
610
+ if (!res.ok) throw new Error("API error");
611
+ const j = await res.json();
612
+ const out = [];
613
+ if (j.ip != null) out.push(item(category, "ip", "\u041F\u0443\u0431\u043B\u0438\u0447\u043D\u044B\u0439 IP", j.ip));
614
+ if (j.city != null) out.push(item(category, "city", "\u0413\u043E\u0440\u043E\u0434", j.city));
615
+ if (j.region != null) out.push(item(category, "region", "\u0420\u0435\u0433\u0438\u043E\u043D", j.region));
616
+ if (j.country_name != null) out.push(item(category, "country", "\u0421\u0442\u0440\u0430\u043D\u0430", j.country_name));
617
+ if (j.country_code != null) out.push(item(category, "countryCode", "\u041A\u043E\u0434 \u0441\u0442\u0440\u0430\u043D\u044B", j.country_code));
618
+ if (j.postal != null) out.push(item(category, "postal", "\u0418\u043D\u0434\u0435\u043A\u0441", j.postal));
619
+ if (j.latitude != null) out.push(item(category, "latitude", "\u0428\u0438\u0440\u043E\u0442\u0430", j.latitude));
620
+ if (j.longitude != null) out.push(item(category, "longitude", "\u0414\u043E\u043B\u0433\u043E\u0442\u0430", j.longitude));
621
+ if (j.org != null) out.push(item(category, "org", "\u041F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 (ISP)", j.org));
622
+ if (j.timezone != null) {
623
+ out.push(item(category, "timezone", "\u0427\u0430\u0441\u043E\u0432\u043E\u0439 \u043F\u043E\u044F\u0441 (\u043F\u043E IP)", j.timezone));
624
+ const tzMatch = browserTz === j.timezone;
625
+ out.push(item("\u0421\u0435\u0442\u044C", "timezoneVsIp", "\u0421\u043E\u0432\u043F\u0430\u0434\u0435\u043D\u0438\u0435 \u0447\u0430\u0441\u043E\u0432\u043E\u0433\u043E \u043F\u043E\u044F\u0441\u0430 \u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0430 \u0438 IP (\u043F\u0440\u043E\u043A\u0441\u0438?)", tzMatch ? "\u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0435\u0442" : "\u043D\u0435 \u0441\u043E\u0432\u043F\u0430\u0434\u0430\u0435\u0442"));
626
+ }
627
+ return out;
628
+ } catch {
629
+ return [item(category, "ip", "\u041F\u0443\u0431\u043B\u0438\u0447\u043D\u044B\u0439 IP", "\u043D\u0435 \u0443\u0434\u0430\u043B\u043E\u0441\u044C \u043F\u043E\u043B\u0443\u0447\u0438\u0442\u044C (\u0441\u0435\u0442\u044C \u0438\u043B\u0438 API \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D)")];
630
+ }
631
+ }
632
+ async function fetchWebRtcLocalIp() {
633
+ const category = "\u0421\u0435\u0442\u044C (WebRTC)";
634
+ return new Promise((resolve) => {
635
+ try {
636
+ const pc = new RTCPeerConnection({ iceServers: [{ urls: "stun:stun.l.google.com:19302" }] });
637
+ const done = (value) => {
638
+ pc.close();
639
+ resolve([item(category, "localIp", "\u041B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 IP (WebRTC)", value)]);
640
+ };
641
+ pc.createDataChannel("");
642
+ pc.createOffer().then((offer) => pc.setLocalDescription(offer));
643
+ pc.onicecandidate = (e) => {
644
+ var _a;
645
+ const c = (_a = e.candidate) == null ? void 0 : _a.candidate;
646
+ if (!c) return;
647
+ const m = c.match(/^candidate:\d+ \d+ (\w+) \d+ (\S+) \d+ typ host .*$/);
648
+ if (m && m[2] && !m[2].endsWith(".local")) {
649
+ done(m[2]);
650
+ }
651
+ };
652
+ setTimeout(() => {
653
+ if (pc.connectionState !== "closed") {
654
+ pc.close();
655
+ resolve([item(category, "localIp", "\u041B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 IP (WebRTC)", "\u043D\u0435 \u043E\u0431\u043D\u0430\u0440\u0443\u0436\u0435\u043D \u0438\u043B\u0438 \u0437\u0430\u0431\u043B\u043E\u043A\u0438\u0440\u043E\u0432\u0430\u043D")]);
656
+ }
657
+ }, 3e3);
658
+ } catch {
659
+ resolve([item(category, "localIp", "\u041B\u043E\u043A\u0430\u043B\u044C\u043D\u044B\u0439 IP (WebRTC)", "\u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D")]);
660
+ }
661
+ });
662
+ }
663
+
664
+ // src/fingerprint/collectors.ts
665
+ var collectorCache = /* @__PURE__ */ new Map();
666
+ var ASYNC_COLLECTORS = {
667
+ network: fetchIpAndGeo,
668
+ webrtc: fetchWebRtcLocalIp,
669
+ permissions: fetchPermissions,
670
+ mediaDevices: fetchMediaDevices,
671
+ battery: fetchBattery,
672
+ uaHints: fetchUserAgentHints,
673
+ keyboard: fetchKeyboardLayout,
674
+ adBlock: fetchAdBlockDetect,
675
+ screenDetails: fetchScreenDetails,
676
+ storageEstimate: fetchStorageEstimate
677
+ };
678
+ function isDefined(value) {
679
+ return value !== null && value !== void 0;
680
+ }
681
+ function sanitizeInputMap(source) {
682
+ return Object.fromEntries(
683
+ Object.entries(source).filter(([, value]) => isDefined(value)).map(([key, value]) => [key, String(value).trim()])
684
+ );
685
+ }
686
+ function runWithTimeout(name, fn, timeoutMs) {
687
+ return new Promise((resolve, reject) => {
688
+ const timeoutId = window.setTimeout(() => {
689
+ reject(new Error(`collector_timeout:${name}`));
690
+ }, timeoutMs);
691
+ fn().then((result) => {
692
+ clearTimeout(timeoutId);
693
+ resolve(result);
694
+ }).catch((error) => {
695
+ clearTimeout(timeoutId);
696
+ reject(error);
697
+ });
698
+ });
699
+ }
700
+ async function runCollectorsInPool(tasks, maxConcurrency) {
701
+ if (!tasks.length) {
702
+ return { items: [], timedOutCollectors: [], failedCollectors: [] };
703
+ }
704
+ const items = [];
705
+ const timedOutCollectors = [];
706
+ const failedCollectors = [];
707
+ let pointer = 0;
708
+ const worker = async () => {
709
+ while (pointer < tasks.length) {
710
+ const currentIndex = pointer;
711
+ pointer += 1;
712
+ const task = tasks[currentIndex];
713
+ try {
714
+ const result = await runWithTimeout(task.name, task.fn, task.timeoutMs);
715
+ items.push(...result);
716
+ } catch (error) {
717
+ const message = error instanceof Error ? error.message : String(error);
718
+ if (message.startsWith("collector_timeout:")) {
719
+ timedOutCollectors.push(task.name);
720
+ } else {
721
+ failedCollectors.push({
722
+ collector: task.name,
723
+ reason: message
724
+ });
725
+ }
726
+ }
727
+ }
728
+ };
729
+ const workersCount = Math.max(1, Math.min(maxConcurrency, tasks.length));
730
+ await Promise.all(Array.from({ length: workersCount }, () => worker()));
731
+ return { items, timedOutCollectors, failedCollectors };
732
+ }
733
+ function buildCacheKey(collect, timeoutMs) {
734
+ const enabled = Object.entries(collect).filter(([, value]) => value).map(([key]) => key).sort().join(",");
735
+ return `${enabled}|t:${timeoutMs}`;
736
+ }
737
+ function toSignalMap(items) {
738
+ return Object.fromEntries(items.map((entry) => [entry.key, entry.value]));
739
+ }
740
+ async function gatherSignals(options) {
741
+ const requiredInputs = sanitizeInputMap(options.required);
742
+ const optionalInputs = sanitizeInputMap(options.optional ?? {});
743
+ const mergedCollect = {
744
+ ...DEFAULT_COLLECT_FLAGS,
745
+ ...options.collect ?? {}
746
+ };
747
+ const performance = resolvePerformance(options.performance);
748
+ const syncItems = collectUserData();
749
+ const cacheKey = buildCacheKey(mergedCollect, performance.timeoutMs);
750
+ const cacheEntry = collectorCache.get(cacheKey);
751
+ const now = Date.now();
752
+ let asyncResult = {
753
+ items: [],
754
+ timedOutCollectors: [],
755
+ failedCollectors: [],
756
+ cacheHit: false
757
+ };
758
+ if (cacheEntry && cacheEntry.expiresAt > now) {
759
+ asyncResult = {
760
+ items: cacheEntry.value,
761
+ timedOutCollectors: [],
762
+ failedCollectors: [],
763
+ cacheHit: true
764
+ };
765
+ } else {
766
+ const tasks = Object.entries(mergedCollect).filter(([, enabled]) => enabled).map(([name]) => ({
767
+ name,
768
+ fn: ASYNC_COLLECTORS[name],
769
+ timeoutMs: performance.timeoutMs
770
+ }));
771
+ const pooled = await runCollectorsInPool(tasks, performance.maxConcurrency);
772
+ asyncResult = {
773
+ items: pooled.items,
774
+ timedOutCollectors: pooled.timedOutCollectors,
775
+ failedCollectors: pooled.failedCollectors,
776
+ cacheHit: false
777
+ };
778
+ collectorCache.set(cacheKey, {
779
+ value: pooled.items,
780
+ expiresAt: now + performance.cacheTtlMs
781
+ });
782
+ }
783
+ const rawItems = [...syncItems, ...asyncResult.items];
784
+ return {
785
+ rawItems,
786
+ deviceSignals: toSignalMap(rawItems),
787
+ requiredInputs,
788
+ optionalInputs,
789
+ timedOutCollectors: asyncResult.timedOutCollectors,
790
+ failedCollectors: asyncResult.failedCollectors,
791
+ cacheHit: asyncResult.cacheHit
792
+ };
793
+ }
794
+ function gatherQuickSignals(options) {
795
+ const requiredInputs = sanitizeInputMap(options.required);
796
+ const optionalInputs = sanitizeInputMap(options.optional ?? {});
797
+ const rawItems = collectUserData();
798
+ return {
799
+ rawItems,
800
+ deviceSignals: toSignalMap(rawItems),
801
+ requiredInputs,
802
+ optionalInputs,
803
+ timedOutCollectors: [],
804
+ failedCollectors: [],
805
+ cacheHit: false
806
+ };
807
+ }
808
+
809
+ // src/fingerprint/hash.ts
810
+ import { md5 as md52 } from "js-md5";
811
+ function toHex(buffer) {
812
+ const bytes = new Uint8Array(buffer);
813
+ return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
814
+ }
815
+ async function createDigest(payload, algorithm = "SHA-256") {
816
+ if (algorithm === "MD5") {
817
+ return md52(payload).toUpperCase();
818
+ }
819
+ const cryptoApi = globalThis.crypto;
820
+ if (!(cryptoApi == null ? void 0 : cryptoApi.subtle)) {
821
+ return md52(payload).toUpperCase();
822
+ }
823
+ const encoded = new TextEncoder().encode(payload);
824
+ const digest = await cryptoApi.subtle.digest("SHA-256", encoded);
825
+ return toHex(digest).toUpperCase();
826
+ }
827
+
828
+ // src/fingerprint/normalize.ts
829
+ function normalizeValue(value) {
830
+ const trimmed = value.trim();
831
+ if (!trimmed) {
832
+ return "\u2014";
833
+ }
834
+ return trimmed.replace(/\s+/g, " ").toLowerCase();
835
+ }
836
+ function toSortedEntries(map) {
837
+ return Object.entries(map).sort(([left], [right]) => left.localeCompare(right));
838
+ }
839
+ function buildComponents(input) {
840
+ const required = toSortedEntries(input.requiredInputs).map(([key, value]) => ({
841
+ key: `required.${key}`,
842
+ value: normalizeValue(value),
843
+ kind: "required",
844
+ required: true,
845
+ present: Boolean(value),
846
+ weight: 5,
847
+ source: "input"
848
+ }));
849
+ const optional = toSortedEntries(input.optionalInputs).map(([key, value]) => ({
850
+ key: `optional.${key}`,
851
+ value: normalizeValue(value),
852
+ kind: "optional",
853
+ required: false,
854
+ present: Boolean(value),
855
+ weight: 2,
856
+ source: "input"
857
+ }));
858
+ const device = toSortedEntries(input.deviceSignals).map(([key, value]) => ({
859
+ key: `device.${key}`,
860
+ value: normalizeValue(value),
861
+ kind: "device",
862
+ required: false,
863
+ present: value !== "\u2014",
864
+ weight: 1,
865
+ source: "signal"
866
+ }));
867
+ return [...required, ...optional, ...device];
868
+ }
869
+ function toCanonicalPayload(components) {
870
+ return components.map((component) => `${component.key}=${component.value}`).join("|");
871
+ }
872
+ function computeConfidenceScore(components, timedOutCollectors, failedCollectorsCount) {
873
+ const weightedTotal = components.reduce((total, component) => total + component.weight, 0);
874
+ if (weightedTotal === 0) {
875
+ return 0;
876
+ }
877
+ const weightedPresent = components.filter((component) => component.present).reduce((total, component) => total + component.weight, 0);
878
+ const completeness = weightedPresent / weightedTotal;
879
+ const penalty = Math.min(0.4, timedOutCollectors.length * 0.05 + failedCollectorsCount * 0.05);
880
+ return Math.max(0, Math.min(1, Number((completeness - penalty).toFixed(3))));
881
+ }
882
+
883
+ // src/fingerprint/api.ts
884
+ function validateRequiredInputs(requiredInputs) {
885
+ const entries = Object.entries(requiredInputs);
886
+ if (!entries.length) {
887
+ throw new Error("FingerprintOptions.required must contain at least one field");
888
+ }
889
+ const emptyKeys = entries.filter(([, value]) => !value || !value.trim()).map(([key]) => key);
890
+ if (emptyKeys.length) {
891
+ throw new Error(`Fingerprint required inputs are empty: ${emptyKeys.join(", ")}`);
892
+ }
893
+ }
894
+ function applyPrivacy(raw, maskSensitive) {
895
+ if (!maskSensitive) {
896
+ return raw;
897
+ }
898
+ const sensitiveKeys = /* @__PURE__ */ new Set(["ip", "localIp", "city", "region", "country", "countryCode", "postal", "latitude", "longitude"]);
899
+ return raw.map((entry) => {
900
+ if (!sensitiveKeys.has(entry.key)) {
901
+ return entry;
902
+ }
903
+ return {
904
+ ...entry,
905
+ value: "[masked]"
906
+ };
907
+ });
908
+ }
909
+ async function getFingerprint(options) {
910
+ var _a, _b, _c, _d;
911
+ const startedAtMs = Date.now();
912
+ const startedAt = new Date(startedAtMs).toISOString();
913
+ const gathered = await gatherSignals(options);
914
+ validateRequiredInputs(gathered.requiredInputs);
915
+ const components = buildComponents({
916
+ requiredInputs: gathered.requiredInputs,
917
+ optionalInputs: gathered.optionalInputs,
918
+ deviceSignals: gathered.deviceSignals
919
+ });
920
+ const canonicalPayload = toCanonicalPayload(components);
921
+ const digestAlgorithm = ((_a = options.hash) == null ? void 0 : _a.algorithm) ?? "SHA-256";
922
+ const hash = await createDigest(canonicalPayload, digestAlgorithm);
923
+ const completedAtMs = Date.now();
924
+ const includeRaw = ((_b = options.privacy) == null ? void 0 : _b.includeRaw) ?? true;
925
+ const maskedRaw = applyPrivacy(gathered.rawItems, ((_c = options.privacy) == null ? void 0 : _c.maskSensitive) ?? false);
926
+ return {
927
+ hash,
928
+ hashVersion: "fp_v1",
929
+ components,
930
+ requiredInputs: gathered.requiredInputs,
931
+ optionalInputs: gathered.optionalInputs,
932
+ deviceSignals: gathered.deviceSignals,
933
+ raw: includeRaw ? maskedRaw : [],
934
+ meta: {
935
+ mode: ((_d = options.performance) == null ? void 0 : _d.mode) ?? DEFAULT_MODE,
936
+ digestAlgorithm,
937
+ startedAt,
938
+ completedAt: new Date(completedAtMs).toISOString(),
939
+ elapsedMs: completedAtMs - startedAtMs,
940
+ timedOutCollectors: gathered.timedOutCollectors,
941
+ failedCollectors: gathered.failedCollectors,
942
+ cacheHit: gathered.cacheHit,
943
+ confidenceScore: computeConfidenceScore(
944
+ components,
945
+ gathered.timedOutCollectors,
946
+ gathered.failedCollectors.length
947
+ )
948
+ }
949
+ };
950
+ }
951
+ async function getFingerprintQuick(options) {
952
+ var _a, _b, _c;
953
+ const startedAtMs = Date.now();
954
+ const startedAt = new Date(startedAtMs).toISOString();
955
+ const gathered = gatherQuickSignals({
956
+ required: options.required,
957
+ optional: options.optional
958
+ });
959
+ validateRequiredInputs(gathered.requiredInputs);
960
+ const components = buildComponents({
961
+ requiredInputs: gathered.requiredInputs,
962
+ optionalInputs: gathered.optionalInputs,
963
+ deviceSignals: gathered.deviceSignals
964
+ });
965
+ const canonicalPayload = toCanonicalPayload(components);
966
+ const digestAlgorithm = ((_a = options.hash) == null ? void 0 : _a.algorithm) ?? "SHA-256";
967
+ const hash = await createDigest(canonicalPayload, digestAlgorithm);
968
+ const completedAtMs = Date.now();
969
+ const includeRaw = ((_b = options.privacy) == null ? void 0 : _b.includeRaw) ?? true;
970
+ const maskedRaw = applyPrivacy(gathered.rawItems, ((_c = options.privacy) == null ? void 0 : _c.maskSensitive) ?? false);
971
+ return {
972
+ hash,
973
+ hashVersion: "fp_v1",
974
+ components,
975
+ requiredInputs: gathered.requiredInputs,
976
+ optionalInputs: gathered.optionalInputs,
977
+ deviceSignals: gathered.deviceSignals,
978
+ raw: includeRaw ? maskedRaw : [],
979
+ meta: {
980
+ mode: "fast",
981
+ digestAlgorithm,
982
+ startedAt,
983
+ completedAt: new Date(completedAtMs).toISOString(),
984
+ elapsedMs: completedAtMs - startedAtMs,
985
+ timedOutCollectors: [],
986
+ failedCollectors: [],
987
+ cacheHit: false,
988
+ confidenceScore: computeConfidenceScore(components, [], 0)
989
+ }
990
+ };
991
+ }
992
+ function resolveFingerprintPerformance(options) {
993
+ return resolvePerformance(options);
994
+ }
995
+ export {
996
+ getFingerprint,
997
+ getFingerprintQuick,
998
+ resolveFingerprintPerformance
999
+ };