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

@hanzogui/telemetry

v8.3.5

Published

Zero-config Hanzo telemetry — one <TelemetryProvider/> wires errors + session capture, pageviews, and product events to the ONE endpoint (POST api.hanzo.ai/v1/event), which lenses them into sentry.hanzo.ai, analytics.hanzo.ai and insights.hanzo.ai. Honors

Readme

@hanzogui/telemetry

Zero-config telemetry for any Hanzo surface — web, desktop and native.

If you mount <GuiProvider> from @hanzo/gui, you already have it. There is nothing to install, import or wire: pageviews, unhandled errors, React render errors and interaction capture reach the one Hanzo endpoint, with the product name resolved from the runtime and the ingest key read from the environment.

import { GuiProvider } from '@hanzo/gui'

<GuiProvider config={config}>{children}</GuiProvider>   // telemetry included
<GuiProvider config={config} telemetry={false}>…        // opt out
<GuiProvider config={config} telemetry={{ product: 'studio' }}>…

Mount it yourself only when you are not a gui app, or when you need to drive pageviews from your router:

import { TelemetryProvider } from '@hanzogui/telemetry'

export default function Root({ children }) {
  return <TelemetryProvider path={usePathname()}>{children}</TelemetryProvider>
}

It is also available from the component layer as @hanzo/ui/telemetry, which re-exports this package unchanged — so an app that already depends on @hanzo/ui adds nothing.

One stream, never two

GuiProvider mounts <TelemetryProvider owner="gui">, and that posture is the whole reason it is safe to turn on for every app at once: a gui-owned provider collects only while nothing else is collecting.

| The app mounts | Where | What gui does | | --------------------------------------------- | ------------------- | -------------------------------------------------------- | | nothing | — | collects — this is the free case | | <AnalyticsProvider> / <TelemetryProvider> | above GuiProvider | goes inert, and passes the app's client straight through | | <TelemetryProvider> | below GuiProvider | goes inert; the app owns the stream | | a second <GuiProvider> | anywhere | one client, one stream |

Ownership is a claim on a module-scope slot, not a render-time guess, so it survives late mounts (a lazy route takes the stream) and unmounts (gui takes it back). owner="app" is the default for a provider you mount yourself and always wins.

The one shape gui cannot see is a raw @hanzo/event <AnalyticsProvider> below GuiProvider: React context does not look down, and unlike <TelemetryProvider> a raw one claims nothing. Those apps pass telemetry={false} until they drop their own wiring.

One endpoint, three lenses

Everything — pageviews, product events, exceptions, interaction capture — is POSTed as one batched stream to the one Hanzo endpoint:

POST https://api.hanzo.ai/v1/event   { batch: [Event, …] }

Cloud lenses that single stream into three views:

| Lens | Host | What it shows | | ---------------- | -------------------- | ------------------------------------------ | | Errors | sentry.hanzo.ai | exceptions, stacks, session capture | | Web analytics | analytics.hanzo.ai | pageviews, referrers, campaigns | | Product insights | insights.hanzo.ai | funnels, notebooks, analytics_errors |

Errors land in insights too, so a funnel drop joins to the exception that caused it. Those three hosts are dashboards. They are never ingest endpoints: there is exactly one API host, which is why there is nothing to configure.

What you get for free

  • Pageviews, including SPA route changes. Pass path (Next: usePathname()) to drive them from your router, or pass nothing and the History API drives them.
  • Errors — window.onerror, unhandled rejections, and React render errors. This is the @sentry replacement; there is no browser Sentry SDK and no CDN script, so a default-src 'none' CSP cannot break it.
  • Session capture — every click/input/submit annotated with a semantic hierarchy (navigation/Dashboard/UserCard/button[save]), so a replay is readable rather than a pixel movie. Loaded with a dynamic import() inside an idle callback, in its own chunk, so it never costs LCP.
  • First-touch attribution and cohorts (channel, refCode, signupWeek) on every event.
  • Product inference from the hostname — console.hanzo.ai reports as console, hanzo.chat as chat, hanzo.ai as site.

Privacy

  • Do-Not-Track and Global Privacy Control are honored by default. With either signal set and no explicit in-app choice, nothing is collected and nothing is written to storage.

  • Consent-aware. An explicit choice the person makes in your UI outranks the browser default in both directions:

    import { setConsent, useConsent } from '@hanzogui/telemetry'
    
    const choice = useConsent() // 'granted' | 'denied' | undefined
    setConsent('denied') // takes effect immediately, everywhere
    setConsent('unset') // forget it; DNT/GPC applies again

    Revocation is prospective: collection stops at once. Events already buffered under a live consent may complete their in-flight flush.

  • Input values are withheld from capture by default. data-hz-private on any element excludes its whole subtree.

Anywhere else

Non-React code does not need the provider — the ambient client builds itself from the environment on first use, and the provider registers its own client into the same slot, so there is one instance and one stream either way.

import { track, identify, captureError, EVENTS } from '@hanzogui/telemetry'

identify(user.id)
track(EVENTS.PLAN_CLICKED, { plan: 'pro' })
try {
  risky()
} catch (err) {
  captureError(err, { properties: { where: 'checkout' } })
}

In components

import { useTelemetry, useTrack } from '@hanzogui/telemetry'

const t = useTelemetry() // total: never null, never throws, SSR-safe
const track = useTrack() // just the recorder

Configuration (all optional)

Environment, read from process.env (Next), import.meta.env (Vite/Expo), or a window.__HANZO_TELEMETRY__ object for pages with no build step:

| Variable | Default | Meaning | | -------------------------------------------------------------------------------------- | -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | NEXT_PUBLIC_PUBLISHABLE_KEY / VITE_PUBLISHABLE_KEY / EXPO_PUBLIC_PUBLISHABLE_KEY | — | Publishable pk-… key — the one name, the one KMS carries at deploy/PUBLISHABLE_KEY and the one @hanzo/event reads. Write-only and safe in a bundle. Without it a beacon is unattributed and the edge refuses it. (The older …_HANZO_INGEST_KEY spelling is still read, second, and is being retired.) | | NEXT_PUBLIC_HANZO_API_URL / VITE_HANZO_API_URL | https://api.hanzo.ai | The one ingest host. | | NEXT_PUBLIC_HANZO_PRODUCT / VITE_HANZO_PRODUCT | resolved from the runtime, then the hostname | Emitting surface. A Tauri window reports as desktop — it serves one bundle from three origins (tauri://localhost, http://tauri.localhost, the dev server), so the URL is the one thing that cannot name it. | | NEXT_PUBLIC_HANZO_TELEMETRY / VITE_HANZO_TELEMETRY | on | 0/false/off is a build-time kill switch. |

Props, when you want them:

<TelemetryProvider
  path={usePathname()} // drive pageviews from your router
  product="console" // override the inferred surface
  host="" // same-origin, cookie-auth apps
  getToken={() => token} // bearer-auth apps
  replay={false} // no interaction capture
  errors={false} // no error capture
  pageviews={false} // no pageviews
  consent="granted" // your own consent gate decided
  enabled={false} // hard kill switch, wins over everything
  fallback={(err, reset) => <Crash error={err} onReset={reset} />}
/>

With no fallback, a render error is reported and then re-thrown so your own error UI still runs. Observing must not change behavior.

Guarantees

  • SSR-safe. Importing does nothing; rendering on the server does nothing.
  • Native mounts, and stays silent for now. GuiProvider wires telemetry identically on React Native, but a DOM is still the precondition for collecting: @hanzo/event's init() reads window.location.search and document.referrer behind a typeof window check, and React Native defines a window with neither. Emitting on native needs that check to require a document — an @hanzo/event fix, not a gui one. Until then native is wired and inert rather than crashing.
  • Fail-soft. Every public method swallows its own errors, network loss is ignored, and a failed capture-engine chunk simply means no capture.
  • Tree-shakable. sideEffects: false, ESM, one export per concern.
  • No CDN. Nothing is loaded from a third-party origin, ever.

Composition

@hanzo/event      the ONE client — batching, attribution, the wire, the endpoint
@hanzo/observe    the capture engine — semantics, redaction, playback stream
@hanzogui/telemetry  ← this: the zero-config policy that wires them together
@hanzo/ui/telemetry   re-export, so the component layer needs no new dependency

Mechanism lives below, policy lives here, and there is exactly one of each.