jigsaw-protocol
v0.1.1
Published
JavaScript/TypeScript client for the Jigsaw Protocol v1.0.0
Maintainers
Readme
Jigsaw Protocol JS
JavaScript/TypeScript bindings for the Jigsaw Protocol v1.0.0.
Jigsaw is a schema-negotiation protocol that replaces JSON with compact MessagePack payloads once both peers have agreed on a schema. This library wraps jigsaw-rust via:
- Node.js – native addon built with napi-rs
- Browser – WebAssembly module built with wasm-bindgen / wasm-pack
Installation
npm install jigsaw-protocolPre-built binaries for common platforms are included in the npm package. Building from source requires a Rust toolchain (see Building from source).
Quick Start
Node.js
import { Session } from "jigsaw-protocol";
// Two in-process sessions wired together for illustration.
// In production, wire the Uint8Array frames over WebSocket / TCP / etc.
const alice = new Session(nativeHandle(true)); // initiator
const bob = new Session(nativeHandle(false)); // responder
// Alice starts the handshake.
let frames = alice.start();
// Shuttle frames until steady state.
while (frames.length) {
const replies: Uint8Array[] = [];
for (const frame of frames) {
const r = bob.receive(frame);
replies.push(...r);
}
frames = [];
for (const frame of replies) {
const r = alice.receive(frame);
frames.push(...r);
}
}
// Both peers are now in S3_STEADY. Alice sends an object.
const outFrames = alice.sendObject({ userId: 42, name: "Alice" });
// Bob receives it:
for (const frame of outFrames) {
bob.receive(frame);
}The nativeHandle helper above is a thin wrapper over the compiled native addon. In a typical application you would import it directly from the built artifact:
// Node.js native addon (built by `npm run build:native`)
const { JsSession } = require("./jigsaw-protocol-native.node");
function nativeHandle(initiator: boolean) {
return new JsSession(initiator);
}Browser (WASM)
<script type="module">
import init, { WasmSession } from "./dist/wasm/jigsaw_protocol_wasm.js";
await init();
const alice = new WasmSession(true);
const bob = new WasmSession(false);
let frames = alice.start();
while (frames.length) {
const replies = [];
for (const frame of frames) replies.push(...bob.receive(frame));
frames = [];
for (const frame of replies) frames.push(...alice.receive(frame));
}
alice.send_object(JSON.stringify({ event: "click", x: 100, y: 200 }));
</script>API Reference
Session (TypeScript wrapper)
The Session class in js/src/session.ts wraps a native or WASM session handle and provides a typed interface.
new Session(handle: NativeSessionHandle)| Method / Property | Signature | Description |
| ----------------- | ------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------- |
| start() | () => Uint8Array[] | Begin the session. Returns framed wire messages to send. Call once before anything else. |
| receive(data) | (data: Uint8Array) => Uint8Array[] | Feed one framed wire message received from the peer. Returns zero or more messages to send in reply. |
| sendObject(obj) | (obj: Record<string, unknown>) => Uint8Array[] | Send a JSON object. Automatically negotiates the schema. Only valid in S3_STEADY or S2_SCHEMA_SYNC. |
| pollTimeouts() | () => Uint8Array[] | Check for expired protocol timers. Call periodically (e.g. every 100 ms). Returns messages to send if a timeout fired. |
| state | SessionState (getter) | Current state string: "S0_INIT" … "S6_CLOSED". |
| isClosed | boolean (getter) | true when the session is in the terminal S6_CLOSED state. |
SessionState
type SessionState =
| "S0_INIT" // not yet started
| "S1_HANDSHAKE" // HELLO / HELLO_ACK exchange
| "S2_SCHEMA_SYNC" // schema negotiation
| "S3_STEADY" // full-speed binary payload exchange
| "S4_RESYNC" // schema mismatch recovery
| "S5_FALLBACK_JSON" // temporary plain-JSON mode
| "S6_CLOSED"; // terminalWire framing
Every Uint8Array returned by the library or passed to receive() is a length-prefixed frame:
[ 4-byte big-endian uint32 length ][ payload bytes ]You must transmit and receive frames atomically (e.g. over a WebSocket message or a framed TCP stream).
JsSchema.computeId (Node.js native)
JsSchema.computeId(fields: string[], refs?: boolean[], refIsArray?: boolean[]): bigintComputes the xxHash64 schema identifier for a given field list. Useful for debugging or pre-computing IDs.
WasmSchema.compute_id (WASM)
WasmSchema.compute_id(fields: string[], refs?: boolean[], refIsArray?: boolean[]): bigintSame as above, callable from browser WASM.
Building from Source
Prerequisites
- Rust (stable toolchain)
- Node.js 18 or later
- wasm-pack (for WASM target)
- @napi-rs/cli (installed via
npm install)
# Install Node dependencies (includes @napi-rs/cli)
npm install
# Build the Node.js native addon
npm run build:native
# Build the WASM bundle
npm run build:wasm
# Compile TypeScript
npm run build:ts
# Build everything at once
npm run buildOutput layout
dist/
├── node/ # CommonJS output (for Node.js require)
├── esm/ # ES module output (for bundlers / browsers)
└── wasm/ # wasm-pack output (jigsaw_protocol_wasm_bg.wasm + JS glue)License
MIT
