UNPKG

rpc-ws

Version:

JSON-RPC 2.0 implementation with websockets

146 lines (145 loc) 3.81 kB
// lib/client-browser.ts async function Client(endpoint, opts) { let call_id = 0; let connected = false; const timeout = opts?.timeout || 1e4; const version = "2.0"; const ws = new WebSocket(endpoint); const events = /* @__PURE__ */ new Map(); const emitter = new EventTarget(); const queue = /* @__PURE__ */ new Map(); await setup(); function assertConnection() { if (!connected) throw new Error("WebSocket connection not estabilished"); } async function setup() { connected = await Promise.race([ new Promise( (resolve) => ws.addEventListener("open", () => resolve(true)) ), new Promise((_, reject) => setTimeout(() => reject(false), timeout)) ]); assertConnection(); ws.addEventListener("message", (e) => { try { const payload = JSON.parse(e.data); if (payload.notification && events.has(payload.notification)) { const cb = events.get(payload.notification); cb(Array.isArray(payload.params) ? payload.params : [payload.params]); return; } if (!payload.id) return; const event = queue.get(payload.id); if (!event) return; if (payload.error) { queue.set(payload.id, { ...event, error: payload.error }); } else { queue.set(payload.id, { ...event, result: payload.result }); } emitter.dispatchEvent(new Event(String(payload.id))); } catch (error) { console.error(error); } }); } function _send(request) { return new Promise((resolve, reject) => { const callTimeout = setTimeout(() => { queue.set(request.id, { type: "request", error: { code: -32e3, message: "Request timed out" } }); emitter.dispatchEvent(new Event(String(request.id))); }, timeout); try { ws.send(JSON.stringify(request)); } catch (error) { return reject({ id: request.id, jsonrpc: request.jsonrpc, error: { code: -32700, message: "Parse error" } }); } emitter.addEventListener(String(request.id), () => { clearTimeout(callTimeout); const response = queue.get(request.id); queue.delete(request.id); return resolve({ id: request.id, jsonrpc: request.jsonrpc, result: response.result, error: response.error }); }); }); } function subscribe(namespace, cb) { assertConnection(); const request = { jsonrpc: version, method: "rpc.on", params: [namespace], id: ++call_id }; events.set(namespace, cb); queue.set(request.id, { type: "notification" }); return _send(request); } function unsubscribe(namespace) { assertConnection(); const request = { jsonrpc: version, method: "rpc.off", params: [namespace], id: ++call_id }; events.delete(namespace); queue.set(request.id, { type: "notification" }); return _send(request); } function send(method, ...params) { assertConnection(); const request = { jsonrpc: version, method, params, id: ++call_id }; queue.set(request.id, { type: "request" }); return _send(request); } function close() { ws.close(); } const base = { subscribe, unsubscribe, close }; return new Proxy(base, { get(target, prop) { if (prop === "then") { return void 0; } if (prop in base) { return Reflect.get(target, prop); } return (...args) => send(prop, ...args); } }); } export { Client as BrowserClient };