Skip to content

Latest commit

 

History

History
119 lines (93 loc) · 5.48 KB

live-protocol.md

File metadata and controls

119 lines (93 loc) · 5.48 KB

The live wire protocol

What travels between a live page and the server: the anchor's attributes, the frames in both directions, the patch kinds, the close codes and the limits. Component, Live, Topic and Async are in the API.

The anchor

Live.mount returns two nodes: the anchor <div> holding the first render, and <script src="/eezo/live.js" defer>. The anchor's attributes:

attribute written by value
data-eezo-page the server, at mount the page id, 32 hex characters
data-eezo-base the server, at mount /, rewritten by a mount to its prefix
data-eezo-state the client connecting, connected, lost
data-eezo-dead the server, at mount capacity, when the registry was full and this is a static render

The client drives the first [data-eezo-page] it finds and no other.

Event bindings

attribute sent on payload
data-eezo-click click, default prevented {}
data-eezo-input input, after data-eezo-debounce ms of quiet, 300 by default {"value": ...}
data-eezo-change change {"value": ...}
data-eezo-submit submit, default prevented every named field, {name: value, ...}

The value of a checkbox or radio is on when ticked and empty otherwise; anything else sends its value. A submit payload follows FormData: ticked checkboxes as on, the selected option's value, files skipped. Listeners are delegated at the document, so a binding survives any patch that replaces its element.

data-eezo-ignore on an element keeps the differ out of its children. data-eezo-key on every child of a list makes the list keyed.

Client to server

kind fields when
join base first, after the socket opens
event name, payload a binding fired
ping every 30 seconds
failed reasons the applier refused patches

base is validated to /[A-Za-z0-9_./-]*, at most 200 characters, and normalised; anything else is ignored and logged. An event's name is a non-empty string, its payload an object of at most 64 string entries. Up to 8 reasons of up to 500 characters each are kept. A frame that fails any of this is answered with an error frame and the page carries on.

Server to client

kind fields
patches patches, a list in application order
error message
pong

A resync is a patches frame with one setChildren whose path is [] and expect is null: the anchor's whole content replaced. The server sends one on every join, on a mailbox overflow, and after a failed report.

Patch kinds

Every patch has op and path, a list of child indices from the anchor, so the component's root is [0]. expect is the node name the server believes is at the path, #text for text, and the client refuses a patch whose target disagrees.

op fields effect
setText text sets a text node's content; text is unescaped
setAttr expect, name, value sets one attribute; a bare attribute is the empty string
removeAttr expect, name removes one attribute
replaceNode expect, html replaces the node at the path
removeNode expect removes the node at the path
appendChildren expect, html appends after the last child
insertChild expect, index, html inserts at index, shifting what follows
moveChild expect, from, to removes child from, inserts it at to in the shortened list
setChildren expect, html replaces every child

Within one positional list a frame holds in-place updates, then removals highest index first, then one append. Within one keyed list: removals highest index first, then moves, then inserts ascending, then content patches at final positions.

Close codes

code sent by meaning the client then
1000 the client beforeunload nothing; the server frees the page at once
1001 the server the server is draining retries
1011 the server a send to this client failed retries
4403 the server origin not allowed, or not the page's user; the reason says which logs the reason, stays lost, never reloads
4404 the server unknown page: reaped or restarted reloads into a fresh page
4409 the server the page already has a socket, a duplicated tab reloads into a fresh page
anything else, or no open the server is unreachable retries

A retry waits 250 × 2^(n-1) ms, capped at 3000. A connect attempt that neither opens nor closes within 4 seconds is closed and counted as a retry. A reload is suppressed when the last one was under 3 seconds ago without a patches frame in between, so a cached page can't loop.

Limits

limit value
pages per process 10000; past it, a static render
never-connected page reaped after 30 s
disconnected page, the grace window reaped after 60 s
reaper cadence every 10 s
mailbox per page 256 topic deliveries; past it, dropped and resynced
slow event logged after 10 s; the socket keeps waiting
text message maxBodySize, 1 MiB
idle timeout 5 minutes; the client pings every 30 s
outgoing frames queued 64; a send that blocks longer than 10 s closes the connection
Async work that throws logged; the state is unchanged

Related

The live layer is the model behind the protocol.