Chapter 5. WebSocket Protocol & JS SDK
5.1 Connection and authentication
ws://127.0.0.1:8899/ws?token=T # plain
wss://host:8898/ws?token=T # TLS (required when the page is https)
- One message is one submission: pass the template and data (or a
data URL) directly; the server accepts, renders, and prints. The
wire protocol has no method envelope.
- Queries (printers/templates/job details/cancel) always go through
REST; WS carries submissions and events only.
- Heartbeats use protocol-level ping/pong frames (browsers answer
automatically).
- Reconnection: after reconnecting, query in-flight jobs over REST
GET /api/jobs/{id} (events are not replayed); cancellation goes
through REST.
5.2 Submission message (client → server)
| Field |
Type |
Required |
Description |
type |
string |
✓ |
"print" (to paper) or "preview" (render only) |
request_id |
string |
✓ |
Client-generated; correlates accepted/done/error |
template |
string | object |
✓ |
As REST: id / {url} / inline object |
data |
any | object |
✓ |
As REST: inline (array = batch) / {json}/{path} / {url,headers,timeout_s} |
printer |
string |
|
Printer name (omitted = system default) |
copies / copies_per_record / collate / pages |
|
|
As REST /api/print |
options |
object |
|
As REST |
type:"preview" is equivalent to REST POST /api/print with
"preview": true: when done, the tray pops the native preview window
(Section 9.3); without a tray, the web component /preview/{job_id}
applies.
{ "type": "print", "request_id": "r1",
"template": "sf-waybill-100x93",
"data": { "url": "https://erp.example.com/api/waybill/12345" },
"printer": "HP-LaserJet-M1005", "copies_per_record": 3 }
5.3 Event messages (server → client)
| type |
Trigger |
Key fields |
accepted |
Submission accepted |
request_id, job_id, total_pages |
job |
Job state change / progress |
job_id, status, page_done, total_pages (broadcast) |
printer |
Printer state change |
name, state (broadcast) |
done |
Job finished |
request_id, job_id, status |
error |
Submission failed |
request_id, code, message (codes as in Section 4.2) |
{ "type": "accepted", "request_id": "r1", "job_id": "j_01J8…", "total_pages": 6 }
{ "type": "job", "job_id": "j_01J8…", "status": "printing", "page_done": 4, "total_pages": 6 }
{ "type": "printer", "name": "HP-LaserJet-M1005", "state": "out_of_paper" }
{ "type": "done", "request_id": "r1", "job_id": "j_01J8…", "status": "done" }
{ "type": "error", "request_id": "r1", "code": "E_RENDER", "message": "…" }
5.4 Raw protocol usage (browser)
Prefer the JS SDK (Section 5.5); the raw protocol is for understanding
and debugging:
const ws = new WebSocket("ws://127.0.0.1:8899/ws?token=T");
ws.onopen = () => ws.send(JSON.stringify({
type: "print", request_id: "r1",
template: "sf-waybill-100x93",
data: { url: "https://erp.example.com/api/waybill/12345" },
copies_per_record: 3 }));
ws.onmessage = (e) => {
const m = JSON.parse(e.data);
if (m.type === "accepted") console.log("job:", m.job_id, m.total_pages);
if (m.type === "job") console.log(m.status, m.page_done + "/" + m.total_pages);
if (m.type === "done") console.log("finished");
if (m.type === "error") console.error(m.code, m.message);
};
5.5 JS SDK (wxprint.js) reference
The server hosts http://127.0.0.1:8899/sdk/wxprint.js (UMD/ESM),
wrapping the WS protocol and the script API into methods. Include it
with:
<script src="http://127.0.0.1:8899/sdk/wxprint.js"></script>
Constructor options
const wp = new wxprint.WxPrint({ host: "127.0.0.1", port: 8899,
tlsPort: 8898, token: "T",
reconnectDelay: 2000 });
// or the factory: const wp = await wxprint.connect({ token: "T" });
| Option |
Type |
Default |
Description |
host |
string |
location.hostname |
Service host |
port |
int |
8899 |
Plain port (http/ws) |
tlsPort |
int |
8898 |
TLS port (used automatically when the page is https) |
token |
string |
null |
Auth token |
reconnectDelay |
int |
2000 |
Reconnect interval (ms) |
Direct-print methods (wrapping the WS protocol)
| Method |
Parameters |
Returns |
Description |
connect() |
— |
Promise |
Establish the connection (auto-reconnect after drops at reconnectDelay; submissions made while offline are queued and flushed on reconnect) |
close() |
— |
— |
Close the connection (no more reconnects) |
print(req) |
fields as in Section 5.2 |
Promise<{job_id, total_pages}> |
Submit to paper |
preview(req) |
same |
Promise |
Submit a preview (tray window pops on done) |
saveas(req, format) |
req + `“pdf” |
“png” |
“zip”` |
printers() |
— |
Promise |
Printer list (REST pass-through) |
job(jobId) |
string |
Promise |
Job status (REST pass-through) |
onJob(cb) |
(ev) => void |
this |
Subscribe to job events (state changes + progress) |
onPrinter(cb) |
(ev) => void |
this |
Subscribe to printer events |
Programmatic building (DocBuilder, wrapping the script API)
wp.document() returns a chainable builder whose verbs map one-to-one
to Chapter 6:
| Method |
Parameters |
Description |
init(taskName) |
string |
Task name (shown in the print queue) |
pagesize(w, h, opts?) |
mm + {orientation, continuous} |
Page size; h=0 fits the content height |
loadData(data) |
object | array |
Inline data (array = batch) |
loadJSON(textOrPath) |
string |
JSON text or a server-side file path |
loadURL(url, opts?) |
url + {headers, timeout_s} |
Fetched at run time |
addText(el) / addHtml(el) / addTable(el) |
element object |
Fields as in Sections 7.5–7.7 |
addBarcode(el) / addQrcode(el) / addImage(el) |
element object |
Fields as in Sections 7.8–7.10 |
addRect(el) / addLine(el) / addChart(el) |
element object |
Fields as in Section 7.4 |
newpage() |
— |
Force a page break |
print(params?) |
{printer, copies, …} |
Submit to paper |
preview() |
— |
Submit a preview |
Complete example
// ① Direct printing + event subscription
const wp = new wxprint.WxPrint({ token: "T" });
wp.onJob(ev => console.log(ev.status, ev.page_done + "/" + ev.total_pages));
wp.onPrinter(ev => console.log(ev.name, ev.state));
await wp.print({ template: "sf-waybill-100x93",
data: { url: "https://erp.example.com/api/waybill/12345" },
printer: "HP-LaserJet-M1005", copies_per_record: 3 });
// ② Programmatic building (no template file)
const doc = wp.document();
doc.init("Delivery note")
.loadURL("https://erp.example.com/api/order/SO-1024")
.pagesize(210, 297)
.addText({ x: 20, y: 15, w: 80, h: 8, text: "{{title}}",
font: { size_pt: 16, weight: 700 } })
.addBarcode({ x: 20, y: 30, w: 60, h: 10,
symbology: "code128", content: "{{order_no}}" })
.newpage()
.addHtml({ x: 0, y: 0, w: 210, h: 297,
html: "<table>{% for it in items %}<tr><td>{{it.name}}</td></tr>{% endfor %}</table>" });
await doc.print({ printer: "HP-LaserJet-M1005", copies: 2 });
5.6 WS or REST?
| Scenario |
Recommendation |
| Web submission + live progress/completion callbacks |
WS (or the SDK’s print) |
| Backend one-shot submission, status on demand |
REST + polling |
| Cancel / detail queries |
REST (WS carries no queries) |
| HTTPS pages |
wss (the SDK switches to the TLS port automatically) |