12345678910export interface PairingSocket {11 send(bytes: Uint8Array): void;12 13 receive(): Promise<Uint8Array>;14 close(): void;15 16 closedBeforeFrame(): boolean;17}181920export function openRelayPairingSocket(url: string): Promise<PairingSocket> {21 return new Promise((resolve, reject) => {22 let ws: WebSocket;23 try {24 ws = new WebSocket(url);25 } catch (err) {26 reject(err instanceof Error ? err : new Error("websocket open failed"));27 return;28 }29 ws.binaryType = "arraybuffer";3031 const inbox: Uint8Array[] = [];32 const readers: Array<{ resolve: (b: Uint8Array) => void; reject: (e: Error) => void }> = [];33 let opened = false;34 let closed = false;35 36 37 let sawCloseBeforeFrame = false;3839 ws.onopen = () => {40 opened = true;41 resolve(sock);42 };43 ws.onmessage = (ev: MessageEvent) => {44 const bytes = new Uint8Array(ev.data as ArrayBuffer);45 const r = readers.shift();46 if (r) { sawCloseBeforeFrame = false; r.resolve(bytes); }47 else inbox.push(bytes);48 };49 ws.onerror = () => {50 if (!opened) reject(new Error("websocket connection failed"));51 };52 ws.onclose = () => {53 closed = true;54 if (readers.length) {55 sawCloseBeforeFrame = true;56 while (readers.length) readers.shift()!.reject(new Error("socket closed"));57 }58 if (!opened) reject(new Error("socket closed before open"));59 };6061 const sock: PairingSocket = {62 send(bytes: Uint8Array) { ws.send(bytes as Uint8Array<ArrayBuffer>); },63 receive(): Promise<Uint8Array> {64 const queued = inbox.shift();65 if (queued) { sawCloseBeforeFrame = false; return Promise.resolve(queued); }66 if (closed) { sawCloseBeforeFrame = true; return Promise.reject(new Error("socket closed")); }67 return new Promise<Uint8Array>((res, rej) => readers.push({ resolve: res, reject: rej }));68 },69 close() { try { ws.close(); } catch { } },70 closedBeforeFrame() { return sawCloseBeforeFrame; },71 };72 });73}