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

p1-chaos-tracker

v0.4.2

Published

Intent-aware analytics tracker — behavioral signals, funnel classification, component visibility tracking

Downloads

714

Readme

p1-chaos-tracker

Intent-aware analytics tracker that classifies visitor behavior into funnel stages — Awareness, Interest, Consideration, Decision, Evaluation — with FAISS-ready signal vectors.

Install

npm install p1-chaos-tracker

Usage

Script tag (CDN)

<script defer data-site-id="YOUR_SITE_ID" src="https://unpkg.com/p1-chaos-tracker/dist/chaos-tracker.iife.js"></script>

The tracker auto-initializes when data-site-id is present. Access the API via window.ChaosTracker.

ES Module

import { initChaosTracker } from 'p1-chaos-tracker';

const tracker = initChaosTracker({ siteId: 'YOUR_SITE_ID' });

CommonJS

const { initChaosTracker } = require('p1-chaos-tracker');

const tracker = initChaosTracker({ siteId: 'YOUR_SITE_ID' });

Next.js / React

'use client';
import { useEffect, useRef } from 'react';
import { initChaosTracker } from 'p1-chaos-tracker';

export function ChaosProvider({ siteId }) {
  const tracker = useRef(null);

  useEffect(() => {
    tracker.current = initChaosTracker({ siteId, allowLocalhost: true });
    return () => tracker.current?.destroy();
  }, [siteId]);

  return null;
}

Config

| Option | Type | Default | Description | |--------|------|---------|-------------| | siteId | string | null | Your site identifier | | apiBase | string | "/api/v1/intent" | API base URL for event ingestion | | allowLocalhost | boolean | false | Enable tracking on localhost | | hasConsent | () => boolean | null | Custom consent callback. If omitted, falls back to opt-out flag and GPC/DNT | | hashIdentifiers | boolean | false | SHA-256 hash PII fields (email, name, phone) client-side before sending | | samplingRate | number | 1 | Fraction (0–1) of events to send. Useful for high-traffic sites |

API

const tracker = initChaosTracker({ siteId: 'xxx' });

// Custom event
tracker.track('plan_toggle', { plan: 'pro' });

// Identify user after login
tracker.identify('user_123', { email: '[email protected]', plan: 'pro' });

// Get current intent (for UI morphing or Stripe linkage)
const intent = tracker.getIntent();
// → { stage: 'consideration', signals: [...], score: 3, visitor_id: 'ct_...' }

// Fire a signal manually
tracker.signal('roi_calc_use');

// Cart tracking
tracker.cartAdd({ product: 'Pro Plan', price: 49 });
tracker.cartComplete();

// Search tracking
tracker.searchQuery('enterprise pricing');

// Flush events immediately
tracker.flush();

// Cleanup (removes all listeners, restores history, flushes)
tracker.destroy();

Data Attributes

Declarative tracking without code:

<!-- Track clicks by intent signal -->
<button data-chaos-track="demo">Book a Demo</button>
<button data-chaos-track="trial">Start Free Trial</button>
<button data-chaos-track="checkout">Proceed to Checkout</button>
<button data-chaos-track="comparison">Compare Plans</button>
<button data-chaos-track="roi_calc">Calculate ROI</button>
<button data-chaos-track="download">Download Guide</button>

<!-- Track element visibility on scroll -->
<section data-chaos-scroll="testimonials">...</section>
<section data-chaos-scroll="pricing_table">...</section>

<!-- Rich component tracking with metadata -->
<div data-chaos-component="hero_cta" data-chaos-meta='{"variant":"A"}'>
  ...
</div>

<!-- Override page type detection -->
<body data-chaos-page-type="pricing">...</body>

<!-- Classify form type -->
<form data-chaos-form="demo">...</form>

Components with data-chaos-component or data-chaos-scroll added after page load are automatically observed via MutationObserver — no manual registerComponent() call needed.

Rich Metadata

Any element with data-chaos-meta will have its JSON parsed and included in all events for that element (visibility milestones, hover, click):

<section
  data-chaos-component="pricing_table"
  data-chaos-meta='{"productId":"pro","variant":"annual"}'
>
  ...
</section>

Attribution & UTM Tracking

UTM parameters (utm_source, utm_medium, utm_campaign, utm_term, utm_content) and ad click IDs (gclid, fbclid, msclkid, etc.) are captured on first page load of a session and persisted in sessionStorage.

They are included in every pageview and signal event, so the backend always has attribution context even if the initial beacon failed.

Session Persistence

Session ID, start time, and page count are persisted in sessionStorage. This means:

  • getSessionDuration() returns time since the session started, not just the current page
  • pages_viewed accumulates across full page loads (not just SPA navigations)
  • Bounce rate can be accurately computed (single-pageview sessions)

Sessions expire after 30 minutes of inactivity (standard analytics definition).

Privacy

The tracker respects:

  • navigator.globalPrivacyControl (GPC) — returns null when set
  • navigator.doNotTrack — returns null when "1"
  • chaos_optout localStorage flag — call tracker.optOut() to set it
  • Custom consent callback — pass hasConsent: () => boolean in config
  • Hash identifiers — set hashIdentifiers: true to SHA-256 hash email, name, and phone before sending

Reliability

  • Retry buffer: failed sends are stored in localStorage (up to 100 events, FIFO eviction) and retried on next flush
  • Batch cap: events are sent in batches of 50 to avoid payload size limits
  • Event IDs: every event carries a unique event_id for server-side deduplication
  • Heartbeat pauses while the tab is hidden to save battery and network

Stripe Revenue Linkage

Pass the visitor_id to Stripe Checkout for direct revenue attribution:

const intent = tracker.getIntent();

// Server-side: create checkout session
const session = await stripe.checkout.sessions.create({
  client_reference_id: intent.visitor_id,
  // ...
});

Intent Stages

| Stage | Weight | Example Signals | |-------|--------|----------------| | Awareness | 1 | first_visit, homepage_under_20s | | Interest | 2 | pages_gte_2, repeat_within_day | | Consideration | 3 | pricing_over_30s, demo_form_start, form_abandon | | Decision | 4 | checkout_entered, trial_signup_attempt | | Evaluation | 5 | case_study_read, feature_deepdive_over_1min |

Events Emitted

| Event | When | Key Data Fields | |-------|------|-----------------| | session_start | Tracker init | visit_count, device, attribution, client_hints | | session_end | Previous session TTL expired | reason | | session_bounce | Unload with 1 page & <10s | time_on_page, scroll_depth | | pageview | Every page load + SPA nav | page_type, pages_viewed, attribution | | entry_page | First pageview of session | pathname, hostname, referrer, attribution | | exit_page | SPA nav away + unload | pathname, time_on_page, scroll_depth | | page_exit | Unload | full signal summary, components | | signal | Intent signal fired | signal name, intent_stage, attribution | | click | [data-chaos-track] click | track attr, text, meta | | outbound_click | External link click | url, hostname, pathname, page_type | | form_focus | Form field focus | form_type, field | | form_submit | Form submission | form_type | | form_abandon | Unload with active form | form_type, field, time_spent_ms | | component_visible | Scroll visibility milestone | component, visibility %, meta | | component_view_end | Component leaves viewport | view_duration_ms, max_visibility | | heartbeat | Every 10s (not when hidden) | intent, duration, scroll, components | | exit_intent | Mouse leaves viewport top | page_type, time_on_page | | identify | tracker.identify() call | user_id, traits (optionally hashed) | | custom | tracker.track() call | name, meta |

Development

yarn install
yarn test         # vitest
yarn build        # rollup → dist/

License

MIT