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

@kirillkychkin/auto-tracker

v0.1.9

Published

Framework-agnostic auto-instrumentation of user interactions for web analytics. Heuristic target_id generation, click/change/submit/view events, Yandex.Metrika adapter.

Downloads

403

Readme

auto-tracker

Framework-agnostic auto-instrumentation of user interactions for web analytics. It listens to DOM events, decides what is worth tracking with a 4-level heuristic, builds a structured context (element → container → section → page), derives a stable target_id, and forwards everything to an analytics provider (Yandex.Metrika adapter included). No per-element wiring required.

Install

npm i @kirillkychkin/auto-tracker

Quick start

import { createTracker, YandexMetrikaAdapter } from '@kirillkychkin/auto-tracker';

createTracker({
  provider: new YandexMetrikaAdapter({ counterId: 12345, debug: true }),
  visibility: { dwellMs: 1500 },
});

createTracker(config) creates a Tracker and calls init() immediately. If you need deferred activation (e.g. after a consent prompt), construct new Tracker(config) and call init() yourself.

Without a provider, events are still logged to a local store — handy for exploring what the tool would track before wiring up an analytics system.

What gets tracked

Four event types are captured automatically:

| Event | When | |----------|------| | click | a click on an interactive or heuristically clickable element | | change | a form field value changed (input / select / textarea) | | submit | a form was submitted | | view | a page section became visible (visibility tracking) |

An element is selected for tracking by a 4-level heuristic (most → least reliable):

  1. Explicit markupdata-track attribute.
  2. HTML interactive contentbutton, a[href], form controls, etc.
  3. ARIA widget rolesrole="button", role="tab", …
  4. Visual heuristics — pointer cursor / click affordance (least reliable).

Each event carries a stable target_id derived from the context layers. Format: route__section__container__element (and route__section_heading__section_tag for view). Cyrillic is transliterated to snake_case, the id is capped at 128 chars with an FNV-1a hash suffix on overflow.

Event context

For every event the tracker builds an EventContext from layers ordered from the specific to the general — element → container → section → page — plus params:

  • element — the event target itself:
    • type — what the heuristic decided: button, input:email, role:tab, visual:clickable, explicit, …
    • text — a human-readable label (cascade: data-track-label → native text / aria-label / placeholder / … , truncated to maxLabelLength), or null.
    • state — type-specific state, present only for change / submit / view (a click has no meaningful state). See below.
  • container — the nearest semantic block parent (form, fieldset, dialog, tabpanel, a heuristic div, …): { type, heading }, or null if none.
  • section — a top-level page block (section / main / nav / header / footer / aside): { tag, heading }, where heading is the first h1h6 inside it (or the data-track-section value).
  • page{ route: location.pathname, title: document.title }.
  • params — collected from data-track-param-* attributes up the ancestor chain.

element.state

A discriminated union keyed by kind, chosen by event type:

| kind | Fields | Used for | |----------|--------|----------| | text | filled, invalid? | free-text inputs / textarea (value is not captured — PII) | | toggle | checked, value, invalid? | checkbox / radio | | select | value, selectedText, invalid? | <select> | | scalar | value, filled, invalid? | typed inputs (date, time, color, range, file…) | | form | filledFields, totalFields, submitter | submit event | | view | trigger (scroll/tab/render/modal) | view event |

Validation flag (aria-invalid)

Field kinds (text / toggle / select / scalar) carry an optional invalid?: boolean, read from the element's aria-invalid attribute — a framework-agnostic validation signal:

  • Normalized per the HTML spec: "false"false; any other value ("true", "grammar", "spelling", empty string) → true.
  • If the attribute is absent, invalid is omitted entirely — this distinguishes "not validated" from an explicit aria-invalid="false".

Example

A click on a "Buy" button produces this TrackedEvent:

{
  "targetId": "catalog__catalog__product_card__buy",
  "eventType": "click",
  "context": {
    "element": { "type": "button", "text": "Buy" },
    "container": { "type": "heuristic", "heading": "Product card" },
    "section": { "tag": "section", "heading": "Catalog" },
    "page": { "route": "/catalog", "title": "Catalog — Shop" },
    "params": { "action": "buy", "page": "catalog" }
  },
  "timestamp": 1716200000000
}

A change on an invalid email field — note the state with invalid:

"element": {
  "type": "input:email",
  "text": "Email",
  "state": { "kind": "text", "filled": true, "invalid": true }
}

Config — TrackerConfig

All fields are optional; the tracker works out of the box with defaults.

| Option | Type | Default | Description | |-------------------|-------------------------------|--------------|-------------| | root | HTMLElement \| Document | document | Root for event delegation (e.g. a shadow root). | | trackDeadClicks | boolean | false | Also record clicks on elements no heuristic level recognized — useful for debugging "dead" UI zones. | | maxLabelLength | number | 50 | Max length of human-readable labels (element/section/container text). | | provider | ProviderAdapter | — | Analytics adapter. If omitted, events go only to the local store. | | visibility | VisibilityConfig | see below | Visibility (view) tracking settings. |

VisibilityConfig

| Option | Type | Default | Description | |-------------|-----------|---------|-------------| | enabled | boolean | true | Set false to disable visibility tracking. | | threshold | number | 0.5 | Viewport intersection ratio (0..1). IAB/MRC recommends 0.5. | | dwellMs | number | 1000 | Continuous visible time before firing view. IAB/MRC: 1000 ms. |

Markup configuration (data-* attributes)

The heuristics work without any markup, but these attributes let you refine or force the tool's decisions:

| Attribute | Purpose | |--------------------------|---------| | data-track | Force-track an element (heuristic level 1, matched via closest('[data-track]')), even if no heuristic recognized it. | | data-track-type | Override the detected element type (used together with data-track). | | data-track-label | Explicit human-readable label for an element or form (takes priority over the text cascade). | | data-track-container | Mark an element as a container; the attribute value becomes container.heading (no truncation), container type → explicit. | | data-track-section | Mark an element as a section and a visibility unit; value becomes section.heading, type → explicit. The most stable signal for view. | | data-track-param-<key> | Custom event params, collected up the ancestor chain; <key> becomes a key in context.params. Can even override target_id / page via data-track-param-target_id. |

Example:

<section data-track-section="Catalog" data-track-param-page="catalog">
  <button data-track-param-action="buy">Buy</button>
</section>

Yandex.Metrika adapter

new YandexMetrikaAdapter({ counterId: 12345, debug: false });

| Option | Type | Description | |-------------|-----------|-------------| | counterId | number | Counter id from the Metrika snippet. | | debug | boolean | If true, every event is logged to console.log regardless of whether window.ym is present — a universal debug switch for any host project. |

The adapter maps event types to a fixed set of generic goals (auto_click / auto_change / auto_submit / auto_view) and sends them via ym(counterId, 'reachGoal', goal, params). The full context is flattened into state_* and custom params (Metrika takes flat key-value pairs). Using four generic goals — instead of creating a goal per unique target_id — is deliberate: the Management API needs an OAuth token (out of scope), and Metrika funnels work natively with one goal + a param filter.

If window.ym is not present, the adapter silently skips sending (and still logs in debug mode).

API

| Export | Kind | Description | |------------------------|----------|-------------| | createTracker | function | Create + init() a tracker. Recommended entry point. | | Tracker | class | Direct access for deferred activation (new Tracker(cfg) + init()). | | generateTargetId | function | Pure target_id calculator: (context, eventType) => string. Useful for tests / custom pipelines. | | YandexMetrikaAdapter | class | Built-in Yandex.Metrika provider. | | ProviderAdapter | type | Contract for custom adapters. |

Plus exported types: TrackerConfig, TrackedEvent, EventType, EventContext, ElementInfo, ElementState, ContainerInfo, SectionInfo, PageInfo, HeuristicResult, VisibilityConfig, VisibilityTrigger, YandexMetrikaAdapterOptions.

Custom adapters

Implement the ProviderAdapter contract to forward events anywhere:

import { createTracker, type ProviderAdapter, type TrackedEvent } from '@kirillkychkin/auto-tracker';

class ConsoleAdapter implements ProviderAdapter {
  sendEvent(event: TrackedEvent): void {
    console.log(event.eventType, event.targetId, event.context);
  }
}

createTracker({ provider: new ConsoleAdapter() });

One tracker uses one adapter; for fan-out to several systems, wrap them in a composite adapter.

License

MIT © Kirill Kychkin