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

@cboxdk/telemetry-browser

v0.2.0

Published

Deep browser RUM SDK for cboxdk/laravel-telemetry — page load, Web Vitals, fetch/XHR, errors and SPA routes, correlated with your backend traces.

Readme

@cboxdk/telemetry-browser

Deep browser RUM, correlated with your backend traces. The frontend companion to cboxdk/laravel-telemetry: page load, Web Vitals, fetch/XHR, uncaught errors and SPA route changes — shipped to your app's ingest endpoint and joined to the backend trace via W3C traceparent. One trace id, one waterfall: click → browser span → request span → queries → jobs.

  • ~6 KB gzipped (Web Vitals included), zero config to get started.
  • No @opentelemetry/* — a lean, tailored SDK that matches the ingest endpoint exactly and emits the same dimensions as the backend, so cboxdk/laravel-telemetry-ui cross-links frontend and backend out of the box.
  • Best-effort by design: never throws into your app.

Status: alpha. APIs may shift before 1.0.

Install

npm i @cboxdk/telemetry-browser
import { init } from '@cboxdk/telemetry-browser';

const telemetry = init({
  endpoint: '/telemetry/spans',
  serviceName: 'my-app',
  environment: 'production',
  release: 'a1b2c3d',
  traceparent: document.querySelector('meta[name=traceparent]')?.content, // root on the server trace
  user: () => window.currentUser?.id,   // an id, never a name/email
});

Or drop the standalone build in a <script> — it auto-inits from its own data-* (the exact contract the Laravel package's @telemetryBrowser directive emits):

<meta name="traceparent" content="00-...-...-01">
<script src="/telemetry/browser.js" defer
        data-endpoint="/telemetry/spans"
        data-service="my-app" data-environment="production"></script>

The dimension contract

Every browser span carries these — aligned with the backend where they overlap, so a browser span and the backend span it triggered filter by the same keys. Exported as ATTR for the UI to align against.

| Dimension | Key | On | |---|---|---| | Service / env / release | service.name, deployment.environment.name, service.version | every span | | User (id only) | enduser.id | every span | | Session | session.id | every span | | Browser marker | browser (true) | every span | | URL | url.path, url.full, http.url | span-dependent | | HTTP | http.request.method, http.response.status_code | fetch/XHR | | Device / client | user_agent.original, browser.language, browser.viewport, network.connection.type, device.memory_gb | page load | | Page timing | browser.ttfb_ms, browser.fcp_ms, browser.dom_interactive_ms | page load | | Web Vitals | web_vital.name, web_vital.value, web_vital.rating | vital markers | | Errors | exception.type, exception.message, exception.file, exception.line, exception.column, exception.group, exception.stacktrace | error spans |

Span names are a small bounded set (SPAN): browser.page_load, browser.route_change, browser.web_vital, exception, plus fetch <METHOD> / xhr <METHOD>.

What it captures

  • Page load — a browser.page_load span with navigation timing (TTFB, DOM interactive/complete, transfer size) + device dimensions.
  • Web Vitals — LCP, CLS, INP, FCP, TTFB as marker spans with Google's good/needs-improvement/poor rating (via the tiny web-vitals lib).
  • fetch & XHR — a client span per call, with traceparent propagated to same-origin requests so the backend continues the trace (cross-origin is left untouched to avoid CORS preflight).
  • Errors — uncaught errors and unhandled promise rejections as exception spans, matching the backend's exception.* keys.
  • SPA route changeshistory pushState/replaceState + popstate as browser.route_change spans.

Toggle any of them via instrument: { fetch: false, ... }.

Source maps (symbolication)

Minified stacks (app-abc.js:1:2481) group and read poorly, and the file names shift every deploy. Upload your build's source maps keyed by the same release you pass to init({ release }), and the backend resolves each browser stack back to original source/line/column/name on read — so frontend error grouping and detail get as good as the backend's.

Upload from CI (which can hold the secret — the browser never does):

npx telemetry-sourcemaps \
  --dir dist/assets --release "$GIT_SHA" \
  --endpoint https://app.example.com/telemetry/sourcemaps \
  --token "$TELEMETRY_SOURCEMAPS_TOKEN"

Or as a Vite plugin, so maps upload on every production build:

// vite.config.ts
import { telemetrySourcemaps } from '@cboxdk/telemetry-browser/sourcemaps';

export default defineConfig({
  plugins: [
    telemetrySourcemaps({
      endpoint: 'https://app.example.com/telemetry/sourcemaps',
      release: process.env.GIT_SHA,
      token: process.env.TELEMETRY_SOURCEMAPS_TOKEN,
    }),
  ],
});

The Laravel package enables the receiving endpoint with TELEMETRY_SOURCEMAPS=true + TELEMETRY_SOURCEMAPS_TOKEN (token-gated, secure by default) and symbolicates at read time via its Symbolicator service — see the browser tracing guide. The uploader is Node-only and never enters the browser bundle. Maps stay private: they're resolved server-side on demand, never shipped to clients.

Until maps exist for a release, browser errors still group — by exception.group, a deploy-stable fingerprint (exception.type + a normalized message, since minified file:line moves every build). Source maps make the detail readable; grouping works with or without them.

Manual API

telemetry.record({ name: 'checkout.step', attributes: { step: 2 } });
telemetry.error(err, { 'order.id': id });
telemetry.setUser(user.id);          // after login
telemetry.setAttributes({ team: 'acme' });
telemetry.track('signup_completed', { plan: 'pro' }); // custom analytics event (needs analytics:true)
telemetry.traceId();                 // correlate your own logs
telemetry.flush();

Configuration

| Option | Default | | |---|---|---| | endpoint | — | the ingest URL (required) | | serviceName / environment / release | — | identity dimensions | | user | — | id or () => id | | traceparent | — | server trace to root on | | session | — | server-provided session.id to share one visit key (set by @telemetryBrowser when analytics is on) | | attributes | {} | extra global dimensions | | sampleRate | 1 | head sampling, decided once per page | | analytics | false | unsampled analytics channel: SPA page views, engagement, track() | | instrument | all on | per-instrumentation toggles | | maxSpans | 128 | buffer cap before a forced flush |

Spans batch and ship with sendBeacon on visibilitychange/pagehide, when the buffer fills, and on a 15 s heartbeat.

Security

The SDK sends only what you configure + the dimensions above. The server enforces the trust boundary (throttling, payload bounding, sampling, optional auth) — see the Laravel package's browser tracing guide. Provide a user id, never a name or email.

License

MIT © Cbox