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

@observtech/rum

v0.1.37

Published

Observ browser RUM + Session Replay SDK (rrweb + OpenTelemetry, replay-as-context).

Readme

@observtech/rum

Browser RUM + Session Replay SDK for Observ — replay-as-context. One call captures a web session (rrweb replay + OpenTelemetry spans + semantic events + JS errors) under a single session.id, so the whole session can be read as context rather than watched as a video.

What it does

A single observ.init() wires all of the following, each stamped with the same session.id so the backend can correlate them:

  • Session replay@rrweb/record DOM capture, sent as gzip chunks.
  • Traceshttp.client spans for fetch/XHR + page load, over OTLP/HTTP, with W3C traceparent propagation (so front and backend traces stitch).
  • Session lifecyclesession.start / session.end events with previous_id chaining, a 30 min inactivity window + 4 h hard cap, and an activity sweep (per-tab sessionStorage by default; opt-in cross-tab).
  • End-user identityenduser.pseudo.id (anonymous, persistent) on every signal, plus enduser.id once you call setUser().
  • Semantic eventsclick, rage_click, navigate as OTel log records.
  • Page views — rich app.page_view (referrer, time-on-page, navigation type).
  • JS errors — uncaught errors and unhandled promise rejections, observe-only.
  • Console forwarding — optionally mirror console.* to the logs pipeline.
  • User-interaction spans — optional click/submit/keydown spans with ZoneContextManager (opt-in; pulls in zone.js).
  • Consent gate — optionally hold ALL telemetry until GDPR analytics consent.
  • PII masking — input values are masked by default (safe-by-default).

observ.init() is idempotent and never throws: any setup failure degrades to "no telemetry" instead of breaking the host page.

Install

yarn add @observtech/rum   # or: npm install / pnpm add

rrweb is pulled in as a transitive dependency and resolved by your bundler. Requires an evergreen browser (uses native CompressionStream, fetch, sessionStorage, history).

Configure

Call it as early as possible, before the first fetch/XHR you want traced:

import { observ } from '@observtech/rum'

observ.init({
  endpoint: 'https://observ.example.com', // Observ backend base URL
  key: '<api-key>', // sent as the x-observ-key header
})

Main options:

| Option | Type | Default | Role | | ------------------------------------------------ | --------------------------- | ------------------------------------------------------- | ---------------------------------------------------------------------- | | endpoint | string | — | Base URL of the Observ backend (/v1/... paths are appended). | | key | string | — | API key sent as x-observ-key (empty ⇒ header omitted). | | propagateTraceHeaderCorsUrls | (string \| RegExp)[] | [] | Cross-origin backends allowed to receive the W3C traceparent header. | | disableReplay | boolean | false | Keep tracing/events but turn off the heavy rrweb replay. | | privacy | PrivacyOptions | masked | PII masking of the replay stream (see below). | | serviceName / serviceVersion / environment | string | — | Resource attributes (service.name, …) stamped on every signal. | | sampleRate | number | 1 | Head-based trace sampling ratio in [0,1) (ParentBased). | | propagateBaggage | boolean | false | Send session.id to allowed backends via the W3C baggage header. | | disableMetrics | boolean | false | Turn off Core Web Vitals + OTLP metrics. | | sessionInactivityMs | number | 30 min | Inactivity window before the session.id rotates. | | sessionMaxDurationMs | number | 4 h | Hard cap on a single session's duration (rotates even while active). | | crossTabSessions | boolean | false | Share one session across tabs (localStorage) instead of per-tab. | | disablePseudoUser | boolean | false | Disable the persistent pseudonymous enduser.pseudo.id. | | forwardConsole | boolean \| ConsoleLevel[] | false | Mirror console.* to the logs pipeline (all levels, or a subset). | | disablePageViews | boolean | false | Turn off rich app.page_view events. | | userInteraction | boolean | false | Interaction spans + ZoneContextManager (opt-in; pulls in zone.js). | | requireConsent | boolean | false | Hold ALL telemetry until analytics consent is present (see below). | | consentKey / consentGrantedValues | string / string[] | observ.consent / ['granted','true','1','yes','all'] | Where/what consent is read from. |

Privacy / PII masking

The replay records the live DOM, so input values are PII. Masking is on by default: omit privacy entirely and every <input>/<textarea>/<select> value is masked. Three CSS classes mark sensitive nodes declaratively:

  • observ-mask — mask the element's text
  • observ-block — drop the element from the replay
  • observ-ignore — record the element but ignore its input value
observ.init({
  endpoint: 'https://observ.example.com',
  key: '<api-key>',
  privacy: {
    maskAllInputs: true, // default; set false only on a surface free of PII
    maskAllText: false, // true masks ALL visible text (degrades the replay)
  },
})

End-user identity

Every signal carries a pseudonymous enduser.pseudo.id (persisted in localStorage, anonymous). After sign-in, attach the authenticated identity; clear it on sign-out:

observ.setUser({ id: user.id, role: user.role, name: user.name }) // → enduser.id/role/name
observ.clearUser() // on logout (the pseudo id persists)

Sessions

A session.id is shared by every signal. It rotates after 30 min of inactivity or a 4 h hard cap, emitting session.start (with session.previous_id on a continuation) and a best-effort session.end (with session.duration_ms). Sessions are per-tab (sessionStorage) by default — two tabs are two visits, by design. Set crossTabSessions: true to share one session across a visitor's tabs.

Console forwarding (optional)

observ.init({ endpoint, key, forwardConsole: ['warn', 'error'] }) // or `true` for all levels

Off by default — console output can be noisy and may contain PII. The original console.* is always still called.

User-interaction spans (optional)

observ.init({ endpoint, key, userInteraction: true })

Turns clicks/submits/keydowns into spans with a ZoneContextManager so trace context survives the async work they trigger. Opt-in: it pulls in zone.js, which monkey-patches the host's global timers/Promise on import — so it is dynamically loaded only when enabled (the default bundle and host globals stay untouched).

Consent (GDPR)

Hold ALL telemetry until analytics consent is present (no session, no persistent id, no network):

observ.init({ endpoint, key, requireConsent: true }) // reads cookie/localStorage `observ.consent`
// …later, from your consent banner:
observ.grantConsent() // persists consent + starts the waiting SDK
observ.revokeConsent() // clears consent + shuts the SDK down

The SDK also auto-starts if another tab grants consent (via the storage event) or the banner writes the key in this tab (a light poll picks it up).

Stop (optional)

await observ.shutdown() // stops replay (flushing the residual) + tears down OTel

The SDK already flushes on visibilitychange/pagehide, so shutdown() is mainly for SPA teardown or tests.

Documentation

  • Full usage & backend (observ-server) setup — endpoints, CORS, API keys, session storage, troubleshooting: see the Observ docs (docs/session-replay-rum-sdk.md).
  • Building, releasing & maintaining this package: docs/observ-rum-sdk-maintainers.md.

MIT licensed.