@ianmenethil/zp-devicefp 0.1.0 → 0.2.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/README.md +173 -103
- package/dist/cdn/zp.dfp.esm.js +294 -84
- package/dist/cdn/zp.dfp.js +287 -84
- package/dist/cdn/zp.dfp.manifest.json +17 -17
- package/dist/cdn/zp.dfp.min.js +1 -1
- package/dist/cdn/zp.dfp.obf.js +1 -1
- package/dist/npm/index.cjs +301 -84
- package/dist/npm/index.d.ts +21 -8
- package/dist/npm/index.mjs +294 -84
- package/docs/api-reference.md +520 -472
- package/docs/architecture.md +171 -40
- package/docs/privacy.md +20 -20
- package/docs/signals.md +425 -383
- package/docs/testing.md +54 -32
- package/package.json +3 -3
package/dist/cdn/zp.dfp.esm.js
CHANGED
|
@@ -22,6 +22,35 @@ function hasCanvas(env) {
|
|
|
22
22
|
return Boolean(env.document?.createElement?.("canvas").getContext);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// src/signals/adblock.ts
|
|
26
|
+
var adblockCollector = {
|
|
27
|
+
name: "adblock",
|
|
28
|
+
tier: "extended",
|
|
29
|
+
supportsSync: false,
|
|
30
|
+
async collect(context) {
|
|
31
|
+
const started = now(context.env);
|
|
32
|
+
const doc = context.env.document;
|
|
33
|
+
if (!doc || !hasDom(context.env)) {
|
|
34
|
+
return { status: "unsupported", durationMs: now(context.env) - started };
|
|
35
|
+
}
|
|
36
|
+
return new Promise((resolve) => {
|
|
37
|
+
const bait = doc.createElement("div");
|
|
38
|
+
bait.innerHTML = "\xA0";
|
|
39
|
+
bait.className = "adsbox";
|
|
40
|
+
doc.body.appendChild(bait);
|
|
41
|
+
setTimeout(() => {
|
|
42
|
+
const adblockEnabled = bait.offsetHeight === 0;
|
|
43
|
+
doc.body.removeChild(bait);
|
|
44
|
+
resolve({
|
|
45
|
+
status: "ok",
|
|
46
|
+
durationMs: now(context.env) - started,
|
|
47
|
+
value: { adblockEnabled }
|
|
48
|
+
});
|
|
49
|
+
}, 100);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
25
54
|
// src/signals/audio.ts
|
|
26
55
|
function getOfflineAudioContext(env) {
|
|
27
56
|
const g = env;
|
|
@@ -140,7 +169,9 @@ var EXTENDED_SIGNALS = [
|
|
|
140
169
|
"paymentSupport",
|
|
141
170
|
"referrerInfo",
|
|
142
171
|
"navigationInfo",
|
|
143
|
-
"riskSignals"
|
|
172
|
+
"riskSignals",
|
|
173
|
+
"adblock",
|
|
174
|
+
"geolocation"
|
|
144
175
|
];
|
|
145
176
|
var SYNC_SIGNALS = [
|
|
146
177
|
"ua",
|
|
@@ -159,16 +190,76 @@ var SYNC_SIGNALS = [
|
|
|
159
190
|
];
|
|
160
191
|
var DEFAULT_TIMEOUT_MS = 1500;
|
|
161
192
|
var DEFAULT_FONT_LIST = [
|
|
193
|
+
"Andale Mono",
|
|
194
|
+
"American Typewriter",
|
|
195
|
+
"Apple Chancery",
|
|
162
196
|
"Arial",
|
|
163
|
-
"
|
|
164
|
-
"
|
|
165
|
-
"
|
|
197
|
+
"Arial Black",
|
|
198
|
+
"Arial Narrow",
|
|
199
|
+
"Arial Rounded MT Bold",
|
|
200
|
+
"Baskerville",
|
|
201
|
+
"Book Antiqua",
|
|
202
|
+
"Bookman Old Style",
|
|
203
|
+
"Bradley Hand ITC",
|
|
204
|
+
"Calibri",
|
|
205
|
+
"Cambria",
|
|
206
|
+
"Candara",
|
|
207
|
+
"Century",
|
|
208
|
+
"Century Gothic",
|
|
209
|
+
"Century Schoolbook",
|
|
210
|
+
"Chalkboard",
|
|
211
|
+
"Charcoal",
|
|
212
|
+
"Cochin",
|
|
213
|
+
"Comic Sans MS",
|
|
214
|
+
"Consolas",
|
|
215
|
+
"Constantia",
|
|
216
|
+
"Corbel",
|
|
217
|
+
"Courier",
|
|
166
218
|
"Courier New",
|
|
219
|
+
"Franklin Gothic Medium",
|
|
220
|
+
"Futura",
|
|
221
|
+
"Garamond",
|
|
222
|
+
"Geneva",
|
|
223
|
+
"Georgia",
|
|
224
|
+
"Gill Sans",
|
|
225
|
+
"Gill Sans MT",
|
|
226
|
+
"Helvetica",
|
|
227
|
+
"Hoefler Text",
|
|
228
|
+
"Impact",
|
|
229
|
+
"Lucida Bright",
|
|
230
|
+
"Lucida Console",
|
|
231
|
+
"Lucida Grande",
|
|
232
|
+
"Lucida Sans",
|
|
233
|
+
"MS Gothic",
|
|
234
|
+
"MS PGothic",
|
|
235
|
+
"MS Serif",
|
|
236
|
+
"MS UI Gothic",
|
|
237
|
+
"Meiryo",
|
|
238
|
+
"Meiryo UI",
|
|
239
|
+
"Monaco",
|
|
240
|
+
"Monotype Corsiva",
|
|
241
|
+
"New York",
|
|
242
|
+
"Optima",
|
|
243
|
+
"Palatino",
|
|
244
|
+
"Palatino Linotype",
|
|
245
|
+
"Papyrus",
|
|
246
|
+
"Plantagenet Cherokee",
|
|
247
|
+
"Segoe Print",
|
|
248
|
+
"Segoe Script",
|
|
249
|
+
"Segoe UI",
|
|
250
|
+
"Segoe UI Light",
|
|
251
|
+
"Segoe UI Semibold",
|
|
252
|
+
"Segoe UI Symbol",
|
|
253
|
+
"Symbol",
|
|
254
|
+
"Tahoma",
|
|
255
|
+
"Times",
|
|
256
|
+
"Times New Roman",
|
|
167
257
|
"Trebuchet MS",
|
|
168
258
|
"Verdana",
|
|
169
|
-
"
|
|
170
|
-
"
|
|
171
|
-
"
|
|
259
|
+
"Wingdings",
|
|
260
|
+
"Wingdings 2",
|
|
261
|
+
"Wingdings 3",
|
|
262
|
+
"Zapfino"
|
|
172
263
|
];
|
|
173
264
|
var DEFAULT_PERMISSION_NAMES = ["geolocation", "notifications", "camera", "microphone"];
|
|
174
265
|
var DEFAULT_UA_HINTS = [
|
|
@@ -296,6 +387,44 @@ var frameInfoCollector = {
|
|
|
296
387
|
}
|
|
297
388
|
};
|
|
298
389
|
|
|
390
|
+
// src/signals/geolocation.ts
|
|
391
|
+
var geolocationCollector = {
|
|
392
|
+
name: "geolocation",
|
|
393
|
+
tier: "extended",
|
|
394
|
+
supportsSync: false,
|
|
395
|
+
async collect(context) {
|
|
396
|
+
const started = now(context.env);
|
|
397
|
+
const nav = context.env.navigator;
|
|
398
|
+
if (!nav?.geolocation) {
|
|
399
|
+
return { status: "unsupported", durationMs: now(context.env) - started };
|
|
400
|
+
}
|
|
401
|
+
return new Promise((resolve) => {
|
|
402
|
+
nav.geolocation.getCurrentPosition(
|
|
403
|
+
(pos) => {
|
|
404
|
+
resolve({
|
|
405
|
+
status: "ok",
|
|
406
|
+
durationMs: now(context.env) - started,
|
|
407
|
+
value: {
|
|
408
|
+
latitude: pos.coords.latitude,
|
|
409
|
+
longitude: pos.coords.longitude,
|
|
410
|
+
accuracy: pos.coords.accuracy
|
|
411
|
+
}
|
|
412
|
+
});
|
|
413
|
+
},
|
|
414
|
+
(err) => {
|
|
415
|
+
resolve({
|
|
416
|
+
// PERMISSION_DENIED = 1
|
|
417
|
+
status: err.code === 1 ? "blocked" : "error",
|
|
418
|
+
durationMs: now(context.env) - started,
|
|
419
|
+
error: err.message
|
|
420
|
+
});
|
|
421
|
+
},
|
|
422
|
+
{ timeout: context.options.timeoutMs, maximumAge: 6e4 }
|
|
423
|
+
);
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
|
|
299
428
|
// src/signals/hardware.ts
|
|
300
429
|
var hardwareCollector = {
|
|
301
430
|
name: "hardware",
|
|
@@ -311,6 +440,17 @@ var hardwareCollector = {
|
|
|
311
440
|
if (!nav) {
|
|
312
441
|
return { status: "unsupported", durationMs: now(context.env) - started };
|
|
313
442
|
}
|
|
443
|
+
const doc = context.env.document;
|
|
444
|
+
const g = context.env.global;
|
|
445
|
+
let touchEventCreationSuccessful = false;
|
|
446
|
+
try {
|
|
447
|
+
if (doc) {
|
|
448
|
+
doc.createEvent("TouchEvent");
|
|
449
|
+
touchEventCreationSuccessful = true;
|
|
450
|
+
}
|
|
451
|
+
} catch {
|
|
452
|
+
}
|
|
453
|
+
const onTouchStartAvailable = g != null && "ontouchstart" in g;
|
|
314
454
|
return {
|
|
315
455
|
status: "ok",
|
|
316
456
|
durationMs: now(context.env) - started,
|
|
@@ -318,7 +458,9 @@ var hardwareCollector = {
|
|
|
318
458
|
hardwareConcurrency: nav.hardwareConcurrency,
|
|
319
459
|
deviceMemory: nav.deviceMemory,
|
|
320
460
|
platform: nav.platform,
|
|
321
|
-
maxTouchPoints: nav.maxTouchPoints
|
|
461
|
+
maxTouchPoints: nav.maxTouchPoints,
|
|
462
|
+
touchEventCreationSuccessful,
|
|
463
|
+
onTouchStartAvailable
|
|
322
464
|
}
|
|
323
465
|
};
|
|
324
466
|
}
|
|
@@ -340,28 +482,36 @@ var localeCollector = {
|
|
|
340
482
|
if (!nav || !intl) {
|
|
341
483
|
return { status: "unsupported", durationMs: now(context.env) - started };
|
|
342
484
|
}
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
485
|
+
try {
|
|
486
|
+
const formatter = new intl.DateTimeFormat();
|
|
487
|
+
const options = formatter.resolvedOptions();
|
|
488
|
+
const numberFormatter = new intl.NumberFormat(nav.language);
|
|
489
|
+
const relativeTimeFormatter = typeof intl.RelativeTimeFormat === "function" ? new intl.RelativeTimeFormat(nav.language, { numeric: "auto" }) : void 0;
|
|
490
|
+
const sampleDate = new Date(Date.UTC(2024, 0, 2, 3, 4, 5));
|
|
491
|
+
return {
|
|
492
|
+
status: "ok",
|
|
493
|
+
durationMs: now(context.env) - started,
|
|
494
|
+
value: {
|
|
495
|
+
language: nav.language,
|
|
496
|
+
languages: nav.languages,
|
|
497
|
+
locale: options.locale,
|
|
498
|
+
calendar: options.calendar,
|
|
499
|
+
numberingSystem: options.numberingSystem,
|
|
500
|
+
timeZone: options.timeZone,
|
|
501
|
+
hourCycle: options.hourCycle,
|
|
502
|
+
timeZoneOffsetMinutes: sampleDate.getTimezoneOffset(),
|
|
503
|
+
formattedNumber: numberFormatter.format(123456.789),
|
|
504
|
+
formattedDate: formatter.format(sampleDate),
|
|
505
|
+
formattedRelativeDay: relativeTimeFormatter?.format(-1, "day")
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
} catch (error) {
|
|
509
|
+
return {
|
|
510
|
+
status: "error",
|
|
511
|
+
durationMs: now(context.env) - started,
|
|
512
|
+
error: error instanceof Error ? error.message : String(error)
|
|
513
|
+
};
|
|
514
|
+
}
|
|
365
515
|
}
|
|
366
516
|
};
|
|
367
517
|
|
|
@@ -672,6 +822,7 @@ var screenCollector = {
|
|
|
672
822
|
if (!screen2) {
|
|
673
823
|
return { status: "unsupported", durationMs: now(context.env) - started };
|
|
674
824
|
}
|
|
825
|
+
const orientation = screen2.orientation;
|
|
675
826
|
return {
|
|
676
827
|
status: "ok",
|
|
677
828
|
durationMs: now(context.env) - started,
|
|
@@ -682,10 +833,24 @@ var screenCollector = {
|
|
|
682
833
|
availHeight: screen2.availHeight,
|
|
683
834
|
colorDepth: screen2.colorDepth,
|
|
684
835
|
pixelDepth: screen2.pixelDepth,
|
|
685
|
-
|
|
686
|
-
orientationAngle:
|
|
836
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- orientation is optional at runtime despite the Screen intersection
|
|
837
|
+
...orientation ? { orientationType: orientation.type, orientationAngle: orientation.angle } : {},
|
|
687
838
|
maxTouchPoints: nav?.maxTouchPoints ?? 0,
|
|
839
|
+
resolution: `${String(screen2.width)}x${String(screen2.height)}`,
|
|
840
|
+
ratio: screen2.height > 0 ? Math.round(screen2.width / screen2.height * 1e10) / 1e10 : 0,
|
|
688
841
|
devicePixelRatio: typeof runtime.devicePixelRatio === "number" ? runtime.devicePixelRatio : void 0,
|
|
842
|
+
// Fingerprint2 / Cardinal Commerce has_lied_resolution: availWidth never exceeds width on a real device
|
|
843
|
+
fakedResolution: screen2.availWidth > screen2.width || screen2.availHeight > screen2.height,
|
|
844
|
+
// Cardinal Commerce CCAScreenSize — outerWidth/outerHeight bucket code
|
|
845
|
+
ccaScreenSize: (() => {
|
|
846
|
+
const w = typeof runtime.outerWidth === "number" ? runtime.outerWidth : 0;
|
|
847
|
+
const h = typeof runtime.outerHeight === "number" ? runtime.outerHeight : 0;
|
|
848
|
+
if (w < 390) return "01";
|
|
849
|
+
if (w < 500) return "02";
|
|
850
|
+
if (w < 600) return "03";
|
|
851
|
+
if (h < 600) return "04";
|
|
852
|
+
return "02";
|
|
853
|
+
})(),
|
|
689
854
|
colorGamutP3: getMediaPreference(runtime, "(color-gamut: p3)"),
|
|
690
855
|
prefersReducedMotion: getMediaPreference(runtime, "(prefers-reduced-motion: reduce)"),
|
|
691
856
|
prefersContrastMore: getMediaPreference(runtime, "(prefers-contrast: more)"),
|
|
@@ -752,6 +917,16 @@ var uaCollector = {
|
|
|
752
917
|
if (!nav) {
|
|
753
918
|
return { status: "unsupported", durationMs: now(context.env) - started };
|
|
754
919
|
}
|
|
920
|
+
let plugins = [];
|
|
921
|
+
try {
|
|
922
|
+
plugins = Array.from(nav.plugins).map((p) => {
|
|
923
|
+
const mimes = Array.from({ length: p.length }, (_, i) => p.item(i)).filter((m) => m !== null).map((m) => `${m.type}~${m.suffixes}`).join(",");
|
|
924
|
+
return `${p.name}::${p.description}::${mimes}`;
|
|
925
|
+
});
|
|
926
|
+
} catch {
|
|
927
|
+
}
|
|
928
|
+
const rawDnt = nav.doNotTrack ?? context.env.global.doNotTrack ?? null;
|
|
929
|
+
const doNotTrack = rawDnt === "1" ? "enabled" : rawDnt === "0" ? "disabled" : rawDnt ?? "not_set";
|
|
755
930
|
return {
|
|
756
931
|
status: "ok",
|
|
757
932
|
durationMs: now(context.env) - started,
|
|
@@ -763,7 +938,9 @@ var uaCollector = {
|
|
|
763
938
|
webdriver: nav.webdriver,
|
|
764
939
|
maxTouchPoints: nav.maxTouchPoints,
|
|
765
940
|
cookieEnabled: nav.cookieEnabled,
|
|
766
|
-
vendorSub: nav.vendorSub
|
|
941
|
+
vendorSub: nav.vendorSub,
|
|
942
|
+
doNotTrack,
|
|
943
|
+
plugins
|
|
767
944
|
}
|
|
768
945
|
};
|
|
769
946
|
}
|
|
@@ -920,7 +1097,9 @@ var signalCollectors = [
|
|
|
920
1097
|
paymentSupportCollector,
|
|
921
1098
|
referrerInfoCollector,
|
|
922
1099
|
navigationInfoCollector,
|
|
923
|
-
riskSignalsCollector
|
|
1100
|
+
riskSignalsCollector,
|
|
1101
|
+
adblockCollector,
|
|
1102
|
+
geolocationCollector
|
|
924
1103
|
];
|
|
925
1104
|
var collectorMap = new Map(signalCollectors.map((collector) => [collector.name, collector]));
|
|
926
1105
|
|
|
@@ -934,6 +1113,13 @@ function asString(value) {
|
|
|
934
1113
|
function asNumber(value) {
|
|
935
1114
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
936
1115
|
}
|
|
1116
|
+
function canonicalizeLegacyPlatform(legacyPlatform) {
|
|
1117
|
+
if (legacyPlatform.startsWith("win")) return "windows";
|
|
1118
|
+
if (legacyPlatform.startsWith("mac")) return "macos";
|
|
1119
|
+
if (/cros|chrome/.test(legacyPlatform)) return "chrome os";
|
|
1120
|
+
if (/linux|x11/.test(legacyPlatform)) return "linux";
|
|
1121
|
+
return legacyPlatform;
|
|
1122
|
+
}
|
|
937
1123
|
function analyseSignals(signals) {
|
|
938
1124
|
const anomalies = [];
|
|
939
1125
|
const automationHints = [];
|
|
@@ -955,8 +1141,12 @@ function analyseSignals(signals) {
|
|
|
955
1141
|
if (userAgent.includes("headless")) {
|
|
956
1142
|
automationHints.push("headless_user_agent");
|
|
957
1143
|
}
|
|
958
|
-
if (platform && hintPlatform
|
|
959
|
-
|
|
1144
|
+
if (platform && hintPlatform) {
|
|
1145
|
+
const canonicalLegacy = canonicalizeLegacyPlatform(platform);
|
|
1146
|
+
const linuxFamilyOk = canonicalLegacy === "linux" && (hintPlatform === "linux" || hintPlatform === "android" || hintPlatform === "chrome os");
|
|
1147
|
+
if (!linuxFamilyOk && canonicalLegacy !== hintPlatform) {
|
|
1148
|
+
anomalies.push("ua_platform_mismatch");
|
|
1149
|
+
}
|
|
960
1150
|
}
|
|
961
1151
|
if (userAgent.includes("iphone") && maxTouchPoints === 0) {
|
|
962
1152
|
anomalies.push("touch_claim_without_touch_points");
|
|
@@ -976,10 +1166,16 @@ function analyseSignals(signals) {
|
|
|
976
1166
|
const anomalyPenalty = anomalies.length * 0.12;
|
|
977
1167
|
const automationPenalty = automationHints.length * 0.18;
|
|
978
1168
|
const score = Math.max(0, Math.min(1, Number((1 - anomalyPenalty - automationPenalty).toFixed(3))));
|
|
1169
|
+
const fakedOS = anomalies.includes("ua_platform_mismatch");
|
|
1170
|
+
const riskVal = asRecord(signals.riskSignals?.value);
|
|
1171
|
+
const headlessHints = Array.isArray(riskVal?.headlessHints) ? riskVal.headlessHints : [];
|
|
1172
|
+
const fakedBrowser = headlessHints.includes("missing-chrome-object");
|
|
979
1173
|
return {
|
|
980
1174
|
score,
|
|
981
1175
|
anomalies,
|
|
982
|
-
automationHints
|
|
1176
|
+
automationHints,
|
|
1177
|
+
fakedOS,
|
|
1178
|
+
fakedBrowser
|
|
983
1179
|
};
|
|
984
1180
|
}
|
|
985
1181
|
|
|
@@ -1049,7 +1245,9 @@ var SIGNAL_WEIGHTS = {
|
|
|
1049
1245
|
paymentSupport: 0.2,
|
|
1050
1246
|
referrerInfo: 0.3,
|
|
1051
1247
|
navigationInfo: 0.4,
|
|
1052
|
-
riskSignals: 0.7
|
|
1248
|
+
riskSignals: 0.7,
|
|
1249
|
+
adblock: 0.3,
|
|
1250
|
+
geolocation: 0.5
|
|
1053
1251
|
};
|
|
1054
1252
|
function computeConfidence(signals, antiSpoof) {
|
|
1055
1253
|
let earned = 0;
|
|
@@ -1271,28 +1469,9 @@ function resolveSignalSet(options, mode = "async") {
|
|
|
1271
1469
|
return included.filter((signal) => !excluded.has(signal));
|
|
1272
1470
|
}
|
|
1273
1471
|
|
|
1274
|
-
// src/core/upload.ts
|
|
1275
|
-
async function uploadFingerprint(env, result, options) {
|
|
1276
|
-
const fetchImpl = options.endpoint && env.fetch;
|
|
1277
|
-
if (!fetchImpl) {
|
|
1278
|
-
throw new Error("Fetch is not available in this runtime.");
|
|
1279
|
-
}
|
|
1280
|
-
return fetchImpl(options.endpoint, {
|
|
1281
|
-
method: "POST",
|
|
1282
|
-
headers: {
|
|
1283
|
-
"content-type": "application/json",
|
|
1284
|
-
...options.headers
|
|
1285
|
-
},
|
|
1286
|
-
body: JSON.stringify({
|
|
1287
|
-
fingerprint: result,
|
|
1288
|
-
...options.bodyExtras
|
|
1289
|
-
})
|
|
1290
|
-
});
|
|
1291
|
-
}
|
|
1292
|
-
|
|
1293
1472
|
// src/version.ts
|
|
1294
|
-
var LIBRARY_VERSION = "0.
|
|
1295
|
-
var SCHEMA_VERSION =
|
|
1473
|
+
var LIBRARY_VERSION = true ? "0.2.0" : "0.0.0-dev";
|
|
1474
|
+
var SCHEMA_VERSION = 2;
|
|
1296
1475
|
|
|
1297
1476
|
// src/client.ts
|
|
1298
1477
|
function toTimedOutResult(durationMs) {
|
|
@@ -1302,6 +1481,18 @@ function toTimedOutResult(durationMs) {
|
|
|
1302
1481
|
error: "Signal collection timed out."
|
|
1303
1482
|
};
|
|
1304
1483
|
}
|
|
1484
|
+
function toErrorResult(durationMs, error) {
|
|
1485
|
+
return {
|
|
1486
|
+
status: "error",
|
|
1487
|
+
durationMs,
|
|
1488
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
function debugLog(resolved, message) {
|
|
1492
|
+
if (resolved.debug) {
|
|
1493
|
+
console.debug(`[zp-devicefp] ${message}`);
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1305
1496
|
async function runCollectorWithTimeout(collector, env, options, warn) {
|
|
1306
1497
|
const started = env.performance?.now?.() ?? Date.now();
|
|
1307
1498
|
if (options.abortSignal?.aborted) {
|
|
@@ -1313,31 +1504,36 @@ async function runCollectorWithTimeout(collector, env, options, warn) {
|
|
|
1313
1504
|
}
|
|
1314
1505
|
let clearTimeoutHandle = () => {
|
|
1315
1506
|
};
|
|
1507
|
+
let removeAbortListener = () => {
|
|
1508
|
+
};
|
|
1316
1509
|
const timeoutPromise = new Promise((resolve) => {
|
|
1317
1510
|
const timeout = setTimeout(() => {
|
|
1318
1511
|
resolve(toTimedOutResult((env.performance?.now?.() ?? Date.now()) - started));
|
|
1319
1512
|
}, options.timeoutMs);
|
|
1320
1513
|
clearTimeoutHandle = () => clearTimeout(timeout);
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
(
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
{ once: true }
|
|
1332
|
-
);
|
|
1333
|
-
});
|
|
1334
|
-
const resultPromise = collector.collect({
|
|
1335
|
-
env,
|
|
1336
|
-
options,
|
|
1337
|
-
warn
|
|
1514
|
+
const onAbort = () => {
|
|
1515
|
+
clearTimeoutHandle();
|
|
1516
|
+
resolve({
|
|
1517
|
+
status: "error",
|
|
1518
|
+
durationMs: (env.performance?.now?.() ?? Date.now()) - started,
|
|
1519
|
+
error: "Collection aborted."
|
|
1520
|
+
});
|
|
1521
|
+
};
|
|
1522
|
+
options.abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
1523
|
+
removeAbortListener = () => options.abortSignal?.removeEventListener("abort", onAbort);
|
|
1338
1524
|
});
|
|
1525
|
+
const resultPromise = (async () => {
|
|
1526
|
+
try {
|
|
1527
|
+
return await collector.collect({ env, options, warn });
|
|
1528
|
+
} catch (error) {
|
|
1529
|
+
const result = toErrorResult((env.performance?.now?.() ?? Date.now()) - started, error);
|
|
1530
|
+
warn(`Signal "${collector.name}" threw: ${result.error ?? "unknown error"}`, collector.name);
|
|
1531
|
+
return result;
|
|
1532
|
+
}
|
|
1533
|
+
})();
|
|
1339
1534
|
return Promise.race([resultPromise, timeoutPromise]).finally(() => {
|
|
1340
1535
|
clearTimeoutHandle();
|
|
1536
|
+
removeAbortListener();
|
|
1341
1537
|
});
|
|
1342
1538
|
}
|
|
1343
1539
|
function buildResult(signals, warnings, requestedSignals, elapsedMs) {
|
|
@@ -1399,6 +1595,7 @@ async function createFingerprintClient(options) {
|
|
|
1399
1595
|
}
|
|
1400
1596
|
const result = await runCollectorWithTimeout(collector, env, resolved, warn);
|
|
1401
1597
|
collected[name] = result;
|
|
1598
|
+
debugLog(resolved, `${name}: ${result.status} (${String(result.durationMs)}ms)`);
|
|
1402
1599
|
completed += 1;
|
|
1403
1600
|
emitter.emit("progress", {
|
|
1404
1601
|
completed,
|
|
@@ -1429,21 +1626,34 @@ async function createFingerprintClient(options) {
|
|
|
1429
1626
|
warn(`Signal "${name}" is not available in synchronous mode.`, name);
|
|
1430
1627
|
continue;
|
|
1431
1628
|
}
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1629
|
+
const signalStarted = env.performance?.now?.() ?? Date.now();
|
|
1630
|
+
let result;
|
|
1631
|
+
try {
|
|
1632
|
+
result = collector.collectSync({
|
|
1633
|
+
env,
|
|
1634
|
+
options: resolved,
|
|
1635
|
+
warn
|
|
1636
|
+
});
|
|
1637
|
+
} catch (error) {
|
|
1638
|
+
result = toErrorResult((env.performance?.now?.() ?? Date.now()) - signalStarted, error);
|
|
1639
|
+
warn(`Signal "${name}" threw: ${result.error ?? "unknown error"}`, name);
|
|
1640
|
+
}
|
|
1641
|
+
collected[name] = result;
|
|
1642
|
+
debugLog(resolved, `${name}: ${result.status} (${String(result.durationMs)}ms)`);
|
|
1437
1643
|
}
|
|
1438
1644
|
return buildResult(collected, warnings, signalNames, (env.performance?.now?.() ?? Date.now()) - started);
|
|
1439
|
-
},
|
|
1440
|
-
upload(result, uploadOptions) {
|
|
1441
|
-
return uploadFingerprint(getBrowserEnv(), result, uploadOptions);
|
|
1442
1645
|
}
|
|
1443
1646
|
};
|
|
1444
1647
|
}
|
|
1445
1648
|
export {
|
|
1649
|
+
CORE_SIGNALS,
|
|
1650
|
+
DEFAULT_FONT_LIST,
|
|
1651
|
+
DEFAULT_PERMISSION_NAMES,
|
|
1652
|
+
DEFAULT_TIMEOUT_MS,
|
|
1653
|
+
DEFAULT_UA_HINTS,
|
|
1654
|
+
EXTENDED_SIGNALS,
|
|
1446
1655
|
LIBRARY_VERSION,
|
|
1447
1656
|
SCHEMA_VERSION,
|
|
1657
|
+
SYNC_SIGNALS,
|
|
1448
1658
|
createFingerprintClient
|
|
1449
1659
|
};
|