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

statewire

v0.14.0

Published

State over the wire: snapshot-first delta streaming and command submission for any state and any command

Downloads

42,427

Readme

statewire

React/TypeScript client for the Statewire protocol: a server owns a JSON state object and streams it to clients as packets (POST /stream attach, hello-snapshot first); clients change it by submitting stamped statement frames (POST /frames, {"cmd": [{"method", "params", "seq"}]}). The client keeps your UI on the canonical state with optimistic updates rebased on top, survives disconnects, evictions, and reloads of the server, and tells you exactly what happened to every command you sent.

import { useStatewire, StatewireSSE } from "statewire";

type Commands = {
  increment(): void;
};

const { state, connection, commands } = useStatewire<State, Commands>({
  transport: StatewireSSE({ url: "/api/threads/demo" }),
  optimistic: (draft, command) => {
    if (command.method === "increment") draft.count += 1;
  },
});

const onClick = async () => {
  await commands.increment(); // resolves when the server has provably applied it
};

What you get

  • stateundefined until the first snapshot, then canonical server state with your pending commands' optimistic effects rebased on top. When the server's ack arrives, the optimistic overlay drops in the same tick: no flicker.

  • connection — connection health as a tagged union: connection.status is "connecting" | "live" | "reconnecting" | "gone", and each variant carries exactly the data that exists in that situation: reconnecting has cause ("dropped", "evicted", or "error"), attempt, nextRetryAt, lastError?, message?; gone carries the server's message? and payload?. connection.reconnect() re-attaches manually from gone, or "retry now" during backoff.

  • commands — a typed proxy over the Commands method map: commands.increment() submits { method: "increment", params: [] } and resolves with the handler's result only once the stream ack proves the effect is in canonical state. Rejects with StatewireSendError, whose failure.fate carries the verdict; the boundary is definitiveness:

    • "aborted" — definitely not applied (never POSTed, or every attempt accounted for); safe to resend
    • "lost" — a POST left the machine and never got a definitive answer; whether the server applied it is unknown — check the snapshot for your payload ids
    • "rejected" — the server said no; not applied (message, optional payload from the backend's StatewireReject)
    • "unknown-command" / "invalid-params" — the server does not know the method, or the args failed its schema
    • "crashed" — acked, but the processing died before producing a result
    • "result-unavailable" — acked, but the result can no longer be delivered

    Every failure also carries cause — which channel settled it: "server" = the server's answer, "stream-gone" = terminal finish(gone), "session-lost" = the server forgot the client id (eviction), "unmount" is client-side. An "aborted" failure carries predecessorLost: true when an earlier in-flight command of the same batch has uncertain fate. The taxonomy's single exported type is StatewireClient.SendFailure; the fate and cause unions are reachable as SendFailure["fate"] / SendFailure["cause"].

    An eviction replays every unsettled command under the rotated client id, in seq order — fresh seqs, relative order preserved, the original promise settles with the replay's outcome. Context submissions settle as session-lost failures and the stored context re-sends whole; replay may re-run a command the lost session already applied.

Reconnects are not errors: transport drops and server evictions self-heal with exponential backoff while state stays renderable. Commands carry a monotonic seq, so retries are deduplicated server-side and a reconnect splices already-applied commands instead of re-running them.

A client id may hold any number of concurrent attaches: each gets its own hello and lease, and cmd answers fan out to every live attach of the id. A lease lives and dies with its attach — a 423 means that attach is gone: re-attach and resend.

HTTP attach and follow-up frame bodies are limited to 8 MiB; oversized bodies receive 413 Payload Too Large before JSON parsing.

Three ways to mount

One implementation, three entry points:

useStatewire(options)               // React hook
StatewireResource(options)          // @assistant-ui/tap resource, for composition
new StatewireClient(options)        // framework-free: .state/.connection/.commands,
                                    // .subscribe(cb), .dispose()

StatewireClient#subscribe follows the external-store contract (useSyncExternalStore-compatible). dispose() settles pending commands like an unmount.

Every public type merges onto the class as a namespace — StatewireClient.Connection, StatewireClient.ConnectionStatus, StatewireClient.Options, StatewireClient.SendFailure, … — so one import covers the whole surface.

There is no session key: a transport url change reattaches in place and the attach snapshot arbitrates identity (an unknown client evicts — pending settles, the client id rotates). For a forced fresh session, key the resource — withKey(threadId, StatewireResource(options)); pending commands settle with reason "unmount".

Transport

StatewireSSE({
  url,        // endpoint root — the transport appends /stream and /commands
  headers,    // HeadersInit or (async) () => HeadersInit, re-evaluated per request
  fetch,      // custom fetch — the escape hatch for everything else
})

StatewireWS({ url, protocols, query, webSocket }) is the full-duplex sibling over /ws. A transport is a tap resource producing the Statewire value the client consumes; createMockTransport from statewire/testing is a complete in-memory driver for tests.

Backend

Any server speaking the Statewire convention works. The Python implementation lives in this repo (python/statewire/statewire):

from statewire import Statewire, StatewireReject, command

class Counter(Statewire):
    @command
    async def increment(self):
        if self.state["count"] >= 10:
            raise StatewireReject("counter is full", payload={"max": 10})
        self.state["count"] += 1

See examples/vite-statewire-example (frontend) and python/statewire/examples/demo_app.py (backend) for a complete runnable pair, and the repo's protocol spec for the wire format.