# marshal-response ## Purpose This module turns the value returned by a sandboxed handler into a host `Response`. The sandbox-side handler returns a `SerializedResponse` (plain object with `status`, ordered header pairs, and base64-url `bodyBase64`); this module validates that shape with a Zod schema and constructs the corresponding WHATWG `Response` for the host's request pipeline to forward to the caller. The validation step is what stops a misbehaving sandboxed handler (returning `null`, returning a primitive, returning a partial object missing `status`) from crashing the host pipeline downstream — and re-rehydrates the structurally pure JSON shape (`Array<[k, v]>` headers, `bodyBase64` string) back into the host's class-flavoured `Response` type. ## Features - Zod-validated unmarshal — `SerializedResponseSchema` enforces `status ∈ [100,599]`, ordered `[name, value]` header pairs, and `bodyBase64: string` plus the optional `errorCode`/`errorMessage` pair the wire format uses for sandbox-tier exceptions. - Failure surfacing — non-conformant shapes raise `SAND_VM_BRIDGE.UNMARSHAL_FAILED` with the Zod issue list attached as `context.issues`. - Base64-url body decode — `Buffer.from(str, "base64url")` rehydrates the bytes; the result feeds straight into `Response`'s `BodyInit`-compatible `Uint8Array` slot. - Headers rehydration — `Array<[name, value]>` is appended onto a fresh `Headers` instance so duplicate-name semantics from the sandbox are preserved as the response leaves the bridge. - Bodyless-status enforcement — 204/304 responses drop the body even if the sandbox produced one, matching the WHATWG fetch rule the host pipeline expects. ## Usage: Basic ```ts import { unmarshalResponse } from "@suaveplan/api-sandbox-vm-host-bridge/marshal/marshal-response"; const response = unmarshalResponse(await sandboxHandler(serialized)); ``` ## Usage: Advanced Use the exported schema to pre-validate cached or replayed responses before they re-enter the pipeline: ```ts import { SerializedResponseSchema } from "@suaveplan/api-sandbox-vm-host-bridge/marshal/marshal-response"; const cached = await responseCache.get(key); const result = SerializedResponseSchema.safeParse(cached); if (result.success) { return cached; } return rebuildFromSandbox(); ``` ## API Reference - `unmarshalResponse(value: unknown): Response` — synchronous; throws `SandboxVmBridgeError(UNMARSHAL_FAILED)` on shape failure. - `SerializedResponseSchema: z.ZodType` — the Zod validator used internally; mirrors the contract published by `@suaveplan/api-types/sandbox-bridge`. ## Implementation Notes Headers cross as a `Headers` instance built via `append()` so duplicates (multi-`Set-Cookie`, etc.) preserve their iteration order. The body decode skips the `Buffer.from` call for empty strings so the common "no body" path costs nothing. Status 204/304 force a `null` body in the constructed `Response` because WHATWG forbids a body on those codes — the bridge enforces this so a sandbox that mistakenly attaches one cannot break the host pipeline.