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

@rebind.gg/client-ts

v0.1.1

Published

TypeScript client for the Rebind Remote Access WebSocket protocol. Typed, auto-reconnecting, zero runtime dependencies.

Downloads

206

Readme

@rebind.gg/client-ts

Elegant TypeScript client for the Rebind Remote Access WebSocket protocol. Typed, auto-reconnecting, zero runtime dependencies.

Works in Node 22+, Bun, Deno, and browsers using the native WebSocket API.

Rebind is a physical input-forwarding device that exposes a scripting SDK and remote-control surface. Learn more at rebind.gg. Built by US Input Company.

Install

npm install @rebind.gg/client-ts
# or
bun add @rebind.gg/client-ts
# or
pnpm add @rebind.gg/client-ts

Install the server

The client speaks the JSON-RPC protocol defined by a reference Lua script that runs inside Rebind. The script is bundled in this package at node_modules/@rebind.gg/client-ts/server/remote_access.lua so you don't need the Rebind source to use it.

  1. Locate your Rebind scripts directory:
    • Windows: %APPDATA%\Rebind\save_data\scripts\
    • macOS/Linux: ~/.config/Rebind/save_data/scripts/ (varies by platform)
  2. Copy server/remote_access.lua from the package into that directory.
  3. Open Rebind → Scripts → start Remote Access.
  4. The UI logs will show Remote Access server listening on ws://0.0.0.0:19561.

The script works out of the box with no auth. To require a token, edit AUTH_TOKEN at the top of the script before installing, and pass the same value to the client as { token: "..." }.

To extend the protocol with your own commands, copy the script, add entries to the handlers table, and point your client at the new port. The lua.exec command (disabled by default) provides an escape hatch for interactive debugging.

Quick start

import { RebindRemote } from "@rebind.gg/client-ts";

const r = new RebindRemote("ws://127.0.0.1:19561", { token: "" });
await r.connect();

// fire-and-forget HID writes
r.hidType("hello from typescript\n");
r.hidMove(100, 50);

// typed RPCs — fully autocompleted, AbortSignal-aware
const { x, y } = await r.systemMouse();
const { r: red, g, b } = await r.screenPixel(x, y);
const hex = [red, g, b].map((c) => c.toString(16).padStart(2, "0")).join("");
console.log(`pixel at (${x},${y}) = #${hex}`);

// async iteration — auto-subscribes on first read, auto-unsubscribes on break
for await (const { x, y } of r.mouseEvents()) {
  console.log(`mouse ${x},${y}`);
  if (x > 500) break;
}

r.close();

Features

  • Fully typed. Every protocol message has a real TypeScript interface. No unknown in the public API, full autocomplete.
  • Auto-reconnect. Transparent exponential backoff on unexpected disconnect. Re-subscribes to any active event streams automatically.
  • Async iterators for events. Modern, composable for await pattern. Auto-subscribes on first read, auto-unsubscribes when the iterator ends or is broken out of. Multiple concurrent iterators of the same stream share the subscription.
  • AbortSignal on every RPC. Standard Node/browser cancellation pattern.
  • Structured errors. Four error classes cover every failure mode: RebindError, ConnectionError, TimeoutError, ServerError.
  • Zero runtime dependencies. Native WebSocket everywhere.

Auto-reconnect

Enabled by default. Reconnects with exponential backoff (100 ms → 10 s) and re-subscribes to any active event streams. Tune or disable via options:

const r = new RebindRemote("ws://127.0.0.1:19561", {
  autoReconnect: true,              // default
  reconnectDelayMs: 100,            // initial delay
  reconnectMaxDelayMs: 10000,       // cap
  reconnectMaxAttempts: "infinite", // or a number
  onStateChange: (state) => console.log("state:", state),
});

Connection state is one of disconnected | connecting | connected | reconnecting and is available via r.state or the onStateChange callback.

Error handling

import {
  RebindRemote,
  RebindError,          // base class
  ConnectionError,      // can't connect, disconnected, closed
  TimeoutError,         // RPC didn't reply within timeoutMs
  ServerError,          // server returned { error: { code, message } }
} from "@rebind.gg/client-ts";

try {
  await r.screenPixel(9999, 9999);
} catch (e) {
  if (e instanceof ServerError && e.code === "screen_error") {
    // handle the specific server-side failure
  } else if (e instanceof TimeoutError) {
    // retry or escalate
  } else {
    throw e;
  }
}

Cancellation

Every RPC accepts an optional AbortSignal:

const ac = new AbortController();
setTimeout(() => ac.abort(), 100);

try {
  await r.clipboardGet(ac.signal);
} catch (e) {
  if ((e as Error).name === "AbortError") {
    // user cancelled
  }
}

Event streams

Three push events are exposed as async iterables:

for await (const { x, y } of r.mouseEvents()) { /* ... */ }
for await (const window of r.windowEvents()) { /* ... */ }
for await (const { keys, modifiers } of r.inputEvents()) { /* ... */ }

Each iterator call auto-subscribes on first read and auto-unsubscribes when the iterator ends. Multiple iterators of the same stream share the underlying subscription via refcounting — no duplicate server traffic.

Pass an AbortSignal to terminate iteration externally:

const ac = new AbortController();
// stop after 5 seconds
setTimeout(() => ac.abort(), 5000);

for await (const { x, y } of r.mouseEvents(ac.signal)) {
  console.log(x, y);
}

Full API

See TypeScript types for the full surface. Summary:

| Category | Methods | |---|---| | Lifecycle | connect(), close(), connected, state | | HID writes (fire-and-forget) | hidDown, hidUp, hidPress, hidType, hidMove, hidMoveTo, hidScroll | | Screen | screenPixel, screenResolution | | System | systemMouse, systemWindow, systemTime | | Input | inputKeys, inputIsDown, inputModifiers | | Clipboard | clipboardGet, clipboardSet | | Window | windowList, windowFind, windowActivate, windowMove | | Events | mouseEvents(), windowEvents(), inputEvents() | | Meta | ping, luaExec |

Performance

Measured on localhost (Windows host, release build):

| Metric | Value | |---|---| | RPC p50 | ~1 ms | | RPC p99 | ~2 ms | | Sustained RPC (16 in-flight) | ~10,000 req/s | | Fire-and-forget wire throughput | ~100,000 msg/s |

Development

bun install
bun test              # unit tests with in-memory mock server
bun run typecheck     # tsc --noEmit
bun run build         # emit dist/

Support

License

MIT © US Input Company