protocol.ts
1 export interface GatewayRequestFrame { 2 type: 'req' 3 id: string 4 method: string 5 params?: Record<string, unknown> 6 } 7 8 export interface GatewayResponseFrame { 9 type: 'res' 10 id: string 11 ok: boolean 12 payload?: unknown 13 error?: { 14 code?: string 15 message?: string 16 } | null 17 } 18 19 export interface GatewayEventFrame { 20 type: 'event' 21 event: string 22 payload?: unknown 23 } 24 25 export type GatewayFrame = GatewayRequestFrame | GatewayResponseFrame | GatewayEventFrame 26 27 function toJsonString(raw: unknown): string | null { 28 if (typeof raw === 'string') return raw 29 if (raw instanceof Uint8Array) return Buffer.from(raw).toString('utf8') 30 if (Buffer.isBuffer(raw)) return raw.toString('utf8') 31 if (raw && typeof raw === 'object' && 'toString' in raw) { 32 try { 33 return String((raw as { toString: () => string }).toString()) 34 } catch { 35 return null 36 } 37 } 38 return null 39 } 40 41 function isRecord(value: unknown): value is Record<string, unknown> { 42 return !!value && typeof value === 'object' && !Array.isArray(value) 43 } 44 45 function parseFrameObject(value: unknown): GatewayFrame | null { 46 if (!isRecord(value)) return null 47 const type = typeof value.type === 'string' ? value.type : '' 48 if (type === 'req') { 49 const id = typeof value.id === 'string' ? value.id : '' 50 const method = typeof value.method === 'string' ? value.method : '' 51 if (!id || !method) return null 52 const params = isRecord(value.params) ? value.params : undefined 53 return { type: 'req', id, method, params } 54 } 55 if (type === 'res') { 56 const id = typeof value.id === 'string' ? value.id : '' 57 const ok = value.ok === true 58 if (!id) return null 59 const error = isRecord(value.error) 60 ? { 61 code: typeof value.error.code === 'string' ? value.error.code : undefined, 62 message: typeof value.error.message === 'string' ? value.error.message : undefined, 63 } 64 : null 65 return { 66 type: 'res', 67 id, 68 ok, 69 payload: value.payload, 70 error, 71 } 72 } 73 if (type === 'event') { 74 const event = typeof value.event === 'string' ? value.event : '' 75 if (!event) return null 76 return { 77 type: 'event', 78 event, 79 payload: value.payload, 80 } 81 } 82 return null 83 } 84 85 export function parseGatewayFrame(raw: unknown): GatewayFrame | null { 86 if (isRecord(raw) && typeof raw.type === 'string') { 87 return parseFrameObject(raw) 88 } 89 const text = toJsonString(raw) 90 if (!text) return null 91 try { 92 const parsed = JSON.parse(text) 93 return parseFrameObject(parsed) 94 } catch { 95 return null 96 } 97 } 98 99 export function serializeGatewayFrame(frame: GatewayFrame): string { 100 return JSON.stringify(frame) 101 } 102 103 export function createGatewayRequestFrame( 104 id: string, 105 method: string, 106 params?: Record<string, unknown>, 107 ): GatewayRequestFrame { 108 return { 109 type: 'req', 110 id, 111 method, 112 params, 113 } 114 }