p1-chaos-tracker
v0.4.2
Published
Intent-aware analytics tracker — behavioral signals, funnel classification, component visibility tracking
Downloads
714
Maintainers
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-trackerUsage
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 pagepages_viewedaccumulates 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) — returnsnullwhen setnavigator.doNotTrack— returnsnullwhen"1"chaos_optoutlocalStorage flag — calltracker.optOut()to set it- Custom consent callback — pass
hasConsent: () => booleanin config - Hash identifiers — set
hashIdentifiers: trueto 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_idfor 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
