Description
When hooks chain (hook 1's result feeds hook 2), the payload for hook 2 is map[string]interface{} — not a typed struct like HookRenderedPayload. Without a dedicated case, this falls through to the default: branch in CallHook's type switch, which JSON-serializes the entire map including ~800KB of HTML.
The RHDS site has two onPageRendered hooks (01-transforms.js and 02-lit-ssr.js). Hook 1 goes through the fast path and returns map[string]interface{}{"html": "..."}. Hook 2 receives that map, hits the default: case, and re-serializes 800KB of HTML through the full JSON round-trip — undoing the fast path's work.
Same issue affects the Node IPC path: EncodeMessage only has split-body cases for typed structs, not for map[string]interface{} chained results.
Fix
QuickJS path
Add case map[string]interface{}: to the fast path switch in CallHook, guarded by key presence:
case map[string]interface{}:
if _, hasHTML := v["html"]; hasHTML {
return r.callHookMapPayload(name, v)
}
if _, hasContent := v["content"]; hasContent {
return r.callHookMapPayload(name, v)
}
// fall through to default JSON path for non-page maps
The guard is critical — map[string]interface{} is also used for onDataFetched, onBeforeValidation, onAfterValidation payloads. Only maps with html or content keys (the signature of a chained page hook result) should use the fast path.
callHookMapPayload builds a JS object with string values as native strings and non-string values via JSON. Selects the extractor based on which content field is present.
Node IPC path
Add case map[string]interface{}: to EncodeMessage's hook type switch with the same html/content key guard. Strip the large field and send raw bytes.
Spec test needs
- Two QuickJS onPageRendered hooks chain: second hook receives
html from first hook's result
- Two hooks chain, second modifies html: pipeline applies second hook's modification
- Non-page map payload (onDataFetched): NOT caught by the map fast path, full JSON round-trip preserved
- Node IPC: chained map with
html key uses split-body framing
- Node IPC: chained map without
html/content key uses standard JSON framing
Refs #1185, #1180
Description
When hooks chain (hook 1's result feeds hook 2), the payload for hook 2 is
map[string]interface{}— not a typed struct likeHookRenderedPayload. Without a dedicated case, this falls through to thedefault:branch inCallHook's type switch, which JSON-serializes the entire map including ~800KB of HTML.The RHDS site has two
onPageRenderedhooks (01-transforms.js and 02-lit-ssr.js). Hook 1 goes through the fast path and returnsmap[string]interface{}{"html": "..."}. Hook 2 receives that map, hits thedefault:case, and re-serializes 800KB of HTML through the full JSON round-trip — undoing the fast path's work.Same issue affects the Node IPC path:
EncodeMessageonly has split-body cases for typed structs, not formap[string]interface{}chained results.Fix
QuickJS path
Add
case map[string]interface{}:to the fast path switch inCallHook, guarded by key presence:The guard is critical —
map[string]interface{}is also used foronDataFetched,onBeforeValidation,onAfterValidationpayloads. Only maps withhtmlorcontentkeys (the signature of a chained page hook result) should use the fast path.callHookMapPayloadbuilds a JS object with string values as native strings and non-string values via JSON. Selects the extractor based on which content field is present.Node IPC path
Add
case map[string]interface{}:toEncodeMessage's hook type switch with the samehtml/contentkey guard. Strip the large field and send raw bytes.Spec test needs
htmlfrom first hook's resulthtmlkey uses split-body framinghtml/contentkey uses standard JSON framingRefs #1185, #1180