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

@remotex-labs/xstruct

v3.0.1

Published

This library provides a simple way to define and serialize data structures (structs) with support for both regular fields and bitfields

Readme

xStruct

Documentation npm version downloads License: MPL 2.0 Test CI Discord Ask DeepWiki

A TypeScript library for defining, serializing, and deserializing binary data structures. Describe a layout once with a plain object and short field strings such as u32le, f64be, or utf8[32], then convert between JavaScript objects and Buffer with full type safety.

It is designed for binary file formats, network protocols, and low-level data manipulation, with support for primitive types, bitfields, fixed and pointer arrays, nested structs, unions, and variable-length data stored on a heap.

Key Features

  • Declarative schemas: describe a layout with a plain object and concise field strings.
  • Type-safe: parameterize a struct with an interface and let TypeScript check what you encode and decode.
  • Rich numeric types: 8/16/32/64-bit integers (64-bit as bigint) and 32/64-bit floats, each with explicit endianness.
  • Strings and arrays: fixed-size strings, multi-dimensional arrays, and variable-length text on the heap.
  • Heap pointers: variable-length fields live behind a pointer, with a configurable 1, 2, 4, 6, or 8-byte pointer size.
  • Bitfields and unions: pack sub-byte fields into a shared container and overlap members at offset 0.

Installation

npm install @remotex-labs/xstruct
# or
pnpm add @remotex-labs/xstruct
# or
yarn add @remotex-labs/xstruct

xStruct requires Node.js 22 or later and has no runtime dependencies.

Quick start

import { Struct } from '@remotex-labs/xstruct';

interface Header {
    magic: number;
    version: number;
    name: string;
}

const header = new Struct<Header>({
    magic: 'u32be',   // unsigned 32-bit, big-endian
    version: 'u16le', // unsigned 16-bit, little-endian
    name: '*utf8'     // pointer to a heap UTF-8 string
});

const buffer = header.toBuffer({ magic: 0xCAFEBABE, version: 1, name: 'demo' });
const data = header.toObject(buffer);
// { magic: 3405691582, version: 1, name: 'demo' }

Pass an interface as the type parameter (new Struct<Header>(...)) and TypeScript checks the objects you pass to toBuffer and the shape returned by toObject.

Field strings

Every field is described by a short expression string. Fields are laid out in declaration order with no alignment padding.

| Form | Example | Meaning | |---------------|--------------|----------------------------------------| | Primitive | 'u32le' | A single integer or float | | String | 'utf8[16]' | A fixed-size string of N bytes | | Array | 'u8[4]' | A fixed array, repeats as 'u8[4][2]' | | Bitfield | 'u8:4' | A sub-byte field in a container | | Pointer | '*utf8' | A heap pointer to variable-length data | | Nested struct | a Struct | An embedded struct or union |

Core types

Integers

A name is u/i (unsigned/signed) + width + endianness (le/be, omitted for 8-bit).

| Type | Bytes | JS type | Range | |------------------|-------|----------|---------------------------| | u8 / i8 | 1 | number | 0..255 / -128..127 | | u16le, u16be | 2 | number | 0..65535 | | i16le, i16be | 2 | number | -32768..32767 | | u32le, u32be | 4 | number | 0..4294967295 | | i32le, i32be | 4 | number | -2147483648..2147483647 | | u64le, u64be | 8 | bigint | 0..2^64-1 | | i64le, i64be | 8 | bigint | -2^63..2^63-1 |

64-bit fields read and write bigint; passing a number throws at encode time. Out-of-range values are not clamped and throw from the buffer layer.

Floats

f32le / f32be (32-bit) and f64le / f64be (64-bit). All read and write number.

Strings

Encodings: utf8, ascii, latin1, utf16le.

new Struct({
    code: 'ascii[4]', // fixed 4 bytes, inline on the stack (zero-padded / truncated)
    name: '*utf8'     // variable-length, stored on the heap behind a pointer
});

A fixed encoding[N] size is a number of bytes, not characters. Variable-length (*) strings have no fixed size and never truncate; their payload is sized by its encoded byte length, so multi-byte UTF-8/UTF-16 round-trips intact.

Bitfields

type:bits packs a sub-byte integer into the named container. Allowed containers are u8/i8 (8 bits), u16le/be & i16le/be (16 bits), and u32le/be & i32le/be (32 bits). 64-bit containers are not allowed.

const reg = new Struct({
    flags: 'u8:4',
    mode: 'u8:2',
    extra: 'u8:2' // all three share one byte
});

Consecutive bitfields pack into the same container until the bit count would overflow it, the type/size changes, or a regular field closes it.

Arrays

Append [N] for a fixed inline array. A fixed string takes two dimensions encoding[bytes][count]. Prefix with * for an array of pointers, each addressing its own heap payload.

new Struct<{ ints: number[]; codes: string[]; tags: string[] }>({
    ints: 'u16le[4]',    // 4 inline u16le values
    codes: 'ascii[4][2]', // 2 strings, 4 bytes each, inline
    tags: '*utf8[3]'     // 3 pointer slots, each to a heap string
});

Grouped pointers

Parentheses group a fixed inner dimension behind a pointer, so *(T[N]) is one pointer to a heap list of T[N] groups. Add an outer [M] for M such pointers. Pass the values inside an array and read the same array back; a single-element list reads back as the lone element (the usual pointer collapse).

const s = new Struct<{ rows: string[]; matrix: number[][][] }>({
    rows: '*(utf8[4])',     // 1 pointer → heap list of 4-byte strings
    matrix: '*(u32le[4])[2]' // 2 pointers, each → heap list of u32le[4] groups
});

s.toObject(s.toBuffer({
    rows: [ 'ABCD', 'EFGH' ],
    matrix: [ [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ] ], [ [ 9, 8, 7, 6 ] ] ]
}));
// rows:   [ 'ABCD', 'EFGH' ]
// matrix: [ [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ] ], [ 9, 8, 7, 6 ] ]  // last slot's 1-element list collapsed

Nested structs

Compose structs to any depth by using a Struct as a field value.

const Point = new Struct<{ x: number; y: number }>({ x: 'i32le', y: 'i32le' });

const Shape = new Struct<{ kind: number; origin: { x: number; y: number } }>({
    kind: 'u8',
    origin: Point // embedded struct, shares the parent's heap
});

Unions

Union lays every member at offset 0 and sizes itself to the widest member. It extends Struct, shares the same API, and (unlike fixed-width unions) accepts pointer members because a pointer is a fixed-size slot.

import { Union } from '@remotex-labs/xstruct';

const value = new Union<{ word: number; text: string }>({
    word: 'u32le',
    text: '*utf8' // a heap pointer is a valid union member
});

value.size; // 4, the widest member

Heap & pointers

A struct has a fixed size (the stack). Pointer fields (*) reserve one pointerSize-byte slot there and write their payload to a heap region appended after the struct, so a serialized buffer can be larger than size.

const s = new Struct<{ id: number; name: string }>({ id: 'u32le', name: '*utf8' });
s.size; // 8  (4 for id + 4 for the pointer)
const buf = s.toBuffer({ id: 1, name: 'Ada Lovelace' });
buf.byteLength; // > 8: the trailing bytes are the heap

A short string such as 'Ada' may fit entirely in the spare bits of the pointer, in which case no heap bytes are added and the buffer stays exactly size long. The decoded value is identical either way.

Options

new Struct<T>(definition, options?):

| Option | Default | Description | |---------------|---------|----------------------------------------------------------------------------------| | pointerSize | 4 | Pointer width in bytes: 1, 2, 4, 6, or 8. Bounds the heap size. | | inherit | true | When nested, adopt the parent's pointer size and heap. false stays standalone. | | heap | - | A heap to write into instead of a per-call one. |

A custom heap is any object with read/write methods. The package exports heapRead / heapWrite, which you bind to a { top, buffer } context:

import { Struct, heapRead, heapWrite } from '@remotex-labs/xstruct';

const ctx = { top: 0, buffer: Buffer.allocUnsafe(0) };
const heap = { read: heapRead.bind(ctx), write: heapWrite.bind(ctx) };

const a = new Struct<{ v: string }>({ v: '*utf8' }, { heap, inherit: false });
a.toBuffer({ v: 'alpha' }); // 'alpha' written into ctx.buffer

Error handling

Invalid schemas throw when the Struct is constructed, so mistakes surface before the first serialize.

const s = new Struct({ id: 'u32le' });

s.toObject(Buffer.alloc(2)); // throws: buffer smaller than the struct size
s.toBuffer(null as never);   // throws: data is not an object
new Struct({ x: 'u9le' });   // throws at construction: unknown type

Documentation

Full guides and the complete type reference live at remotex-labs.github.io/xStruct.

Contributing

Contributions are welcome!
Please see our Contributing Guide for details.

Links

Documentation, GitHub Repository, Issue Tracker, npm Package

License

This project is licensed under the Mozilla Public License 2.0 - see the LICENSE file for details.