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

@clickroom/sdk-js

v0.9.0

Published

Browser SDK for Clickroom — product analytics, feature flags and experiments.

Readme

@clickroom/sdk-js

Browser SDK for clickroom — autocapture, batched event transport, and feature flags. Ships two ways: a CDN <script> snippet for plain HTML pages and an npm ESM package for bundled apps.

Install

Option A — CDN snippet (recommended for websites)

Paste this into your page <head>. It installs a queued stub immediately, loads the real bundle asynchronously, and replays any calls made before load:

<script>
  "use strict";(()=>{(function(i,s){var p="http://localhost:3001",_="/static/clickroom.js",l=["init","capture","identify","reset","isFeatureEnabled","getFeatureFlag","onFeatureFlags"];if(i.clickroom&&i.clickroom.__loaded__)return;var t=i.clickroom||{};t._q=t._q||[],t.__stub__=!0;function f(r){return function(){var e=Array.prototype.slice.call(arguments);t._q.push([r,e]),r==="init"&&e[1]&&e[1].api_host&&(p=e[1].api_host)}}var o;for(o=0;o<l.length;o++)t[l[o]]=f(l[o]);i.clickroom=t;function h(){var r=i.clickroom,e,n,u;if(!(!r||r.__stub__))for(e=0;e<t._q.length;e++)n=t._q[e][0],u=t._q[e][1],typeof r[n]=="function"&&r[n].apply(r,u)}var a=s.createElement("script");a.type="text/javascript",a.async=!0,a.src=p.replace(/\/$/,"")+_,a.onload=h;var c=s.getElementsByTagName("script")[0];c&&c.parentNode?c.parentNode.insertBefore(a,c):s.head.appendChild(a)})(window,document);})();

  clickroom.init('ckw_live_xxx', { api_host: 'https://your-host', autocapture: true })
</script>

The readable source of this snippet is loader.js.

Option B — npm

pnpm add @clickroom/sdk-js
import clickroom, { init, capture } from '@clickroom/sdk-js'

init('ckw_live_xxx', { api_host: 'https://your-host', autocapture: true })
capture('signup_started', { plan: 'pro' })
// or via the default singleton:
clickroom.capture('signup_completed')

Quickstart

import clickroom from '@clickroom/sdk-js'

clickroom.init('ckw_live_xxx', {
  api_host: 'https://your-host',
  autocapture: true, // auto-track clicks, submits, and SPA pageviews
  capture_pageview: true,
})

Events are batched (flushed by size/interval) and POSTed to {api_host}/v1/capture as { batch: EventPayload[] }. A beforeunload beacon flushes the remaining queue.

Error handling

If the server rejects a batch outright (HTTP 4xx — e.g. a schema-invalid event under strict validation), the SDK does not retry: retrying a request the server will never accept would just waste the retry budget and delay delivery of everything behind it. The batch is dropped and the failure is surfaced via console.warn, including the response body (e.g. { accepted, errors }) when the server returns one, so you can see which event/property failed validation. Network errors and 5xx responses are unaffected — the SDK retries those with its existing bounded exponential backoff.

Public API

| Method | Signature | Description | | --- | --- | --- | | init | init(writeKey, options?) | Initialise the SDK. options: api_host, autocapture, capture_pageview. | | capture | capture(event, properties?) | Enqueue a custom event. | | identify | identify(distinctId, properties?) | Associate the current session with a user id. | | reset | reset() | Clear the current identity / anonymous id. | | isFeatureEnabled | isFeatureEnabled(key): boolean | Whether a boolean flag is on. | | getFeatureFlag | getFeatureFlag(key): string \| boolean \| undefined | Flag value (multivariate). | | onFeatureFlags | onFeatureFlags(cb) | Subscribe to flag updates. |

All of the above are available both as named exports and as methods on the default clickroom singleton (and on window.clickroom when loaded via the CDN bundle).

Feature flags

Flag methods (isFeatureEnabled / getFeatureFlag / onFeatureFlags) are fully implemented: flags are fetched from {api_host}/v1/flags (with a cheap {api_host}/v1/flags/version check ahead of it to skip the full fetch when nothing changed), evaluated, cached in localStorage, and re-fetched on identify() or via the optional feature_flag_poll_interval.

Support widget

Opt-in (support is off by default — an existing embed fetches zero extra bytes). When enabled, init() fetches + caches the form config from GET /v1/support, gates each form on its linked flag, and builds the support client. There are three shapes, and they differ only in where the UI mounts — identity, event context, form analytics and realtime replies are wired identically in every mode. The tracking story does not change between the FAB and an inline page.

1. Default FAB — unchanged. support: true behaves exactly as before: it auto-mounts the floating launcher. This is byte-identical to prior releases; nothing about support: true changed.

clickroom.init('WRITE_KEY', { support: true })
// normalises to { enabled: true, mode: 'launcher' }

2. Page-only (mode: 'manual'). Build the client but mount nothing — you place the support surface yourself as a full page. Nothing auto-mounts; no FAB.

clickroom.init('WRITE_KEY', { support: { enabled: true, mode: 'manual' } })

React — extend the existing <ClickroomSupport> with mode="inline":

import { ClickroomSupport } from '@clickroom/sdk-js/react'

// e.g. a /support route
<ClickroomSupport mode="inline" />

Vanilla (Vue / Svelte / Angular / plain JS) — clickroom.support.mount() returns the same SupportRenderHandle (open() / close() / refresh() / destroy()). Call destroy() on route leave so a route change doesn't leak the host:

const handle = clickroom.support.mount({
  el: '#support',          // HTMLElement | selector string
  mode: 'inline',          // 'inline' (default) fills the container | 'launcher'
  view: 'home',            // 'home' (default) | 'search' | 'bot' | 'new' | 'tickets' | 'thread'
  home: 'search',          // 'search' (default) | 'conversations' — the home surface's posture
  // formId, ticketId, onTicketOpen, onTicketCreated optional
})
// on unmount:
handle?.destroy()

view picks the opening surface: 'home' is the search-first landing, 'search' the docs search, 'bot' the AI assistant, 'new' a fresh conversation, 'tickets' the conversation list, and 'thread' a single thread (implied when you pass ticketId without a view). home only affects the 'home' surface, and falls back to 'conversations' automatically when the form has no docs source connected. The same view / home props exist on <ClickroomSupport mode="inline" />.

clickroom.support.open() / .close() drive the support surface from a customer's own "Help" nav item — the one you mounted via mount() if there is one, otherwise the auto-mounted FAB. Both are no-ops when support is off or nothing is mounted.

3. Both (mode: 'both'). Auto-mount the FAB and allow manual mounts — a FAB on marketing pages plus a full page at /support.

clickroom.init('WRITE_KEY', { support: { enabled: true, mode: 'both' } })

mode gates exactly one thing — whether the FAB auto-mounts. 'launcher' (default) and 'both' auto-mount it; 'manual' does not. Config fetch, flag gating, the ticket-token store, the recentEvents context ring, the realtime subscription and every $support_* event are the same across all three.

Build

pnpm --filter @clickroom/sdk-js build

Outputs to dist/:

  • clickroom.js — minified IIFE CDN bundle (entry src/global.ts, exposes window.clickroom)
  • index.js — ESM entry for npm consumers (entry src/index.ts)
  • index.d.ts — type declarations (tsc --emitDeclarationOnly)
  • *.js.map — source maps

Build pipeline: esbuild.config.mjs then tsc for types.

Demo

demo/index.html is a manual / Playwright test page. Build first, then open it (or serve the package dir) and watch batched 202s in the Network tab.