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

@gatekeeperx/device-intelligence-web

v0.2.5

Published

GatekeeperX Device Intelligence SDK for Web (isomorphic, SSR+Edge compatible). Browser-native homologue of the Android SDK V_6 fingerprint.

Readme

@gatekeeperx/device-intelligence-web

Web SDK for GatekeeperX Device Intelligence — browser-native homologue of the Android SDK com.gatekeeperx:devicex (V_6 fingerprint).

Recolecta ~22 señales del navegador, produce un fingerprint determinista (SHA-256) y envía eventos firmados con HMAC-SHA256 al backend de GatekeeperX. Mismo payload, mismos endpoints y mismas firmas que el SDK Android: el servidor no requiere cambios.

Instalación

npm install @gatekeeperx/device-intelligence-web

Quickstart

import { DeviceIntelligence } from '@gatekeeperx/device-intelligence-web';

DeviceIntelligence.configure({
  apiKey: 'your-api-key',
  tenant: 'your-tenant',
  environment: 'sandbox',
});

const result = await DeviceIntelligence.sendEvent('login', { userId: 'u_123' });
if (result.kind === 'success') {
  console.log('deviceXId:', result.deviceXId);
} else {
  console.warn(result.errorCode, result.errorMessage);
}

DSL completo

DeviceIntelligence.configure({
  apiKey: 'sk_xxx',
  tenant: 'rappi',
  environment: 'production',       // 'production' | 'sandbox' | 'development'
  organizationId: 'rappi-co',      // opcional, default = tenant
  headers: { 'X-App-Version': '1.0.0' },
  fingerprintVersion: 'V_6',       // default
  stabilityLevel: 'OPTIMAL',       // 'STABLE' | 'OPTIMAL' | 'UNIQUE'
  optIns: {
    location: true,                // geolocalización GPS (prompt al usuario)
    wifi: true,                    // IP local vía WebRTC
    behavioral: true,              // biometría conductual (mouse + teclado)
  },
  timeoutMs: 30_000,
  apiBaseUrl: 'https://custom.endpoint.io', // opcional, sobreescribe baseUrl por environment
});

API

| Método | Tipo | Descripción | |---|---|---| | configure(opts) | void | Inicializa el singleton. Lanza si ya está configurado. | | sendEvent(name, properties?, headers?) | Promise<EventResult> | Envía evento firmado. Nunca rechaza la promesa. | | sendEventAsync(name, properties?, headers?, callback?) | void | Fire-and-forget con callback opcional. | | getFingerprint(version?, stability?) | Promise<string> | Hash hex de 64 chars. | | getDeviceId() | Promise<DeviceIdResult> | Fingerprint con señales STABLE únicamente. | | getRiskAssessment() | Promise<RiskAssessment> | Detección de headless/automation/webdriver/incógnito/debugger. | | isInitialized() | boolean | true solo en browser después de configure(). | | getVersion() | string | Versión del SDK. | | shutdown() | void | Resetea el singleton y detiene la recolección conductual. |

DeviceIdResult

interface DeviceIdResult {
  deviceId: string;      // fingerprint hex-64 usando solo señales STABLE
  signalCount: number;   // número de señales STABLE usadas
}

EventResult

EventResult es discriminated union:

type EventResult =
  | { kind: 'success'; code: number; deviceXId: string; message?: string }
  | { kind: 'failure'; errorCode: ErrorCode; errorMessage: string; httpCode?: number };

ErrorCode

| Código | Descripción | |---|---| | SDK_NOT_INITIALIZED | configure() no fue llamado antes del evento. | | INVALID_EVENT_NAME | Nombre de evento vacío o con caracteres inválidos. | | INVALID_PROPERTIES | Properties contiene tipos no soportados. | | PII_DETECTED | Se detectó PII en el payload (email, CURP, etc.). | | PAYLOAD_TOO_LARGE | El payload serializado supera el límite de tamaño. | | UNSUPPORTED_DATA_TYPE | Tipo de dato no soportado en properties. | | RATE_LIMIT_EXCEEDED | Se superaron 100 eventos/segundo (token bucket). | | NETWORK_ERROR | Error de red o CORS al enviar el evento. | | TIMEOUT | La solicitud superó timeoutMs. | | SERVER_ERROR | El servidor respondió con HTTP ≥ 500. | | SERIALIZATION_ERROR | Fallo al serializar el payload a JSON. | | UNKNOWN_ERROR | Error no clasificado. |

Señales recolectadas (V_6)

| Categoría | Señales | |---|---| | UA / Plataforma | userAgentBrandsSignal, platformSignal (UA Client Hints) | | Hardware | hardwareConcurrencySignal, deviceMemorySignal, touchPointsSignal | | Display | screenSignal (width/height/colorDepth/orientation/pixelRatio) | | Locale | timezoneSignal, languagesSignal, localeSignal | | Gráficos | canvasFingerprintSignal, webglRendererSignal | | Audio | audioContextFingerprintSignal | | Fuentes | fontListSignal (~40 fuentes detectadas) | | Privacidad | cookiesEnabledSignal, doNotTrackSignal, gpcSignal | | Storage | storageQuotaSignal | | Red | connectionSignal (effectiveType/downlink/rtt) | | Sensores | sensorsSignal, batterySignal | | Plugins | pluginsSignal | | Location | locationSignals (opt-in) con detección de spoofing | | WiFi | wifiSignals (opt-in, vía WebRTC para IP local) | | VPN | vpnSignals (heurísticas: timezone/locale mismatch, RTT anómalo, geo-IP via ipapi.co) | | Risk | riskSignals (webdriver, automation hooks, incógnito, debugger adjunto) | | Behavioral | behavioralSignals (opt-in, biometría conductual — ver sección siguiente) |

Biometría conductual (opt-in)

Al pasar optIns.behavioral: true, el SDK activa un BehaviorCollector que escucha eventos DOM pasivos y construye un BehaviorSnapshot al momento de enviar un evento.

Privacidad: el colector nunca registra el valor de teclas ni caracteres escritos (e.key y e.data no se leen). Solo se capturan tiempos y conteos. Si el usuario tiene activo DNT o GPC, el snapshot retorna privacyRedacted: true con todos los contadores en cero.

BehaviorSnapshot

| Campo | Tipo | Descripción | |---|---|---| | moveCount | number | Eventos pointermove procesados (throttle 50 ms). | | hadPointerActivity | boolean | true si hubo movimiento o clic. | | avgVelocity | number | Velocidad media del puntero (px/ms). | | velocityStdDev | number | Desviación estándar de velocidad. | | straightLineRatio | number | 0–1; ratio de movimientos perfectamente rectos (indica scripting). | | clickCount | number | Total de clicks/taps. | | avgClickInterval | number | Intervalo medio entre clicks (ms). | | keyCount | number | Total de teclas soltadas (keyup). | | avgDwellTime | number | Tiempo medio de pulsación (keydown→keyup, ms). | | dwellTimeStdDev | number | Desviación estándar del dwell time. | | avgFlightTime | number | Intervalo medio entre keyup consecutivos (ms). | | avgDownDownTime | number | Intervalo medio entre keydowns consecutivos (ms). | | maxDownDownTime | number | Máximo down-down interval observado (ms). | | keyOverlapCount | number | Teclas pulsadas simultáneamente (rollover). | | pasteCount | number | Eventos paste (Ctrl+V, Cmd+V, menú contextual). | | copyCount | number | Eventos copy. | | cutCount | number | Eventos cut. | | backspaceCount | number | Pulsaciones de Backspace. | | deleteCount | number | Pulsaciones de Delete. | | tabCount | number | Pulsaciones de Tab. | | enterCount | number | Pulsaciones de Enter/NumpadEnter. | | arrowKeyCount | number | Pulsaciones de teclas de dirección. | | shortcutCount | number | Combinaciones con Ctrl/Meta/Alt. | | untrustedEventCount | number | Eventos con isTrusted === false (sintéticos/script). | | programmaticInputCount | number | Inputs sin keydown previo (autofill, password manager, bot). | | correctionRate | number | (backspace + delete) / keyCount. | | typingSpeedCpm | number | Caracteres por minuto estimados. | | inputEventCount | number | Eventos beforeinput de tipo inserción (IME/móvil). | | compositionCount | number | Composiciones IME completadas. | | sessionDurationMs | number | Duración de sesión desde configure() (ms). | | timeToFirstInteraction | number | Milisegundos hasta el primer evento de usuario; -1 si no hubo. | | privacyRedacted | boolean | true si DNT o GPC activo; todos los demás campos = 0. |

Stability levels

  • STABLE — señales no-volátiles (platform, hardware, screen, timezone, languages, webglVendor, touchPoints, cookies). Para identidad longitudinal (≥ días).
  • OPTIMAL (default) — STABLE + canvas, webglRenderer, audio, fontList, plugins, storageQuota, connection. Balance estabilidad/unicidad.
  • UNIQUE — OPTIMAL + sensors, battery, gpc, dnt. Máxima unicidad pero menos estable.

RiskAssessment

interface RiskAssessment {
  isHeadless: boolean;          // navegador sin UI (Chrome headless, JSDOM, etc.)
  isAutomation: boolean;        // hooks de automatización detectados (Playwright, Puppeteer)
  isWebdriver: boolean;         // navigator.webdriver === true
  isPrivacyMode: boolean;       // modo incógnito / ventana privada
  isDebuggerAttached: boolean;  // DevTools adjuntos al momento del evento
  indicators: string[];         // evidencia cruda: e.g. ['webdriver', 'privacy_mode']
}

El SDK reporta señales crudas, no un score ni un veredicto. El cálculo de riesgo se realiza server-side.

SSR (Next.js, Remix, Nuxt)

El import es seguro en Server Components. En server:

  • isBrowser()false
  • configure() registra config pero no recolecta señales.
  • isInitialized()false.
  • sendEvent()EventResult.failure('SDK_NOT_INITIALIZED').
  • getFingerprint()''.

Patrón recomendado en Next.js:

'use client';
import { useEffect } from 'react';
import { DeviceIntelligence } from '@gatekeeperx/device-intelligence-web';

export function DeviceIntelligenceBoot({ apiKey, tenant }: { apiKey: string; tenant: string }) {
  useEffect(() => {
    if (!DeviceIntelligence.isInitialized()) {
      DeviceIntelligence.configure({ apiKey, tenant, environment: 'sandbox' });
    }
  }, [apiKey, tenant]);
  return null;
}

Permisos opt-in

| Opt-in | Default | Descripción | |---|---|---| | location | false | Geolocalización GPS. Requiere prompt al usuario. Si denegado, locationSignals = {} y meta.locationStatus = 'not_requested'. | | wifi | false | IP local via WebRTC ICE candidates. Si desactivado, wifiSignals = {}. | | behavioral | false | Biometría conductual (mouse + teclado + clipboard). Respeta DNT/GPC automáticamente. |

Rate limiting

El SDK aplica un token bucket de 100 eventos/segundo por instancia. Si se supera el límite, sendEvent() retorna inmediatamente con errorCode: 'RATE_LIMIT_EXCEEDED' sin realizar ninguna solicitud de red.

Detección de VPN

vpnSignals se construye combinando:

  • Heurísticas locales: mismatch timezone/locale, RTT anómalo.
  • Geo-IP vía ipapi.co (free, CORS habilitado, 1000 req/día por IP). El resultado se cachea por sesión. Si el fetch falla o hace timeout (3 s), el detector cae a las heurísticas locales.

Uso avanzado

DeviceIntelligenceAgent (inyección de dependencias / testing)

El singleton DeviceIntelligence es un wrapper sobre DeviceIntelligenceAgent. Para tests o integraciones que requieren múltiples instancias independientes:

import { DeviceIntelligenceAgent, buildConfig } from '@gatekeeperx/device-intelligence-web';

const config = buildConfig({ apiKey: 'sk_xxx', tenant: 'acme' });
const agent = new DeviceIntelligenceAgent(config, {
  // Inyectar dependencias mock para testing:
  // eventClient, rateLimiter, payloadBuilder, etc.
});

const result = await agent.sendEvent('checkout');
agent.destroy(); // detiene BehaviorCollector

buildConfig

Valida y construye un DeviceIntelligenceConfig inmutable a partir de ConfigureOptions. Útil para pre-validar la configuración antes de instanciar el agente.

Compatibilidad

  • Navegadores: Chrome/Edge ≥ 80, Firefox ≥ 88, Safari ≥ 14.
  • Node: ≥ 18 (para SSR y tests).
  • Bundlers: webpack, vite, parcel, esbuild, rollup, Next.js, Remix, Nuxt, Angular CLI, Vue CLI.
  • JavaScript puro: sí, ignora los .d.ts.

Privacidad

  • ❌ No envía MAC, IMEI, IP pública, biometría biológica, contactos.
  • ❌ No registra qué teclas se pulsaron, solo tiempos y conteos.
  • ✅ Location se redondea a 2 decimales (~1 km de precisión).
  • ✅ Permisos opt-in para Location, WiFi y biometría conductual.
  • ✅ Compatible con GPC (Global Privacy Control): behavioral snapshot retorna privacyRedacted: true.
  • ✅ Compatible con DNT (Do Not Track): misma protección que GPC.

Licencia

MIT © GatekeeperX