npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@ianmenethil/zp-devicefp

v0.1.1

Published

Async-first browser and device fingerprinting library with explainable signals and anomaly scoring.

Downloads

885

Readme

Browser Fingerprint Library

Async-first TypeScript browser and device fingerprinting library. Collects 19 tiered browser signals, computes per-component SHA-256 hashes, returns a composite thumbprint, and includes an anti-spoof anomaly report with confidence scoring.

  • Zero runtime dependencies — no CDN loads, no npm deps at runtime.
  • ESM, CJS, and IIFE — single TypeScript source tree, three distribution formats.
  • 84 tests, 95% line coverage.

Install

bun add @ianmenethil/zp-devicefp
import { createFingerprintClient } from '@ianmenethil/zp-devicefp';

Quick start

import { createFingerprintClient } from '@ianmenethil/zp-devicefp';

const client = await createFingerprintClient({ timeoutMs: 1500 });

// Async collection — all signals, parallel with timeouts
const result = await client.collect();

console.log(result.thumbprint);      // "a1b2c3d4..." (64-char SHA-256 hex)
console.log(result.confidence);      // 0.87
console.log(result.antiSpoof.score); // 0.94

API overview

| Method | Returns | Description | |--------|---------|-------------| | client.collect(options?) | Promise<FingerprintResult> | Async collection. All signals run in parallel with per-signal timeouts. Emits events. | | client.collectSync(options?) | FingerprintResult | Sync collection. Only 13 of 19 signals support sync mode. No timeout/abort. | | client.upload(result, options) | Promise<Response> | POST the fingerprint to a backend endpoint. | | client.on(event, callback) | () => void | Subscribe to progress, warning, or complete events during async collection. |

Events

client.on('progress', (e) => {
	// { completed: 3, total: 10, signal: 'canvas', result: SignalResult }
	console.log(`${e.completed}/${e.total}: ${e.signal}`);
});

client.on('warning', (e) => {
	// { signal?: 'webrtc', message: 'RTCPeerConnection not available' }
	console.warn(e.message);
});

client.on('complete', (e) => {
	// { result: FingerprintResult }
	console.log('Done', e.result.thumbprint);
});

Upload

await client.upload(result, {
	endpoint: 'https://api.example.com/fingerprint',
	headers: { 'Authorization': 'Bearer token' },
	bodyExtras: { sessionId: 'abc123' },
});

Aborting

const controller = new AbortController();

const resultPromise = client.collect({ abortSignal: controller.signal });

// Cancel mid-flight
controller.abort();

const result = await resultPromise;
// Aborted signals appear as status: 'error' with error: 'Collection aborted.'

Options

interface CollectOptions {
	include?: SignalName[];      // whitelist: only collect these signals
	exclude?: SignalName[];      // blacklist: skip these signals
	timeoutMs?: number;          // per-signal timeout (default: 1500)
	abortSignal?: AbortSignal;   // cancel in-flight collection
	extended?: boolean;          // include extended-tier signals (default: false)
	debug?: boolean;             // enable debug logging (default: false)
}

Result shape

interface FingerprintResult {
	schemaVersion: number;       // schema version (integer, bumped on breaking changes)
	libraryVersion: string;      // library semver string
	thumbprint: string;          // SHA-256 of all component hashes combined
	confidence: number;          // 0–1 quality score
	componentHashes: Partial<Record<SignalName, string>>; // per-signal SHA-256 digests
	signals: Partial<Record<SignalName, SignalResult>>;   // raw signal results
	antiSpoof: AntiSpoofReport;  // coherence analysis and automation hints
	warnings: string[];          // non-fatal collection warnings
	diagnostics: CollectorDiagnostics; // run metadata
}

AntiSpoofReport

{
	score: number;           // 0 = automated/bot, 1 = genuine user
	anomalies: string[];     // signal inconsistencies (6 anomaly types)
	automationHints: string[]; // headless/bot indicators (2 hint types)
}

SignalResult

{
	status: 'ok' | 'unsupported' | 'blocked' | 'timeout' | 'error';
	value?: unknown;     // signal-specific data (only when status === 'ok')
	durationMs: number;  // wall-clock collection time
	error?: string;      // error description (when status !== 'ok')
}

Full API reference with all types, methods, events, options, confidence scoring, anti-spoof heuristics, and constants: docs/api-reference.md


Signals (19 total)

Core tier (10 signals, enabled by default)

| Signal | Sync | Description | |--------|------|-------------| | ua | yes | navigator.userAgent, platform, webdriver, maxTouchPoints, cookieEnabled, vendor | | uaHints | no | User-Agent Client Hints: brands, platform, high-entropy values (architecture, bitness, model, etc.) | | locale | yes | Language, languages, locale, calendar, numbering system, timeZone, formatted samples | | screen | yes | Screen width/height, colorDepth, orientation, DPR, media queries (color-gamut, reduced-motion, contrast, forced-colors) | | hardware | yes | navigator.hardwareConcurrency, deviceMemory, platform, maxTouchPoints | | storage | yes | localStorage, sessionStorage, IndexedDB, Web SQL availability (write-read-delete probe) | | fonts | yes | Installed font detection via hidden DOM span measurement (10 fonts, 3 generic families) | | canvas | yes | Canvas 2D rendering pattern → toDataURL() (GPU+driver fingerprint) | | webgl | yes | WebGL renderer/vendor (unmasked), version, extensions, max texture/viewport | | audio | no | OfflineAudioContext oscillator+compressor numeric signature |

Extended tier (9 signals, requires extended: true)

| Signal | Sync | Description | |--------|------|-------------| | mediaDevices | no | Media device counts by kind (audioinput, audiooutput, videoinput) — no labels/IDs | | permissions | no | Permissions API state for geolocation, notifications, camera, microphone | | webrtc | no | RTCPeerConnection SDP codec/extmap capability hash | | frameInfo | yes | Iframe count, source domains, top-level window check | | networkInfo | yes | NetworkInformation API: effectiveType, downlink, RTT, saveData | | paymentSupport | yes | window.PaymentRequest availability | | referrerInfo | yes | document.referrer | | navigationInfo | yes | PerformanceNavigationTiming.type (navigate/reload/back_forward/prerender) | | riskSignals | no | Bot/headless detection: webdriver, headless UA, missing chrome object, zero dimensions, empty plugins, UA-CH consistency, notification permission, time to capture |

Complete per-signal documentation with value shapes, APIs used, and edge cases: docs/signals.md


Anti-spoof heuristics (6 anomalies + 2 automation hints)

| Code | What it detects | |------|----------------| | navigator_webdriver | navigator.webdriver is truthy | | headless_user_agent | UA contains "headless" | | ua_platform_mismatch | navigator.platform contradicts UA-CH platform | | touch_claim_without_touch_points | iPhone UA but maxTouchPoints is 0 | | mobile_ua_desktop_screen | Android UA with screen width >= 1600px | | windows_ua_mac_platform | Windows UA but platform reports macOS | | implausible_device_memory | deviceMemory < 0.25 or > 64 GB | | tiny_screen_geometry | Screen width and height both < 200px |


Confidence scoring

Each signal has a fixed weight (0.2–1.0). Canvas, WebGL, fonts, and audio carry the highest weight. Status affects points earned: ok = full weight, unsupported/blocked = 35%, timeout = 15%, error = 0.

The base ratio is blended with the anti-spoof score (65% base / 35% anti-spoof). Full details in docs/api-reference.md.


CDN

CDN files are available from jsDelivr (automatic from npm publish) and self-hosted.

| File | Format | |------|--------| | zp.dfp.js | IIFE (unminified) | | zp.dfp.min.js | IIFE (minified) | | zp.dfp.obf.js | IIFE (minified + obfuscated) | | zp.dfp.esm.js | ESM for import() | | zp.dfp.manifest.json | SRI integrity hashes |

<!-- jsDelivr -->
<script src="https://cdn.jsdelivr.net/npm/@ianmenethil/zp-devicefp/dist/cdn/zp.dfp.min.js"></script>

<!-- Self-hosted -->
<script src="https://cdn.zenithpayments.support/devicefp/latest/zp.dfp.min.js"></script>

<script>
	const client = await window.DeviceFP.createFingerprintClient({ timeoutMs: 1500 });
	const result = await client.collect();
	console.log(window.DeviceFP.LIBRARY_VERSION); // "0.1.0"
</script>

Package layout

dist/
├── npm/
│   ├── index.mjs       # ESM entry (browser, ES2022)
│   ├── index.cjs       # CJS entry (Node 22+)
│   └── index.d.ts      # public types
└── cdn/
    ├── zp.dfp.js       # IIFE (unminified)
    ├── zp.dfp.min.js   # IIFE (minified)
    ├── zp.dfp.obf.js   # IIFE (minified + obfuscated)
    ├── zp.dfp.esm.js   # ESM for import()
    └── zp.dfp.manifest.json

Build and test

bun install
bun run build       # lint → typecheck → knip → jscpd → tests + coverage → esbuild → CDN deploy
bun test            # 84 tests, 95% line coverage
bun run smoke       # validates all dist artifacts

Docs

| Document | Covers | |----------|--------| | docs/api-reference.md | Complete API: types, methods, events, options, constants, confidence, anti-spoof, upload, CDN | | docs/signals.md | All 19 signals: value shapes, APIs used, sync support, edge cases | | docs/architecture.md | High-level flow and module map | | docs/privacy.md | Privacy-by-design notes | | docs/testing.md | Test strategy and coverage intent |


Design choices

  • Async first — high-value collectors (audio, UA hints, media devices, permissions, WebRTC, risk signals) run in collect().
  • Explainable output — every signal includes a status and duration. No silent failures.
  • No dangerous defaults — no battery, sensors, arbitrary installed-app probes, or local IP harvesting.
  • Anti-spoofing focuses on coherence checks and automation hints, not "secret" client tricks.
  • Upload is backend-neutral and optional — you control where (if anywhere) fingerprints are sent.

Limitations

  • Browser-side fingerprints are probabilistic, not identity proofs.
  • Font detection is bounded to 10 common fonts and is intentionally conservative.
  • The anti-spoof report is heuristic — combine with server-side context for fraud or login-risk decisions.