Files
suaveplan/packages/api-stack/api/api-sandbox-vm-host-bridge/src/marshal/marshal-response.md
2026-05-27 23:29:53 +01:00

3.1 KiB

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

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:

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<SerializedResponse> — 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.