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

@3leaps/ipcprims

v0.2.5

Published

TypeScript bindings for ipcprims via Node-API

Readme

@3leaps/ipcprims

TypeScript bindings for ipcprims using Node-API (NAPI-RS).

Async Peer

Use AsyncPeer and AsyncListener when the process must keep its event loop live while waiting for IPC frames. The sync Peer.recv() and Peer.recvOn() APIs are still available for short scripts, but they block the JavaScript thread while waiting.

import { AsyncListener, AsyncPeer, COMMAND } from "@3leaps/ipcprims";

const listener = AsyncListener.bind("/tmp/ipcprims.sock", {
  channels: [COMMAND],
});

const accepted = listener.accept();
const client = await AsyncPeer.connect("/tmp/ipcprims.sock", [COMMAND]);
const server = await accepted;

const controller = new AbortController();
const pending = client.recvAsync({ signal: controller.signal });

await server.send(COMMAND, Buffer.from("hello"));
const frame = await pending;
console.log(frame.channel, frame.payload.toString());

client.close();
server.close();
await listener.close();

recvAsync() receives frames in arrival order. recvOnAsync(channel) and openChannel(channel) receive FIFO frames for that channel; they do not claim global ordering across channels.

The TypeScript async wrapper owns a small dispatcher above Rust AsyncPeer so concurrent JavaScript receive calls can wait independently. That dispatcher buffers at most 256 frames or 16 MiB while routing frames to waiters; Rust AsyncPeer transport, channel negotiation, CONTROL handling, schema validation, and bounded receive behavior still remain the underlying safety boundary.

const receiver = await client.openChannel(COMMAND);

for await (const frame of receiver) {
  console.log(frame.payload.toString());
}

Passing an already-aborted or later-aborted AbortSignal rejects the pending receive without closing the peer, so a later recvAsync() or recvOnAsync() can still consume future frames.

Auth Token Checks

Auth tokens are opaque bytes. Use connectWithAuth() on the client and takeClientAuthToken() on the accepted server peer. Treat an absent token, an empty token, a length mismatch, or a mismatched token as a handshake failure and close the peer before exposing it to application state.

import { timingSafeEqual } from "node:crypto";
import { AsyncListener, AsyncPeer, COMMAND } from "@3leaps/ipcprims";

declare function loadExpectedTokenBytes(): Buffer;

const expected = loadExpectedTokenBytes();

const listener = AsyncListener.bind("/tmp/ipcprims.sock", {
  channels: [COMMAND],
});
const accepted = listener.accept();
const client = await AsyncPeer.connectWithAuth(
  "/tmp/ipcprims.sock",
  [COMMAND],
  expected,
);
const server = await accepted;

const presented = server.takeClientAuthToken();
const ok =
  presented.present &&
  presented.token !== undefined &&
  presented.token.length > 0 &&
  presented.token.length === expected.length &&
  timingSafeEqual(presented.token, expected);

if (!ok) {
  server.close();
  client.close();
  await listener.close();
  throw new Error("handshake failed");
}

crypto.timingSafeEqual() throws on unequal lengths, so check the length first and reject cleanly. Do not use ==, string comparison, or Buffer.equals() for token authorization.

Schema Registry

Load a schema directory for standalone validation with SchemaRegistry. It uses the Rust registry's default configuration, so a channel without a schema passes without validation. TypeScript does not currently expose strict mode, missing-schema rejection, or programmatic schema registration.

import { COMMAND, SchemaRegistry } from "@3leaps/ipcprims";

const registry = SchemaRegistry.fromDirectory("/opt/example/schemas");
registry.validate(COMMAND, Buffer.from('{"id":7}'));
registry.close();

close() is safe to call more than once. Calling validate() after closing the registry throws because the native registry is no longer available.

Both Listener.bind() and AsyncListener.bind() accept schemaDir in their options. Each loads and attaches its own registry with the same default-only configuration:

import { AsyncListener, COMMAND, Listener } from "@3leaps/ipcprims";

const listener = Listener.bind("/tmp/ipcprims.sock", {
  channels: [COMMAND],
  schemaDir: "/opt/example/schemas",
});

const asyncListener = AsyncListener.bind("/tmp/ipcprims-async.sock", {
  channels: [COMMAND],
  schemaDir: "/opt/example/schemas",
});

The standalone SchemaRegistry cannot be attached to a TypeScript peer or listener. Use schemaDir when a TypeScript listener needs validation, and do not assume it rejects unregistered channels. See the repository's Schema Registry Guide for Rust configuration, directory rules, and platform-specific protections.