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

@waves-counter/client

v0.2.2

Published

Framework-neutral browser client for Wave Counter

Readme

@waves-counter/client

Framework-neutral browser client and state controller for Wave Counter.

Use this package when you want to talk to a Wave Counter HTTP endpoint from any browser UI: vanilla TypeScript, React, Svelte, custom elements, or a design system component of your own. If you use Vue, start with @waves-counter/vue instead; it is built on top of this package.

Install

npm install @waves-counter/client

What it does

  • Calls the Wave Counter HTTP contract: read totals, record idempotent events, and load 7d, 1M, or all analytics.
  • Generates UUIDv7 event IDs by default so retrying an increment does not double-count.
  • Exposes a small WaveCounterController for optimistic UI state, pending increments, analytics popovers, and errors.
  • Has no framework dependency and no opinion about auth, routing, styling, or deployment.

Quick start

import { WaveCounterClient, WaveCounterController } from '@waves-counter/client'

const transport = new WaveCounterClient({
  endpoint: '/api/waves',
})

const counter = new WaveCounterController('coffee', transport)

counter.subscribe((state) => {
  document.querySelector('[data-total]')!.textContent =
    state.counter?.total.toString() ?? '—'
})

await counter.load()

document.querySelector('button')!.addEventListener('click', () => {
  void counter.increment().catch(console.error)
})

HTTP endpoint expected by the client

Mount a Wave Counter backend at any prefix and pass that prefix as endpoint.

GET  /counters/{key}
POST /counters/{key}/events
GET  /counters/{key}/analytics?window=7d|1M|all

The event request body is:

{ "eventId": "0198f2f7-6d42-7d94-b1a6-e4305543f132" }

A new event returns 201. Replaying the same event ID returns 200 and the same authoritative counter total.

WaveCounterClient

const client = new WaveCounterClient({
  endpoint: '/api/waves',
  fetch: window.fetch,
  eventId: () => crypto.randomUUID(),
})

| Option | Required | Description | | --- | --- | --- | | endpoint | Yes | Base URL where the Wave Counter routes are mounted. Trailing slashes are removed. | | fetch | No | Custom fetch implementation for tests, auth wrappers, or non-browser runtimes. | | eventId | No | Function used by increment(). Defaults to UUIDv7. |

Methods:

await client.getCounter('coffee')
await client.recordEvent('coffee', '0198f2f7-6d42-7d94-b1a6-e4305543f132')
await client.increment('coffee')
await client.getAnalytics('coffee')
await client.getAnalytics('coffee', '1M')

Failed HTTP responses throw WaveCounterHttpError with status, code, and retryAfter.

WaveCounterController

The controller is useful when the UI should feel instant while the backend remains authoritative.

const controller = new WaveCounterController('coffee', client, {
  showStats: true,
})

const unsubscribe = controller.subscribe((state) => {
  state.counter          // latest total plus pending optimistic increments
  state.pendingIncrements
  state.loading
  state.error
  state.analytics
  state.analyticsWindow
  state.analyticsLoading
  state.analyticsError
  state.statsOpen
})

await controller.load()
await controller.increment()
await controller.openStats()
await controller.setAnalyticsWindow('all')
controller.closeStats()
unsubscribe()

TypeScript types

The package ships its own declarations. Useful exported types include CounterSnapshot, Analytics, AnalyticsPoint, AnalyticsWindow, WaveCounterTransport, WaveCounterState, and WaveCounterListener.

Production notes

  • Put authentication, CORS, rate limiting, and bot protection in your host application.
  • Use stable counter keys such as coffee, likes, or downloads. Keys are URL-encoded by the client.
  • Handle WaveCounterHttpError.code === 'busy' as a retryable storage-pressure response. Backends include Retry-After: 1.
  • If you wrap fetch to add credentials or headers, keep the idempotent event ID behavior intact.

Related packages

  • @waves-counter/vue: accessible Vue component and composable.
  • @waves-counter/node: native Node and Express backend integration.
  • wave-counter: Python bindings and FastAPI integration.