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

@re-cinq/floor-client

v0.2.0

Published

The floor over HTTP and its live socket, typed: definitions, runs, visits, blobs, events, schedules and costs, plus a watch that replays a run from a cursor and reconnects without losing a frame.

Readme

@re-cinq/floor-client

The floor over HTTP and its live socket, typed. Every route a floor serves, and a watch that replays a run from a cursor and comes back without losing a frame.

import { createFloorClient, serviceToken } from "@re-cinq/floor-client";

const floor = createFloorClient({ url: "https://floor.example.com", token: serviceToken(process.env.FLOOR_SERVICE_TOKEN!) });

const { run } = await floor.lines.start("code-review", {
  repo: "github.com/re-cinq/lore",
  startItems: { pr_url: { kind: "value", ref: "https://github.com/re-cinq/lore/pull/412", by: "me" } },
});

for await (const frame of floor.runs.watch(run.id)) {
  if (frame.type === "run_settled") break;
}

repo is left out for a line with no git argument, and the run then has none: run.repo is null. floor.runs.list({ withoutRepo: true }) lists those runs.

Where this fits

The one way anything reaches a floor from outside it. apps/cluster-agent, @re-cinq/floor-station, @re-cinq/floor-pipeline and the lore converter all talk to a floor through this, and so does lore. It links no part of the floor's own storage — only @re-cinq/floor-contracts, for the shapes — so a floor's database is not reachable through it even by accident.

See the map.

What it decides for you

Two tokens, and neither fits where the other belongs. ServiceToken and VisitToken are branded, so one cannot be passed for the other. createVisitClient goes further: it binds the visit id at construction and takes one in no method, so addressing another visit is not something a caller can express. The floor refuses that with a 403; this refuses it at the compiler. The same client has no events.post — it has report(), which fills in the event's name, its visit and its dedupe key, because none of those are the caller's to choose.

A status that carries meaning is in the return type, not an exception. Absence is null, an empty queue is [], and where a refusal is a real decision it is a named union:

const outcome = await floor.stationRuns.brief(visitId);

if (outcome.kind === "reported") return ack(event);      // 409: already done, run nothing
if (outcome.kind === "absent") return deadLetter(event); // 404: asking again will never find it

That one matters: before this package, a station read both as null and could not tell them apart, while the cluster agent threw on both and retried a settled visit until the event died.

Everything else a floor refuses throws a FloorProblem carrying the RFC 9457 body — status, title, detail, and the per-field errors a 400 lists.

A filter the floor would 400 on does not compile. runs.list, events.feed and costs.summary each need at least one filter, and the types say so.

No runtime validation. The client casts, in one place. The rule, which the floor already followed and never wrote down: validate across a trust boundary you do not own — apps/api validates what a git credential provider answers, because that is somebody else's service — and annotate across one you do. The floor's own handlers are annotated with these same types, so drift is a compile error on the server rather than a throw in someone else's pod.

The live socket

runs.watch(runId, options) is an async iterable. It replays the run's journal from after, emits caught_up, then follows it live.

It comes back for you. The protocol was built for it — every frame but unsupported carries the seq to return with — so a watch reopens from the last seq it handed out, and misses nothing. It does not come back from a refusal: a bad cursor, a missing token, no such run, or too many viewers. That last one is deliberate. Sixteen viewers is a capacity signal, and a client that retried it invisibly would turn pressure on a busy run into a hidden hammer loop.

There is no idle timeout here on purpose. A caught-up quiet run legitimately says nothing for minutes; the floor's own ping already finds a dead viewer, and a client-side timer would only reconnect healthy watches.

A run a start by hand reopened goes on past its run_settled: run_reopened follows it, and the watch ends only at the settling the journal ends on.

One caveat for a relay: visit_opened and visit_reported carry the whole visit, agentSettings included — the model, the prompt, the image. Filter those before they reach a browser.

runs.watchFloor(options) follows the whole floor instead of one run: run_started and run_changed, each with a run's id and nothing else. It has no cursor. The floor opens every connection with resync, which means "read your list again", so a watch that drops comes back with nothing to remember. It is refused without the service token and comes back from everything else.

Testing against it

Both seams are injectable and neither needs a server: fetchFn for HTTP, socketFn for the live socket. With backoffMs: () => 0, the whole reconnect table is unit-testable in milliseconds — see live.test.ts.