/ src / lib / server / gateway / protocol.ts
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  }