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

churn-warn-js-sdk

v0.7.0

Published

Browser SDK for POST /api/events to ChurnWarn Gateway.

Readme

ChurnWarn JavaScript browser SDK v0.7.0

Small, dependency-free client for sending raw product events to the ChurnWarn Gateway POST /api/events endpoint. Intended for script tag embedding (bundled as a single global).

v0.7.0 additions (non-breaking)

  • Tier-C signals — Metrics now covers every business-type template signal (order_placed, product_viewed, cart_created, iap_purchase, transaction, …). New PayloadFields, AccountAttributes, BusinessTypes constants (mirror sdks/signals.manifest.json).
  • data-cw-signal — tag any element and auto-capture emits that business signal on click. See Domain signals for dashboard templates.
  • E-commerce bridge — autoCapture: { ecommerce: true } mirrors GA4/GTM dataLayer + gtag ecommerce events to ChurnWarn signals with zero extra code.
  • identify(id, traits) / upsertAccount(id, traits) — write account facts (plan, LTV, push_opt_in, marketplace role, …) to PUT /api/accounts.

v0.6.0 breaking changes

  • element_clicked.<label> event types are gone. All clicks now emit a single element_clicked type with payload.label for the button/link name. Update any event-map patterns from element_clicked.* → element_clicked.
  • page_viewed payload now includes a route field: the pathname with auto-collapsed IDs (UUIDs, integers, long tokens) replaced by :id. Use in condition mappings: {"payload.route":"/orders/:id"}.
  • AutoCaptureEvents.ELEMENT_CLICKED_PREFIX renamed to AutoCaptureEvents.ELEMENT_CLICKED.

What you get

  • configure — set bearer token, optional API base URL, optional tenant override, optional event source label, optional auto-capture.
  • identify — set the account id for auto-capture (call after login).
  • captureEvent — fire-and-forget HTTP POST; does not block the UI thread after the call returns.
  • Metrics — frozen object of canonical event type strings (aligned with the default dashboard metrics in the API).
  • RawEvents — frozen object of dotted raw event names the API can map to those metrics.
  • AutoCaptureEvents — frozen event names emitted by auto-capture (page_viewed, session_started, etc.).
  • version — SDK version string.

Runtime has no npm dependencies. The published file is an ES5-targeted IIFE exposing the global ChurnWarn.

Quick start (script tag)

Host or copy dist/churn-warn.umd.min.js and load it before your app code:

<script src="/static/churn-warn.umd.min.js"></script>
<script>
  ChurnWarn.configure({
    apiKey: '<your-ingest-api-key>',      // from ChurnWarn Settings → API keys
    serverUrl: 'https://your-gateway.example.com', // omit if same-origin
    tenantId: '00000000-0000-0000-0000-000000000000',
    autoCapture: true                     // page views, clicks, sessions, errors
  });

  // Call after the user is authenticated and you know their account id
  ChurnWarn.identify('account-external-id-123');
</script>

That's all you need. Auto-capture handles page views, session tracking, clicks, errors, and scroll depth automatically once identify() is called.

Call configure and identify as early as possible (ideally before your app paints) so session and page events are not missed.

Auto-capture

When autoCapture is enabled, the SDK emits health-oriented events automatically after identify(accountId):

| Event | When | |-------|------| | page_viewed | Initial identify + every URL change — payload includes url (origin + path only), path, and route (normalized) | | session_started | New session (first visit or idle longer than timeout) | | session_ended | Tab hidden or page unload (session_minutes in payload) | | session_heartbeat | Every 60s while the tab is visible and the user was active in the last 90s | | element_clicked | Clicks on buttons, links, inputs, and labeled elements — payload.label has the element name | | feature_used | Any click inside a data-feature="…" section | | rage_click | 3+ clicks on the same element within 2 s | | form_abandoned | Input typed into but form never submitted | | scroll_depth | 25 / 50 / 75 / 100 % scroll milestones | | js_error | Uncaught errors and unhandled promise rejections |

Route normalization

page_viewed payloads include a route field — the pathname with high-cardinality ID segments auto-collapsed to :id:

/orders/9281           → /orders/:id
/users/abc-123/profile → /users/:id/profile
/static/home           → /static/home   (no collapse — not ID-like)

Use this in ChurnWarn event-map conditions to route page views to metrics:

page_viewed  →  feature_used   condition: {"payload.route":"/billing*"}
page_viewed  →  plan_upgraded  condition: {"payload.route":"/checkout/success"}

Override or extend the auto-collapse rules with routes.normalize:

ChurnWarn.configure({
  autoCapture: {
    enabled: true,
    routes: {
      normalize: [
        { pattern: '/reports/*', as: '/reports/:id' },
        { pattern: '/orgs/*/members', as: '/orgs/:id/members' }
      ]
    }
  }
});

User-defined rules are evaluated first; the auto-collapse (UUID / integer / long-hex) runs as a fallback.

Privacy and sensitive data

Auto-capture is designed to avoid leaking query strings, URL fragments, and raw DOM text:

  • url and referrer include only origin + pathname. Query parameters and hash fragments are never sent.
  • element_clicked does not capture visible innerText / textContent. Use data-churn-warn-label for stable click names.
  • Click labels derived from data-testid, id, aria-label, or name are masked for common sensitive patterns (emails, tokens, phone numbers, etc.).
  • Mark sensitive UI with data-churn-warn-ignore to exclude it from click capture entirely.
  • Prefer payload.route (not full URLs) in event-map conditions.

Configuration

// Shorthand — enables all auto-capture with defaults
ChurnWarn.configure({ token: '...', autoCapture: true });

// Full options object
ChurnWarn.configure({
  token: '...',
  autoCapture: {
    enabled: true,
    trackClicks: true,             // default true
    sessionTimeoutMinutes: 30,     // default 30; new session after this idle gap
    captureErrors: true,           // default true
    captureRageClicks: true,       // default true
    captureFormAbandons: true,     // default true
    captureScrollDepth: true,      // default true
    captureFeatures: true,         // default true
    routes: {
      normalize: []                // custom route rules (see above)
    }
  }
});

// Disable and tear down listeners
ChurnWarn.configure({ token: '...', autoCapture: false });

Auto-capture attaches listeners when enabled but does not send events until identify(externalAccountId) is called. Re-calling identify with the same id is a no-op; a different id ends the prior session and starts fresh.

Click labeling

Use attributes on interactive elements:

  • data-churn-warn-label="submit_checkout" — preferred stable name (also makes non-standard elements clickable).
  • data-churn-warn-ignore — exclude element and descendants from click capture.

Otherwise the SDK derives a name from data-testid, id, aria-label, name, or a tag_role fallback. Visible button/link text is not captured. Names are sanitized to [a-z0-9_] and masked for common sensitive patterns.

Domain signals for dashboard templates

The auto-capture table above feeds the default SaaS/PLG dashboard. For the business-type templates (e-commerce, subscription-box, fintech, mobile, marketplace) you also need their domain signals — order_placed, product_viewed, cart_created, transaction, etc. Two zero-to-low-code ways to emit them from the browser:

1. data-cw-signal attributes (declarative)

Tag any element. On click, auto-capture emits the named signal — the value goes in payload.value (read by the sum_payload RFM/GMV components) and any other data-cw-* attribute becomes a payload field:

<button data-cw-signal="order_placed" data-cw-value="49.90">Buy now</button>
<a data-cw-signal="product_viewed" data-cw-product-id="sku_123">Details</a>
<button data-cw-signal="transaction" data-cw-value="120" data-cw-side="buyer">Book</button>

Numeric data-cw-* values are coerced to numbers; text values are masked for sensitive patterns and length-capped. Use canonical keys from ChurnWarn.Metrics. Toggle with autoCapture.captureDeclaredSignals (default on).

2. E-commerce dataLayer / gtag bridge

If your store already emits GA4 / Google Tag Manager enhanced-ecommerce events, turn on the bridge and they are mirrored to ChurnWarn automatically:

ChurnWarn.configure({
  apiKey: '...',
  autoCapture: { enabled: true, ecommerce: true }
});

| GA4 / GTM event | ChurnWarn signal | |-----------------|------------------| | purchase | order_placed (with value) | | view_item | product_viewed | | add_to_cart, begin_checkout | cart_created | | refund | order_returned | | checkout started but no purchase before unload | cart_abandoned |

Both window.dataLayer.push({ event, ecommerce: { value } }) and gtag('event', 'purchase', { value }) shapes are supported. Remap or extend the table via ecommerce: { map: { purchase: 'order_placed', quote_requested: 'transaction_request' } }, and disable the gtag wrapper with ecommerce: { gtag: false }.

Account facts — identify(id, traits) / upsertAccount(id, traits)

Some template signals are slow-changing account facts, not events: the fintech direct_deposit/kyc_completed flags, the mobile push_opt_in flag, a marketplace account's role, or a headline money figure. Pass a traits object to identify (or call upsertAccount) and they are written to PUT /api/accounts/{id}:

ChurnWarn.identify('acct-123', {
  planKey: 'pro',            // known column
  monetaryValue: 480,        // known column
  valueBasis: 'ltv',         // known column
  role: 'buyer',             // marketplace side
  push_opt_in: true,         // → attributes bag
  kyc_completed: true        // → attributes bag
});

Known keys (name, email, kind, businessType, monetaryValue, valueBasis, currency, planKey, lifecycleStage, renewalAt, status, role) map to account columns; everything else folds into the account attributes bag. The commercial block yields to a connected billing provider. Put metrics (things you count/sum) in events, facts (a balance, an opt-in) in traits.

Recommended event-map configuration

After installing the SDK, open Settings → Event map in your ChurnWarn project and add these mappings:

| Raw event type | Mapped metric | Condition (optional) | |----------------|--------------|----------------------| | element_clicked | feature_used | — | | page_viewed | active_user | — | | page_viewed | feature_used | {"payload.route":"/billing*"} | | session_ended | session | — | | session_heartbeat | active_user | — |

For element_clicked and page_viewed you only need one row each — conditions let you fan-out the same raw type to different metrics. Enable the Pattern checkbox on element_clicked if you still have old element_clicked.* data from v0.5.

Constants are available on ChurnWarn.AutoCaptureEvents.

serverUrl

  • If you omit serverUrl or pass an empty string, the SDK uses window.location.origin when sending (same-origin as the page). That matches setups where the SPA and the API share a host (for example behind a reverse proxy that serves /api).
  • If your UI and API live on different origins, set serverUrl to the gateway root without a trailing slash (for example https://api.example.com). Requests go to {serverUrl}/api/events.

tenantId

  • Optional. If omitted, the server resolves the tenant from the JWT tenant_id claim.
  • Pass tenantId when you need to override the claim (must be a UUID string the authenticated user is allowed to access).

token

  • A Bearer access token accepted by your Gateway (same style as the web app). You may pass either '<token>' or 'Bearer <token>'; the SDK normalizes to a single Authorization: Bearer … header.

captureEvent(externalAccountId, eventName, extraData?)

| Argument | Description | |----------|---------------| | externalAccountId | Customer account id in your system (required, max 100 characters after trim). | | eventName | Event type string. Use ChurnWarn.Metrics.*, ChurnWarn.RawEvents.*, ChurnWarn.AutoCaptureEvents.*, or any custom string your tenant maps in ChurnWarn (max 100 characters). | | extraData | Optional plain object. Serialized with JSON.stringify and sent as the API payload field (a JSON string on the wire). Omitted or null becomes {}. |

The HTTP call uses fetch and returns immediately; success and failure are handled internally.

Event name constants

There are no TypeScript-style enums in the bundle. Use frozen objects whose values are the strings sent to the API:

  • ChurnWarn.Metrics — canonical keys such as LOGIN → 'login', FEATURE_USED → 'feature_used', etc. (mirror of backend DefaultDashboardMetricKeys).
  • ChurnWarn.RawEvents — dotted aliases such as APP_LOGIN → 'app.login' (mirror of backend EventTestGeneratorAliases).
  • ChurnWarn.AutoCaptureEvents — auto-capture event names such as PAGE_VIEWED → 'page_viewed'.

Example:

ChurnWarn.captureEvent('acct-1', ChurnWarn.RawEvents.APP_FEATURE_USED, { feature: 'export_csv' });
ChurnWarn.captureEvent('acct-1', ChurnWarn.Metrics.FEATURE_USED, { feature: 'export_csv' });

Batching, retries, and offline delivery

Unlike the server SDKs, the browser SDK has no tunable retry options — the delivery policy is fixed so the bundle stays small and predictable:

| Behaviour | Value | |-----------|-------| | Batch flush | when 10 events are queued, or every 2 s, whichever comes first | | Retried failures | network errors and HTTP 5xx | | Not retried | 4xx (bad token, validation) — the event is dropped and logged | | Offline queue | up to 100 events in localStorage; oldest dropped when full | | Queue drained | on SDK load, and 1.5 s after the browser fires an online event |

A failed batch is written to localStorage under churn_warn_offline_q and retried in a later page load. Events are redacted before they are persisted, so sensitive payload data never lands on disk. A drain that itself fails is not re-queued, which prevents a poison batch from looping forever.

session_ended is sent as a single keepalive request so it survives page unload; if that fails it also falls back to the offline queue.

Call flush() to send the pending batch immediately instead of waiting for the 2 s timer — useful just before a deliberate navigation or logout.

Error handling and logging

The SDK is written so callers do not need try/catch:

  • configure, identify, and captureEvent catch their own synchronous errors and log with the prefix [ChurnWarn] via console.warn.
  • Network and non-OK HTTP responses are logged the same way; captureEvent never throws and does not reject a promise to the caller (it returns undefined).

Check the browser console if events do not appear server-side.

CORS and authentication

  • The Gateway route is POST /api/events and requires Authorization: Bearer <token>. Your page origin must be allowed by the API CORS configuration.
  • The token must be valid and, when tenantId is omitted, include a usable tenant_id claim unless you pass tenantId explicitly.

Browser support

The bundle targets ES5 syntax for broad compatibility. You still need:

  • fetch
  • Promise
  • sessionStorage (for auto-capture sessions)

Very old browsers without these need polyfills. JSON must exist for non-empty extraData objects.

Building the bundle from source

From this directory:

npm install
npm run build

Output: dist/churn-warn.umd.min.js (IIFE, global name ChurnWarn).

Source entry: src/index.js.

API reference summary

| Export / global | Type | Description | |-----------------|------|-------------| | configure(opts) | function | opts: { apiKey?, token?, serverUrl?, tenantId?, source?, redact?, autoCapture? } — redact defaults to true (see Privacy); autoCapture accepts true or { enabled, trackClicks, sessionTimeoutMinutes, captureErrors, captureRageClicks, captureFormAbandons, captureScrollDepth, captureFeatures, captureDeclaredSignals, ecommerce, routes: { normalize } } | | identify(externalAccountId, traits?) | function | Account id for auto-capture; traits upserts account facts to PUT /api/accounts | | upsertAccount(externalAccountId, traits) | function | Write account facts without touching the auto-capture session | | captureEvent(accountId, eventName, extraData?) | function | Queues one event for the next batch flush; always non-throwing | | flush() | function | Sends the pending batch immediately instead of waiting for the 2 s timer | | Metrics | object | Canonical eventType strings — all business-type template signals | | RawEvents | object | Dotted raw eventType aliases the gateway maps to Metrics | | PayloadFields | object | Payload keys read by sum_payload/avg_payload (value, side, quantity) | | AccountAttributes | object | Account fact keys (direct_deposit, push_opt_in, …) | | BusinessTypes | object | Dashboard-template keys (ecommerce, fintech, …) | | AutoCaptureEvents | object | Read-only catalog of auto-capture eventType strings | | version | string | SDK version |