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