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

bufferfish

v0.2.0

Published

Binary message utility library for Rust and TypeScript interop.

Downloads

4

Readme

🐡 bufferfish

bufferfish is utility library for working with binary network messages between Rust and TypeScript, such as over WebSockets. It provides a simple API for encoding and decoding data into binary arrays, as well as generating TypeScript definitions and decoding functions from your Rust code.

See the project repository for more details, including how to interop with Rust.

This library has an unstable API and may be missing some basic functionality. I can't recommend using it in production, although I am using it for my own production project.

Table of Contents

Getting Started

Use cargo add bufferfish to add the Rust library to your project.

Use npm install bufferfish to add the TypeScript library to your project.

Examples

Writing to a Bufferfish

import { Bufferfish } from 'bufferfish'

const ws = new WebSocket("ws://127.0.0.1:3000")
ws.binaryType = "arraybuffer"

const bf = new Bufferfish()
bf.writeUint8(0)
bf.writeString("Hello, world!")

ws.send(bf.view())

Reading from a Bufferfish

import { Bufferfish } from 'bufferfish'

const ws = new WebSocket("ws://127.0.0.1:3000")
ws.binaryType = "arraybuffer"

onmessage = (event) => {
  const bf = new Bufferfish(event.data)
  const id = bf.readUint8()

    if (id === 0) {
        const message = bf.readString()

        console.log({ message }) // { message: "Hello, world!" }
    }
}

TypeScript Code Generation

bufferfish provides a generate function that can be used in build.rs (or used in a CLI script, called by server at launch, etc) to generate TypeScript definitions and functions from your Rust code, meaning your Rust server becomes the source of truth for all network messages, and reducing manually interacting with bufferfish on the client.

fn main() {
    bufferfish::generate("src/", "../client/src/generated/Packet.ts")?;
}
use bufferfish::Encode;

#[derive(Encode)]
#[repr(u16)]
pub enum PacketId {
    Join = 0,
    Leave,
    Unknown = 255,
}

impl From<PacketId> for u16 {
    fn from(id: PacketId) -> u16 {
        match id {
            PacketId::Join => 0,
            PacketId::Leave => 1,
            PacketId::Unknown => 255,
        }
    }
}

#[derive(Encode)]
#[bufferfish(PacketId::Join)]
pub struct JoinPacket {
    pub id: u8,
    pub username: String,
}

#[derive(Encode)]
#[bufferfish(PacketId::Leave)]
pub struct LeavePacket;
/* AUTOGENERATED BUFFERFISH FILE, DO NOT EDIT */
import { Bufferfish } from 'bufferfish';

export enum PacketId {
    Join = 0,
    Leave = 1,
    Unknown = 255,
}

export interface JoinPacket {
    id: number
    username: string
}

export const decodeJoinPacket = (bf: Bufferfish): JoinPacket => {
    return {
        id: bf.readUint8() as number
        username: bf.readString() as string
    }
}

Using Generated Decoding Functions

These are built when defining a struct or enum in Rust with the #[derive(Encode)] macro after calling the bufferfish::generate() function.

const ws = new WebSocket("ws://127.0.0.1:3000")
ws.binaryType = "arraybuffer"

ws.onmessage = (event) => {
  const bf = new Bufferfish(event.data)
  const packetId = bf.readUint16()

    if (packetId === PacketId.Join) {
        const packet = decodeJoinPacket(bf)

        console.log(packet) // { id: 1, username: "Rob" }
    }
}

Encodable Types

Supported Types | Decodes As --------------------------- | --------------------- u8 | number u16 | number u32 | number i8 | number i16 | number i32 | number bool | boolean String | string Vec<T> where T: Encodable | Array<T> T where T: Encodable | object or primitive

*The reverse is true for decoding.

Feature Flags (Rust)

Flag | Default | Description | Dependencies -------------- | -------- | --------------------------------------------------------------- | ------------------------------------------------ pretty-print | Disabled | Enables pretty-printing of buffer output. | unicode-width derive | Disabled | Enables the #[derive(Encode)] and #[derive(Decode)] macros. | syn, quote, proc-macro2, proc-macro-error |

Notes

  • I recommend using the num_enum crate for deriving IntoPrimitive and FromPrimitve on enums you wish to Encode. This removes a lot of boilerplate.
  • Enums in TypeScript are often mentioned as a "bad" feature, and this is generally true when considering typical web development use-cases. In the case of a list of "op codes" mapping to dev-friendly names, however, they are actually really useful. Modern bundlers - like esbuild - can actually inline them, meaning we just get integer literals in the final output..

Security

bufferfish functions ensure inputs are valid as a "best effort". Internal buffers are constructed with a maximum capacity (default of 1024 bytes), and will fail to construct if an input would cause the internal buffer to cross that threshold.

When reading data, you will always get the correct return type - however, you are still subject to corrupted data if the input was incorrect but technically valid. For example, if you call read_u8 on a buffer that contains a u16 at the cursor position, you will get a u8 back, as the buffer has no way to know that it was originally encoded as a u16. It is valid data, but will very likely be an unexpected value.

This kind of problem should be dealt with before operating on the buffer.

Decoding an oversized bufferfish via the Decode trait will just ignore / discard the additional data, as it is only going to read specific byte lengths generated by the Encodable impl.

Decoding an undersized bufferfish will return a BufferfishError::FailedWrite.

Contributing

bufferfish is open to contributions, however it should be noted that the library was created for my own game projects, and I am not interested in making it widely general-purpose. If you have a feature request or bug fix that you think would be useful to others, feel free to open an issue or PR either way.

License

bufferfish source code is dual-licensed under either

at your option.