Barazo AppView backend barazo.forum
at main 52 lines 1.5 kB view raw
1import { decodeFirst } from 'cborg' 2 3/** 4 * Header of an AT Protocol XRPC event stream frame. 5 * 6 * @see https://atproto.com/specs/event-stream 7 */ 8export interface FrameHeader { 9 /** Operation type: 1 = regular message, -1 = error */ 10 op: number 11 /** Lexicon sub-type in short form (e.g. '#labels'), present when op = 1 */ 12 t?: string 13} 14 15/** 16 * Decoded AT Protocol event stream frame (header + body). 17 */ 18export interface DecodedFrame { 19 header: FrameHeader 20 body: Record<string, unknown> 21} 22 23/** 24 * Decode an AT Protocol XRPC event stream binary frame. 25 * 26 * Each WebSocket frame contains two concatenated CBOR objects: 27 * 1. A header with `op` (operation type) and optional `t` (message type) 28 * 2. The message body 29 * 30 * @param data - Raw binary data (Uint8Array, ArrayBuffer, or Buffer) 31 * @returns The decoded header and body 32 * @throws If the data cannot be decoded as two consecutive CBOR objects 33 * 34 * @see https://atproto.com/specs/event-stream 35 */ 36export function decodeEventStreamFrame(data: Uint8Array | ArrayBuffer | Buffer): DecodedFrame { 37 const bytes = data instanceof Uint8Array ? data : new Uint8Array(data) 38 39 if (bytes.length === 0) { 40 throw new Error('Empty event stream frame') 41 } 42 43 const [header, remainder] = decodeFirst(bytes) as [FrameHeader, Uint8Array] 44 45 if (remainder.length === 0) { 46 throw new Error('Truncated event stream frame: missing body after header') 47 } 48 49 const [body] = decodeFirst(remainder) as [Record<string, unknown>, Uint8Array] 50 51 return { header, body } 52}