UNPKG

fingerprint-web

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.

2 lines (1 loc) 6.76 kB
(function(c,s){typeof exports=="object"&&typeof module!="undefined"?module.exports=s():typeof define=="function"&&define.amd?define(s):(c=typeof globalThis!="undefined"?globalThis:c||self,c.Fingerprint=s())})(this,function(){"use strict";var f=Object.defineProperty;var p=Object.getOwnPropertySymbols;var m=Object.prototype.hasOwnProperty,w=Object.prototype.propertyIsEnumerable;var d=(c,s,e)=>s in c?f(c,s,{enumerable:!0,configurable:!0,writable:!0,value:e}):c[s]=e,g=(c,s)=>{for(var e in s||(s={}))m.call(s,e)&&d(c,e,s[e]);if(p)for(var e of p(s))w.call(s,e)&&d(c,e,s[e]);return c};var l=(c,s,e)=>new Promise((t,n)=>{var r=o=>{try{i(e.next(o))}catch(u){n(u)}},a=o=>{try{i(e.throw(o))}catch(u){n(u)}},i=o=>o.done?t(o.value):Promise.resolve(o.value).then(r,a);i((e=e.apply(c,s)).next())});class c{constructor(e={}){this.data=[],this.options=g({enableFonts:!0,excludeVolatile:!0,hashAlgorithm:"SHA-256",hashLength:0,components:{userAgent:!0,language:!0,screen:!0,timezone:!0,touchPoints:!0,hardware:!0,doNotTrack:!0,canvas:!0,webGL:!0,colorGamut:!0,plugins:!0,fonts:!0,audio:!1},weights:{userAgent:1,language:1,screen:1,timezone:1,touchPoints:1,hardware:1,doNotTrack:1,canvas:1,webGL:1,colorGamut:1,plugins:1,fonts:1,audio:1},customComponents:[],separator:"||"},e)}get(){return l(this,arguments,function*(e={}){const t=g(g({},this.options),e);yield this.collect(t);const n=this.data.join(t.separator),r=yield this.hash(n,t.hashAlgorithm);return t.hashLength>0&&r.length>t.hashLength?r.substring(0,t.hashLength):r})}getRawData(){return l(this,null,function*(){return yield this.collect(),[...this.data]})}collect(){return l(this,arguments,function*(e=this.options){const t=e.components;e.weights;const n=[];if(t.userAgent&&n.push(navigator.userAgent),t.language&&n.push(navigator.language),t.screen&&n.push(screen.width+"x"+screen.height+"x"+screen.colorDepth),t.timezone&&n.push(Intl.DateTimeFormat().resolvedOptions().timeZone),t.touchPoints&&n.push(navigator.maxTouchPoints),t.hardware&&(n.push(navigator.hardwareConcurrency||"unknown"),n.push(navigator.deviceMemory||"unknown")),t.doNotTrack&&n.push(navigator.doNotTrack),t.canvas&&n.push(this.getCanvas()),t.webGL&&n.push(this.getWebGL()),t.colorGamut&&n.push(this.getColorGamut()),t.plugins&&n.push(this.getPlugins()),t.fonts&&e.enableFonts&&n.push(yield this.getAvailableFonts()),!e.excludeVolatile&&t.audio&&n.push(yield this.getAudio()),e.customComponents&&e.customComponents.length>0)for(const r of e.customComponents)try{const a=yield r.call(this);a!=null&&n.push(a)}catch(a){console.error("Error in custom component:",a)}this.data=n.filter(r=>r!=null)})}getCanvas(){try{const e=document.createElement("canvas");e.width=250,e.height=60;const t=e.getContext("2d");t.textBaseline="alphabetic",t.fillStyle="#f60",t.fillRect(125,1,62,20),t.fillStyle="#069",t.font="15px Arial",t.fillText("Consistent-Fingerprint",2,15),t.fillStyle="rgba(102, 204, 0, 0.7)",t.font="16px Georgia",t.fillText("FingerprintWeb",4,45),t.strokeStyle="#FF0000",t.beginPath(),t.arc(50,30,15,0,Math.PI*2,!0),t.closePath(),t.stroke();const n=t.getImageData(0,0,e.width,e.height).data,r=[];for(let a=0;a<n.length;a+=4e3)a<n.length&&r.push(n[a]);return r.join(",")}catch(e){return"canvas_unsupported"}}getAudio(){return l(this,null,function*(){try{const e=new(window.OfflineAudioContext||window.webkitOfflineAudioContext)(1,44100,44100),t=e.createOscillator();t.type="sine",t.frequency.setValueAtTime(1e4,e.currentTime);const n=e.createGain();return n.gain.setValueAtTime(.5,e.currentTime),t.connect(n),n.connect(e.destination),t.start(0),e.startRendering(),new Promise(r=>{e.oncomplete=a=>{const i=a.renderedBuffer.getChannelData(0),o=[],u=[0,4410,8820,13230,17640,22050,26460,30870,35280,39690];for(const h of u)h<i.length&&o.push(i[h].toFixed(6));r(o.join(","))}})}catch(e){return"audio_unsupported"}})}getWebGL(){try{const e=document.createElement("canvas"),t=e.getContext("webgl")||e.getContext("experimental-webgl");if(!t)return"webgl_unsupported";let n,r;try{const i=t.getExtension("WEBGL_debug_renderer_info");i&&(n=t.getParameter(i.UNMASKED_VENDOR_WEBGL),r=t.getParameter(i.UNMASKED_RENDERER_WEBGL))}catch(i){}n=n||t.getParameter(t.VENDOR),r=r||t.getParameter(t.RENDERER);const a=[];return a.push(`max_texture_size:${t.getParameter(t.MAX_TEXTURE_SIZE)}`),a.push(`max_viewport_dims:${t.getParameter(t.MAX_VIEWPORT_DIMS)}`),a.push(`aliased_line_width_range:${t.getParameter(t.ALIASED_LINE_WIDTH_RANGE)}`),`${n}~${r}~${a.join(",")}`}catch(e){return"webgl_unsupported"}}getPlugins(){try{if(!navigator.plugins||navigator.plugins.length===0)return"no_plugins";const e=[];for(let t=0;t<navigator.plugins.length;t++){const n=navigator.plugins[t];if(n&&n.name){let r=n.name.replace(/\s+/g," ").trim();e.push(r)}}return e.sort().join(",")}catch(e){return"plugins_unsupported"}}getColorGamut(){try{return window.matchMedia("(color-gamut: rec2020)").matches?"rec2020":window.matchMedia("(color-gamut: p3)").matches?"p3":window.matchMedia("(color-gamut: srgb)").matches?"srgb":"unknown"}catch(e){return"gamut_unsupported"}}getAvailableFonts(){return l(this,null,function*(){if(!this.options.enableFonts)return"";const e=["monospace","sans-serif","serif"],t=["Arial","Courier New","Georgia","Times New Roman","Trebuchet MS","Verdana","Tahoma","Helvetica"],n="mmMMMwWWiii";try{const r=document.createElement("div");r.style.cssText="position: absolute; left: -9999px; visibility: hidden;",document.body.appendChild(r);const a={},i=[];for(const o of e)r.style.fontFamily=o,r.innerHTML=n,a[o]=r.clientWidth;for(const o of t){let u=!1;for(const h of e)if(r.style.fontFamily=`${o}, ${h}`,r.innerHTML=n,r.clientWidth!==a[h]){u=!0;break}u&&i.push(o)}return document.body.removeChild(r),i.sort().join(",")}catch(r){return"font_detection_unsupported"}})}hash(e,t="SHA-256"){return l(this,null,function*(){const r=["SHA-1","SHA-256","SHA-384","SHA-512"].includes(t)?t:"SHA-256",a=new TextEncoder().encode(e);try{const i=yield crypto.subtle.digest(r,a);return Array.from(new Uint8Array(i)).map(o=>o.toString(16).padStart(2,"0")).join("")}catch(i){console.warn(`Hash algorithm ${t} failed, falling back to SHA-256`);const o=yield crypto.subtle.digest("SHA-256",a);return Array.from(new Uint8Array(o)).map(u=>u.toString(16).padStart(2,"0")).join("")}})}formatHash(e,t="hex"){switch(t.toLowerCase()){case"base64":const n=e.match(/.{2}/g).map(r=>parseInt(r,16));return btoa(String.fromCharCode.apply(null,n));case"int":return parseInt(e.slice(0,13),16);case"binary":return e.split("").map(r=>parseInt(r,16).toString(2).padStart(4,"0")).join("");case"hex":default:return e}}static createComponent(e){return function(){return l(this,null,function*(){try{return yield e.call(this)}catch(t){return console.error("Error in custom component:",t),null}})}}}return c});