UNPKG

thumb-browser

Version:

A blazing-fast, dependency-free browser fingerprinting library to uniquely identify devices using entropy data. Designed for high performance, privacy-respecting analytics, bot detection, and session tracking in modern web apps.

181 lines (180 loc) 6.13 kB
var s = (u, t, e) => new Promise((a, r) => { var i = (n) => { try { o(e.next(n)); } catch (g) { r(g); } }, c = (n) => { try { o(e.throw(n)); } catch (g) { r(g); } }, o = (n) => n.done ? a(n.value) : Promise.resolve(n.value).then(i, c); o((e = e.apply(u, t)).next()); }); class d { constructor() { this.data = []; } /** * Generates the unique fingerprint by collecting system/browser data * and hashing it using SHA-256. * @returns {Promise<string>} The generated fingerprint hash */ get() { return s(this, null, function* () { yield this.collect(); const t = this.data.join("||"); return yield this.hash(t); }); } /** * Collects various browser/device characteristics to generate a fingerprint. * This includes User Agent, screen resolution, canvas fingerprint, etc. */ collect() { return s(this, null, function* () { this.data = [ navigator.userAgent, // User Agent string for browser identification navigator.language, // User language setting screen.width + "x" + screen.height + "x" + screen.colorDepth, // Screen resolution and color depth (/* @__PURE__ */ new Date()).getTimezoneOffset(), // Timezone offset this.getCanvas(), // Canvas fingerprint for uniqueness yield this.getAudio(), // Audio context fingerprint (unique to device) this.getWebGL(), // WebGL fingerprint (unique renderer info) navigator.maxTouchPoints, // Number of touch points supported navigator.deviceMemory || "unknown", // Device memory (if available) navigator.hardwareConcurrency || "unknown", // Number of CPU cores Intl.DateTimeFormat().resolvedOptions().timeZone, // Timezone navigator.doNotTrack, // Do Not Track setting in the browser `${window.innerWidth}x${window.innerHeight}`, // Window size this.getPlugins(), // Installed browser plugins this.getColorGamut(), // Color gamut of the screen (P3 or sRGB) this.getBattery(), // Battery status (if available) this.getNetwork(), // Network connection type (4G, WiFi, etc.) this.getNoise() // Random noise for added entropy ]; }); } /** * Generates a fingerprint based on the canvas rendering result. * This is unique to the specific device's GPU. * @returns {string} A base64-encoded string of the canvas content */ getCanvas() { try { const t = document.createElement("canvas"), e = t.getContext("2d"); return e.textBaseline = "top", e.font = "14px Arial", e.fillStyle = "#f60", e.fillRect(0, 0, 100, 40), e.fillStyle = "#069", e.fillText("🧠 Fingerprint!", 2, 2), t.toDataURL(); } catch (t) { return "canvas_unsupported"; } } /** * Generates an audio fingerprint by rendering an audio context and measuring the output. * @returns {Promise<string>} The audio fingerprint value (hash of the output) */ getAudio() { return s(this, null, function* () { try { const t = new (window.OfflineAudioContext || window.webkitOfflineAudioContext)(1, 44100, 44100), e = t.createOscillator(), a = t.createGain(); return e.type = "triangle", e.frequency.value = 1e4, e.connect(a), a.connect(t.destination), e.start(0), t.startRendering(), new Promise((r) => { t.oncomplete = (i) => { const c = i.renderedBuffer.getChannelData(0); let o = 0; for (let n = 0; n < c.length; n++) o += Math.abs(c[n]); r(o.toString()); }; }); } catch (t) { return "audio_unsupported"; } }); } /** * Collects WebGL information, specifically the vendor and renderer information, * to uniquely identify the GPU. * @returns {string} WebGL renderer information */ getWebGL() { try { const e = document.createElement("canvas").getContext("webgl"), a = e.getExtension("WEBGL_debug_renderer_info"), r = e.getParameter(a.UNMASKED_VENDOR_WEBGL), i = e.getParameter(a.UNMASKED_RENDERER_WEBGL); return `${r}~${i}`; } catch (t) { return "webgl_unsupported"; } } /** * Retrieves the list of installed browser plugins. * @returns {string} A comma-separated list of plugin names */ getPlugins() { try { return navigator.plugins ? [...navigator.plugins].map((t) => t.name).join(",") : "no_plugins"; } catch (t) { return "plugins_unsupported"; } } /** * Determines the color gamut of the device (P3 or sRGB). * @returns {string} The color gamut of the device */ getColorGamut() { return window.matchMedia("(color-gamut: p3)").matches ? "p3" : "srgb"; } /** * Retrieves the battery level of the device (if accessible). * @returns {Promise<string>} Battery level or 'battery_unsupported' if not accessible */ getBattery() { return navigator.getBattery ? navigator.getBattery().then((t) => t.level) : "battery_unsupported"; } /** * Retrieves the network connection type (e.g., WiFi, 4G). * @returns {string} Network connection type or 'network_unsupported' */ getNetwork() { const t = navigator.connection || navigator.mozConnection || navigator.webkitConnection; return t ? t.effectiveType : "network_unsupported"; } /** * Adds a random noise string for added entropy in fingerprint generation. * @returns {string} A random noise string */ getNoise() { return Math.random().toString(36).substring(2, 15); } /** * Generates a SHA-256 hash of the provided string. * @param {string} str The string to hash * @returns {Promise<string>} The SHA-256 hash of the string */ hash(t) { return s(this, null, function* () { const e = new TextEncoder().encode(t), a = yield crypto.subtle.digest("SHA-256", e); return Array.from(new Uint8Array(a)).map((r) => r.toString(16).padStart(2, "0")).join(""); }); } } export { d as default };