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

acos-json-encoder

v2.0.15

Published

JSON serializer and deserializer used by ACOS.games

Downloads

2,231

Readme

ACOS JSON Encoder for Websocket Networking

Efficiently encode JSON objects into compact binary for network transmission. Includes a string dictionary, typed protocol schemas, array delta compression, and hidden-field extraction.

Installation

npm i acos-json-encoder

Usage

CommonJS (nodejs)

const { encode, decode, createDefaultDict } = require("acos-json-encoder");

ES6 (webpack, vite, etc)

import { encode, decode, createDefaultDict } from "acos-json-encoder";

Development

npm install
npm run build
npm test

TypeScript source files live in src/ and compile to dist/.


Modules

  • Encoder — generic JSON binary encoding with dictionary compression
  • Protocol — typed schema-based binary encoding for structured messages
  • Delta — compute and apply minimal diffs between two values

Encoder

The encoder serializes any JSON value to a compact binary format. A string dictionary reduces repeating keys and values to 2-byte references.

import { encode, decode, createDefaultDict } from "acos-json-encoder";

const dict = ["name", "rank", "score", "teamid"];
createDefaultDict(dict);

const data = { name: "Alice", rank: 1, score: 4200, teamid: "team_o" };
const bytes = encode(data);
const decoded = decode(bytes);

console.log(JSON.stringify(data) === JSON.stringify(decoded)); // true
console.log("JSON size:", JSON.stringify(data).length);         // e.g. 51
console.log("Encoded size:", bytes.byteLength);                 // e.g. 14

API

createDefaultDict(strings[])

Set the global default dictionary. Strings in the list are encoded as 2-byte references.

encode(json, [dictionary])

Encode a JSON value to ArrayBuffer. Uses the default dictionary if none is provided.

decode(bytes, [dictionary])

Decode an ArrayBuffer back to a JSON value.


Protocol

The protocol system encodes typed, schema-defined messages. Schemas are registered once; each message is prefixed with a 1-byte type index, allowing the decoder to reconstruct the value without any schema in the wire payload.

Defining a schema

import {
  registerProtocol, protoEncode, protoDecode,
  setDefaultDictionary, getProtocolSchema
} from "acos-json-encoder";

const Player = {
  id: "uint",
  displayname: "string",
  score: "uint",
  // $custom marks a field as replaceable via extendProtocol
  stats: { "$custom": "any" }
};

const PROTOCOL = {
  type: "gameupdate",
  payload: {
    room: {
      sequence: "uint",
      timeend:  "uint",
      status:   { "$enum": ["waiting", "active", "finished"] }
    },
    players: { "$static": Player },
    // $custom fields can be replaced at runtime
    state: { "$custom": "any" }
  }
};

setDefaultDictionary(["sequence", "timeend", "status", "players", "state"]);
registerProtocol(PROTOCOL);

Encoding and decoding

const message = {
  type: "gameupdate",
  payload: {
    room: { sequence: 12, timeend: 1716346342382, status: "active" },
    players: [
      { id: 1, displayname: "Alice", score: 100, stats: {} },
      { id: 2, displayname: "Bob",   score: 80,  stats: {} },
    ],
    state: { turn: 1 }
  }
};

const encoded = protoEncode(message);
const decoded = protoDecode(encoded);
// decoded.type    → "gameupdate"
// decoded.payload → { room: {...}, players: [...], state: {...} }

Extending a protocol at runtime

extendProtocol adds new fields or replaces $custom-marked fields. Existing non-custom fields are never replaced. Recurses through $static/$array element schemas automatically.

import { extendProtocol, revertProtocol } from "acos-json-encoder";

extendProtocol("gameupdate", {
  // Replace the $custom state field with a typed schema
  state: {
    cells: { "$static": { "$enum": ["", "X", "O"] } }
  },
  // Recurse into players/$static/Player to extend its $custom stats field
  players: {
    stats: { wins: "uint", losses: "uint" }
  }
});

// Inspect the live schema
console.log(getProtocolSchema("gameupdate"));

// Restore the original schema (resets all $custom fields)
revertProtocol("gameupdate");

Fallback encoding

Messages with an unregistered type are encoded using the generic encoder with the default dictionary. Set a default dictionary to ensure the fallback path decodes correctly.

setDefaultDictionary(["id", "type", "payload"]);

// Unregistered protocol — falls back to generic encoding
const bytes = protoEncode({ type: "ping", payload: { id: 1 } });
const msg   = protoDecode(bytes); // { type: "ping", payload: { id: 1 } }

Schema types

| Schema value | Meaning | |---|---| | "uint" | Unsigned integer (LEB128) | | "int" | Signed integer (SLEB128) | | "float" | 64-bit float | | "string" | UTF-8 string with optional dictionary compression | | "boolean" | 1 byte | | "any" | Generic fallback encoding | | "object" | Opaque object (generic encoding) | | { "$object": {...} } | Known-key object with bitflag presence encoding | | { "$map": {...} } | String-keyed map with typed values | | { "$array": schema } | Variable-length array | | { "$static": schema } | Fixed-schema array supporting delta ops | | { "$enum": [v, ...] } | One of a fixed list of string/number values (1 byte index) | | { "$custom": schema } | Replaceable field; default schema used until extendProtocol overrides it |

Protocol API

registerProtocol(definition, [dictionary])

Register a protocol. definition.type (string) is the message type name. definition.payload is the schema.

extendProtocol(type, overrides)

Deep-merge overrides into the live schema. Only $custom fields can be replaced; non-custom fields are skipped. $static/$array element schemas are traversed automatically.

revertProtocol(type)

Reset the protocol schema back to its state at registration time.

setDefaultDictionary(strings[])

Set the dictionary used by the fallback (unregistered type) encode/decode path.

getProtocolSchema(type)

Returns a plain-object description of the current compiled schema.

protoEncode(message)

Encode { type, payload } to ArrayBuffer.

protoDecode(buffer)

Decode an ArrayBuffer to { type, payload }.


Delta

Compute minimal diffs between two values and apply them. Designed to pair with $array / $static schemas in the protocol encoder.

import { delta, merge } from "acos-json-encoder";

const prev = ["X", "", "O", "", "", "", "", "", ""];
const next = ["X", "", "O", "X", "", "", "", "", ""];

const changes = delta(prev, next);
// → [{ op: "set", index: 3, value: "X" }]

const restored = merge(prev, changes);
// → ["X","","O","X","","","","",""]

Object deltas work the same way:

const a = { score: 10, name: "Alice" };
const b = { score: 15, name: "Alice" };

const d = delta(a, b);  // → { score: 15 }
const r = merge(a, d);  // → { score: 15, name: "Alice" }

Hidden fields

hidden extracts all _-prefixed keys from an object (mutating it in place) and returns the extracted subtree. Useful for stripping server-only data before broadcasting to clients.

import { hidden, unhidden } from "acos-json-encoder";

const state = {
  turn: 1,
  _secret: "xyz",
  players: [{ id: 1, _hand: ["card1", "card2"] }, { id: 2 }]
};

const priv = hidden(state);
// priv  → { _secret: "xyz", players: [{ _hand: ["card1","card2"] }, null] }
// state → { turn: 1, players: [{ id: 1 }, { id: 2 }] }

// Merge private data back in for a specific player
const playerView = unhidden(state, priv, 0);

Delta API

delta(from, to)

Returns the minimal delta between two values, or undefined if equal.

merge(from, delta)

Apply a delta returned by delta() and return the patched value. Does not mutate from.

hidden(obj)

Extracts _-prefixed keys from obj (mutated in place). Returns the hidden subtree or undefined.

unhidden(obj, hiddenData, [index])

Merges hidden data back into an object or a specific array element.