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

@observejs/plugin-auto-track

v0.1.0

Published

Automatic tracking plugin for Observe.js — pageviews, clicks, forms, scroll, clipboard, visibility, session, network, and window events.

Readme

@observejs/plugin-auto-track

Automatic tracking plugin for Observe.js. Capture user interactions without writing a single Observe.track() call.

Built on the @observejs/browser plugin system — pluggable, isolated, and zero impact on the host app if it crashes.


Install

npm install @observejs/browser @observejs/plugin-auto-track

Usage

import { Observe } from "@observejs/browser";
import { autoTrackPlugin } from "@observejs/plugin-auto-track";

Observe.init({
  apiKey: "obs_pk_live_xxx",
  plugins: [autoTrackPlugin()],
});

That's it. The plugin attaches listeners and ships events through the SDK queue — same batching, retries, and offline behavior as any other event.


What gets captured

| Category | Event names | | ----------- | ------------------------------------------------------------------------ | | Page | $page (initial, pushstate, replacestate, popstate, hashchange) | | Click | $click, $dblclick, $contextmenu | | Form | $form_submit, $form_reset, $input_focus, $input_blur, $input, $input_change, $checkbox_change, $radio_change, $select_change, $textarea_change | | Scroll | $scroll_depth (25 / 50 / 75 / 100 thresholds, one-shot per route) | | Mouse | $mouse_enter, $mouse_leave (only on [data-track-hover] / [data-track]) | | Clipboard | $clipboard_copy, $clipboard_cut, $clipboard_paste (action only — never contents) | | Visibility | $tab_hidden, $tab_visible, $beforeunload, $pagehide | | Session | $session_started, $session_ended, $session_timeout, $session_resumed | | Online | $online, $offline, $connection_restored | | Window | $viewport_resize, $orientation_change |

Every event carries the full SDK schema (eventId, sessionId, visitorId, deviceId, url, path, referrer, device, metadata, …) on top of the category-specific properties shown above.


Configuration

autoTrackPlugin({
  // Toggle individual categories — all on by default.
  categories: {
    page: true,
    click: true,
    form: true,
    scroll: true,
    mouse: true,
    clipboard: true,
    visibility: true,
    session: true,
    online: true,
    window: true,
  },

  // CSS selectors whose interactions should never produce events.
  ignoreSelectors: ["[data-no-track]", ".sensitive", "#admin-panel"],

  // Field name / id / aria-label substrings to mask (case-insensitive).
  // Sensible defaults already cover password, cc, cvv, ssn, otp, token, etc.
  maskFields: ["accountNumber", "routingNumber"],

  // URL patterns to ignore (combined with the SDK-level ignoreUrls).
  ignoreUrls: ["/admin/", /\/internal\//],

  // Cap text captured from buttons/links/inputs.
  maxTextLength: 120,

  // Debounce for `$input` events.
  inputDebounce: 400,
});

Opting elements out

Any element with these attributes is ignored entirely:

<form data-obs-ignore>...</form>
<button data-observe-ignore>Don't track me</button>
<input class="obs-ignore" />

Opting elements in (hover tracking)

Mouse enter/leave events fire only for elements that opt in (to avoid flooding the queue):

<div data-track-hover>Pricing tier — hover counts</div>
<a data-track href="/upgrade">Upgrade</a>

Privacy

The plugin never captures:

  • <input type="password"> values
  • <input type="hidden"> values
  • Fields with autocomplete="cc-*", current-password, new-password, one-time-code
  • Fields whose name, id, aria-label, placeholder, or data-field contains: password, secret, token, apikey, auth, ssn, credit, card, cvv, cvc, pin, otp, mfa, verification, … (full list in DEFAULT_MASK_FIELDS)
  • Clipboard contents (only the action: copy / cut / paste)
  • Mouse coordinates or movement paths
  • Cookies or auth tokens

Add your own substrings with maskFields. All input text is also truncated to 120 chars before being sent.


Performance

  • All listeners use capture phase + passive: true where possible.
  • $scroll_depth is throttled to 200ms and emits at most 4 events per route.
  • $input is debounced (default 400ms).
  • $viewport_resize is debounced to 250ms.
  • Hover tracking is opt-in ([data-track-hover]) to avoid DOM-wide noise.
  • Continuous mouse movement is deliberately not captured here — the replay plugin (later phase) handles that.
  • Total listener footprint: ~15 listeners regardless of DOM size.

Lifecycle

The plugin returns a teardown function. Observe.shutdown() will:

  • Remove every DOM listener
  • Restore the original history.pushState / history.replaceState
  • Cancel any pending debounced/throttled callbacks
  • Clear the session-timeout timer

No memory leaks, even across hot reloads.


Debug

Enable debug: true on the SDK and the plugin logs initialization, each tracker's setup, and every captured event:

Observe.init({
  apiKey: "obs_pk_live_xxx",
  debug: true,
  plugins: [autoTrackPlugin()],
});

Production mode (default) is silent.


License

MIT