JSON codec
How frames are serialised, what is validated, and why the wire is text.
Encode and decode
encode(frame) // → JSON.stringify(frame)
decode(raw) // → JSON.parse(raw), then check `op`That is the whole codec. decode requires the result to be an object with a
numeric op in the known set; anything else — malformed JSON, a missing op, an
opcode from a future version — raises a ProtocolError.
What is not validated
Everything else. There is no schema check beyond op, and extra fields survive
the typed cast rather than being stripped.
That is a deliberate trade. Frames are validated by the handler that acts on them, where the error can say something useful about the operation; a schema layer in between would duplicate those checks and answer with a message about shapes instead. It also means a client sending a field a newer server understands is not rejected by an older one.
Text only
The uWebSockets gateway ignores binary WebSocket frames. Only text JSON is accepted.
A client that sends a Buffer or an ArrayBuffer will see no error and no Ack:
the frame is dropped before anything looks at it. If a publish never resolves
and the connection is healthy, check what your WebSocket library sends by
default — several send binary for anything that is not a string.
Why JSON
The v1 drop-in path prefers debuggability over wire size. You can read a Blackevin
session in devtools, paste a frame into curl, and diff two of them by eye.
Against the messages most applications actually send, the difference against a
packed binary format is smaller than it looks.
The codec is isolated behind encode / decode precisely so a binary format
can be added later without any of the fanout, routing or storage code learning
about it.
Writing your own client
The wire contract is:
- open a WebSocket to the endpoint with the credential in the query string
- wait for
Connected(op 12) and keep theconnectionId - send text JSON frames; put an
idon anything you want acknowledged - match
Ack(9) andNack(10) back to your requests by thatid - answer
Heartbeat(11) with aHeartbeat
Opcodes has the frame shapes and the Nack codes.
One thing that catches people: Message frames are not correlated to anything
you sent, so they arrive with no id. Do not route inbound frames purely by
id — switch on op first.