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

@wingfoil/client

v4.3.3

Published

Browser client for the wingfoil web adapter. Thin TypeScript wrapper around the wingfoil-wasm decoder with reactive framework bindings.

Downloads

805

Readme

@wingfoil/client

TypeScript / JavaScript client for the wingfoil web adapter. Wraps the wingfoil-wasm decoder and exposes a small framework-agnostic WingfoilClient plus optional reactive-framework adapters for Solid.js, Svelte, and Vue 3.

The Rust server is the single source of truth for the wire format — the browser imports a Rust-compiled-to-wasm codec instead of maintaining hand-written TypeScript schemas.

Install

Not yet published. During development, point your app at the local package (see vite.config.ts for the alias pattern).

// package.json
{ "dependencies": { "@wingfoil/client": "^4.0.1" } }

Quick start

import { WingfoilClient } from "@wingfoil/client";

const client = new WingfoilClient({
  url: "ws://localhost:8080/ws",   // bincode is the default
});

client.subscribe("price", (value, timeNs) => {
  console.log(timeNs, value);
});

// Send a UI event back to the graph:
client.publish("ui", { kind: "click", note: "hi" });

Latency tracing

For UIs that drive a wingfoil server using the Traced<T, L> / latency_stages! pattern, @wingfoil/client/tracing provides a LatencyTracker that owns the per-tab session UUID, stamps outbound requests with client_seq + t_client_send, filters inbound responses to the current session, and (optionally) echoes the round-trip back so the server can compute rtt_total / wire_rtt within a single clock domain. The listener receives the four deltas pre-computed.

import { WingfoilClient } from "@wingfoil/client";
import { LatencyTracker } from "@wingfoil/client/tracing";

const client = new WingfoilClient({ url: "ws://localhost:8080/ws", codec: "json" });
const tracker = new LatencyTracker({
  client,
  outbound: "orders",
  inbound:  "fills",
  echo:     "latency_echo",   // omit to disable the echo leg
});

tracker.onResponse<FillFrame>(({ payload, rttNs, serverResidentNs, wireRttNs, stamps }) => {
  console.log(payload.client_seq, rttNs, serverResidentNs, wireRttNs);
});

// session, client_seq, and t_client_send are stamped by the tracker.
tracker.send({ side: 0, qty: 1 });

The default field names match the wingfoil convention (session, client_seq, t_client_send, t_client_recv, stamps) and can be overridden via LatencyTrackerOptions.fields (the same map applies to both outbound publishes and inbound parsing). The end-to-end latency demo at wingfoil/examples/latency_e2e/static/app.js is the canonical example.

Requires the server to use CodecKind::Json: the tracker sends session as a JS number[], which the JSON codec round-trips as [u8; 16] but the bincode codec encodes as a length-prefixed Vec<u8>.

The main package also re-exports the small browser helpers the tracker relies on, in case you need them directly: newSessionId, sessionHex, nowNs.

Reactive-framework bindings

Solid.js

import { useTopic, usePublisher } from "@wingfoil/client/solid";

function LivePrice({ client }) {
  const price = useTopic<PriceTick>(client, "price");
  const sendClick = usePublisher(client, "ui");
  return (
    <div>
      {price()?.mid.toFixed(4)}
      <button onClick={() => sendClick({ kind: "click", note: "" })}>go</button>
    </div>
  );
}

Solid's fine-grained signals are the recommended default for kHz+ streams — signal writes are cheap and paints coalesce to rAF, so high-frequency data drives UI without per-frame DOM thrash.

Svelte

<script lang="ts">
  import { topic, publisher } from "@wingfoil/client/svelte";
  const price = topic<PriceTick>(client, "price");
  const send = publisher(client, "ui");
</script>
{#if $price}<div>{$price.mid.toFixed(4)}</div>{/if}

Vue 3

<script setup lang="ts">
import { useTopic, usePublisher } from "@wingfoil/client/vue";
const price = useTopic<PriceTick>(client, "price");
const send = usePublisher(client, "ui");
</script>
<template><div>{{ price?.mid.toFixed(4) }}</div></template>

Not included

Generic React bindings are intentionally not shipped as a first-class target. React re-renders at kHz without manual batching will tank frame-rate — use Solid or Svelte instead, or implement a React adapter with useSyncExternalStore coalesced to requestAnimationFrame if you need React.

Development

From wingfoil-js/:

pnpm install
pnpm run build:wasm   # wasm-pack → ./src/wasm
pnpm build            # build:wasm + tsc + copy ./src/wasm to ./dist/wasm
pnpm dev              # Vite dev server for examples/solid-dashboard
pnpm run lint         # tsc --noEmit

Codec round-trip coverage lives in the Rust unit tests of wingfoil-wasm (run with cargo test in that crate) and in wasm-pack test for browser-target coverage.

Start the Rust example in another terminal:

cargo run --example web --features web

Then open http://localhost:5173 — the Solid dashboard connects to ws://127.0.0.1:8080/ws by default.

Wire format

Every WebSocket frame is binary — either a bincode-serialized Envelope (default) or a JSON one (if the server was started with .codec(CodecKind::Json)). The wingfoil-wasm decoder handles both without any user configuration other than the codec hint passed to WingfoilClient.