Binary wire format for Procwire data plane (zero JSON overhead).
npm install @procwire/protocolBinary wire format for Procwire data plane - zero JSON overhead.
- 11-byte binary header - minimal overhead
- ~80x performance vs JSON-RPC on data plane
- ~2.5 GB/s throughput on named pipes
- Zero runtime dependencies
- Backpressure support via DrainWaiter singleton
Procwire uses a dual-channel architecture:
| Channel | Transport | Format | Purpose |
|---------|-----------|--------|---------|
| Control Plane | stdio | JSON-RPC 2.0 | Handshake, heartbeat, lifecycle |
| Data Plane | Named Pipe | BINARY | User data, high throughput |
This package provides the binary protocol for the Data Plane.
``
┌──────────┬───────┬──────────┬──────────┬──────────────────────┐
│ Method ID│ Flags │ Req ID │ Length │ Payload │
│ 2 bytes │ 1 byte│ 4 bytes │ 4 bytes │ N bytes │
│ uint16 BE│ │ uint32 BE│ uint32 BE│ (codec output) │
└──────────┴───────┴──────────┴──────────┴──────────────────────┘
Header: 11 bytes (fixed)
Payload: N bytes (variable, serialized by codec)
`
| Bit | Flag | Values |
|-----|------|--------|
| 0 | DIRECTION_TO_PARENT | 0 = to child, 1 = to parent |IS_RESPONSE
| 1 | | 0 = request/event, 1 = response |IS_ERROR
| 2 | | 0 = ok, 1 = error response |IS_STREAM
| 3 | | 0 = single message, 1 = stream chunk |STREAM_END
| 4 | | 0 = more coming, 1 = final chunk |IS_ACK
| 5 | | 0 = full response, 1 = ack only |
| 6-7 | Reserved | Must be 0 |
`bash`
npm install @procwire/protocol
Requirements: Node.js >= 22
`typescript
import {
encodeHeader,
decodeHeader,
createFlags,
hasFlag,
Flags,
HEADER_SIZE,
} from "@procwire/protocol";
// Encode a request header
const header = encodeHeader({
methodId: 1,
flags: createFlags({ toParent: false }),
requestId: 42,
payloadLength: 1024,
});
// Decode received header
const decoded = decodeHeader(buffer);
if (hasFlag(decoded.flags, Flags.IS_STREAM)) {
// handle stream chunk
}
`
| Constant | Value | Description |
|----------|-------|-------------|
| HEADER_SIZE | 11 | Header size in bytes |DEFAULT_MAX_PAYLOAD_SIZE
| | 1 GB | Default max payload |ABSOLUTE_MAX_PAYLOAD_SIZE
| | ~2 GB | Node.js Buffer limit |ABORT_METHOD_ID
| | 0xFFFF | Reserved for cancellation |HEADER_POOL_SIZE
| | 16 | Ring buffer pool size |
#### encodeHeader(header: FrameHeader): Buffer
Encode a frame header into an 11-byte buffer.
#### encodeHeaderInto(buffer: Buffer, header: FrameHeader): void
Encode into an existing buffer (zero allocation for high-throughput).
#### decodeHeader(buffer: Buffer): FrameHeader
Decode an 11-byte buffer into a FrameHeader object.
#### createFlags(options): number
Build flags byte from options object.
`typescript`
const flags = createFlags({
toParent: true,
isResponse: true,
isStream: true,
streamEnd: false,
});
#### hasFlag(flags: number, flag: number): boolean
Check if a specific flag is set.
#### validateHeader(header: FrameHeader, maxPayloadSize?: number): void
Validate header values. Throws on invalid values.
#### FrameBuffer
Accumulates incoming TCP data into complete frames.
- Batch mode (default): Returns Frame[] when complete frames are available
- Streaming mode: Delivers payload chunks via callbacks for large payloads
`typescript
const frameBuffer = new FrameBuffer({ maxPayloadSize: 100 1024 1024 });
socket.on("data", (chunk) => {
const frames = frameBuffer.push(chunk);
for (const frame of frames) {
handleFrame(frame);
}
});
`
#### Frame
Parsed frame with header and payload.
`typescript`
interface Frame {
header: FrameHeader;
payloadChunks: readonly Buffer[]; // Zero-copy chunks
payload: Buffer; // Merged (may allocate)
payloadLength: number;
}
#### DrainWaiter
Singleton for socket backpressure management. Prevents MaxListenersExceededWarning when multiple concurrent requests wait for drain.
`typescript
const drainWaiter = new DrainWaiter(socket);
if (socket.writableNeedDrain) {
await drainWaiter.waitForDrain();
}
socket.write(data);
`
#### buildFrame(header: FrameHeader, payload: Buffer): Buffer
Build a single buffer containing header + payload.
#### buildFrameBuffers(header: FrameHeader, payload: Buffer): [Buffer, Buffer]
Build separate header and payload buffers for socket.cork() / writev()`.
MIT