import type { StandardRPCCustomJsonSerializer } from "@orpc/client/standard" import * as z from "zod" import { decode, encode, type Encodable } from "./index" /** Opaque codec bytes carried through oRPC's native Blob transport. */ export class CodecEnvelope { /** * Creates an opaque encoded value. * * @param blob - Msgpack bytes represented as a transport-safe Blob. */ constructor(public readonly blob: Blob) {} } const BLOB_SCHEMA = z.instanceof(Blob) /** Schema for one opaque codec transport value. */ export const codecEnvelopeSchema = z .instanceof(CodecEnvelope) .refine((envelope) => envelope.blob instanceof Blob, { message: "Codec envelope must contain Blob bytes", }) /** * Wraps already encoded codec bytes without decoding or re-encoding them. * * @param bytes - Exact persisted or freshly encoded codec bytes. */ export function createCodecEnvelope(bytes: Uint8Array) { return new CodecEnvelope(new Blob([Uint8Array.from(bytes)])) } /** * Reads the exact codec bytes carried by an envelope. * * @param envelope - Opaque bytes received from oRPC. */ export async function readCodecEnvelope(envelope: CodecEnvelope) { return new Uint8Array(await envelope.blob.arrayBuffer()) } /** * Encodes one runtime value before it crosses an oRPC boundary. * * @param value - Codec-supported value to transport. */ export async function encodeCodecEnvelope(value: Encodable) { return createCodecEnvelope(await encode(value)) } /** * Decodes one runtime value after it crosses an oRPC boundary. * * @param envelope - Opaque bytes received from oRPC. */ export async function decodeCodecEnvelope(envelope: CodecEnvelope) { return await decode(await readCodecEnvelope(envelope)) } /** Symmetric oRPC serializer for opaque codec envelopes. */ export const codecEnvelopeSerializer: StandardRPCCustomJsonSerializer = { type: 21, condition: (data) => data instanceof CodecEnvelope, serialize: (data) => data.blob, deserialize: (blob) => new CodecEnvelope(BLOB_SCHEMA.parse(blob)), }