@device-router/probe
v1.0.1
Published
Lightweight (~1 KB gzipped) client-side probe for collecting device capability signals
Maintainers
Readme
@device-router/probe
A ~1 KB (gzipped) client-side probe that collects device capability signals via browser APIs for DeviceRouter.
Installation
pnpm add @device-router/probeHow it works
The probe runs once per session in the browser. It collects device signals using standard browser APIs, then POSTs them to your server's probe endpoint. A session cookie prevents repeated collection.
Browser Server
│ │
│ collectSignals() │
│ getBattery() │
│ │
│ POST /device-router/probe │
│ ─────────────────────────────────────> │
│ { hardwareConcurrency: 8, │
│ deviceMemory: 8, ... } │
│ │
│ { sessionToken: "abc123" } │
│ <───────────────────────────────────── │
│ │
│ Set cookie: device-router-session=abc123 │
└────────────────────────────────────────────┘Usage
Script tag (IIFE)
The simplest approach — include the pre-built bundle:
<script src="/device-router-probe.min.js"></script>The probe auto-executes on load. Serve the file from dist/device-router-probe.min.js.
Auto-injection
Middleware packages can auto-inject the probe into HTML responses:
const { middleware, probeEndpoint, injectionMiddleware } = createDeviceRouter({
storage,
injectProbe: true,
});Programmatic
import { runProbe } from '@device-router/probe';
await runProbe({
endpoint: '/device-router/probe', // default
cookieName: 'device-router-session', // default
cookiePath: '/', // default
});Programmatic with retry
import { runProbeWithRetry } from '@device-router/probe';
await runProbeWithRetry({
endpoint: '/device-router/probe',
retry: {
maxRetries: 3, // default: 3
baseDelay: 500, // default: 500ms
maxDelay: 5000, // default: 5000ms
},
});Uses exponential backoff with jitter on network failure. Signals are collected once before the retry loop. Does not affect the IIFE bundle size.
Signals collected
| Signal | API | Browser Support |
| ---------------------- | -------------------------- | ------------------- |
| CPU cores | hardwareConcurrency | All modern browsers |
| Device memory | deviceMemory | Chromium |
| Connection info | navigator.connection | Chromium |
| User agent | navigator.userAgent | All browsers |
| Viewport dimensions | window.innerWidth/Height | All browsers |
| Pixel ratio | devicePixelRatio | All browsers |
| Prefers reduced motion | matchMedia | All modern browsers |
| Color scheme | matchMedia | All modern browsers |
| GPU renderer | WebGL debug info | Most browsers |
| Battery status | navigator.getBattery() | Chromium |
All signals are optional — the probe gracefully degrades based on what the browser supports. Unavailable APIs are silently skipped.
Bundle size
The IIFE bundle is strictly capped at 1024 bytes gzipped. This is enforced at build time — the build fails if the limit is exceeded.
Exports
runProbe(options?)— Run the probe (async, idempotent per session)collectSignals()— Collect all synchronous device signalsProbeSignals— Type for the collected signal objectProbeOptions— Configuration type forrunProberunProbeWithRetry(options?)— Run the probe with retry on failureRetryOptions— Retry configuration typeProbeWithRetryOptions— Configuration type forrunProbeWithRetry
Individual collectors are also exported for selective use:
collectHardwareConcurrency()collectDeviceMemory()collectConnection()collectUserAgent()collectViewport()collectPixelRatio()collectPrefersReducedMotion()collectPrefersColorScheme()collectGpuRenderer()
License
MIT
