Context
Follow-up from #1180. The Node bridge has the same 4-pass JSON serialization overhead as QuickJS for onPageRendered payloads — EncodeMessage embeds the full ~800KB HTML inside a JSON envelope, requiring JSON escaping on encode and unescaping on decode on both Go and Node sides.
The QuickJS path is addressed in #1180 via native object construction. The Node path requires IPC protocol changes and is tracked separately here.
Current Node IPC path
CallHook → bridge.Send(&Message{Payload: HookRenderedPayload{...}}) → EncodeMessage:
- Go:
jsonCodec.Marshal(msg) — JSON-encodes ~800KB HTML (escaping ", <, >, \n, etc.)
- Node:
JSON.parse(body) — parses the full JSON including escaped HTML
- Node: Hook runs, returns object
- Node:
JSON.stringify(result) — re-encodes ~800KB HTML
- Go:
jsonCodec.Unmarshal(body, &resp) — parses it back
Why it's lower priority than #1180
BatchCallHook distributes pages across the worker pool — per-page overhead is amortized across CPU cores
- QuickJS is single-threaded, so the QuickJS fix has higher wall-clock impact
- Protocol changes touch
EncodeMessage/DecodeMessage (Go), the Node-side bridge JS message handler, and the Message struct — higher blast radius
Possible approaches
- Split-message framing: Send a small JSON metadata message (
{ frontMatter, url, path, htmlLength: N }) followed by N bytes of raw HTML. Avoids JSON-encoding the HTML body entirely.
- Length-prefixed binary fields: Extend the LSP-style framing to support binary fields — the JSON envelope references a field by name, and the raw bytes follow the JSON body.
- Base64 encoding: Simpler than protocol changes but only reduces overhead ~33% (base64 inflation vs JSON escaping). Not recommended.
Refs
Context
Follow-up from #1180. The Node bridge has the same 4-pass JSON serialization overhead as QuickJS for
onPageRenderedpayloads —EncodeMessageembeds the full ~800KB HTML inside a JSON envelope, requiring JSON escaping on encode and unescaping on decode on both Go and Node sides.The QuickJS path is addressed in #1180 via native object construction. The Node path requires IPC protocol changes and is tracked separately here.
Current Node IPC path
CallHook→bridge.Send(&Message{Payload: HookRenderedPayload{...}})→EncodeMessage:jsonCodec.Marshal(msg)— JSON-encodes ~800KB HTML (escaping",<,>,\n, etc.)JSON.parse(body)— parses the full JSON including escaped HTMLJSON.stringify(result)— re-encodes ~800KB HTMLjsonCodec.Unmarshal(body, &resp)— parses it backWhy it's lower priority than #1180
BatchCallHookdistributes pages across the worker pool — per-page overhead is amortized across CPU coresEncodeMessage/DecodeMessage(Go), the Node-side bridge JS message handler, and theMessagestruct — higher blast radiusPossible approaches
{ frontMatter, url, path, htmlLength: N }) followed by N bytes of raw HTML. Avoids JSON-encoding the HTML body entirely.Refs