UNPKG

viem

Version:

TypeScript Interface for Ethereum

154 lines 5.83 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.rpc = exports.getSocket = exports.socketsCache = void 0; const request_js_1 = require("../errors/request.js"); const createBatchScheduler_js_1 = require("./promise/createBatchScheduler.js"); const withTimeout_js_1 = require("./promise/withTimeout.js"); const stringify_js_1 = require("./stringify.js"); let id = 0; async function http(url, { body, fetchOptions = {}, timeout = 10000 }) { const { headers, method, signal: signal_ } = fetchOptions; try { const response = await (0, withTimeout_js_1.withTimeout)(async ({ signal }) => { const response = await fetch(url, { ...fetchOptions, body: Array.isArray(body) ? (0, stringify_js_1.stringify)(body.map((body) => ({ jsonrpc: '2.0', id: body.id ?? id++, ...body, }))) : (0, stringify_js_1.stringify)({ jsonrpc: '2.0', id: body.id ?? id++, ...body }), headers: { ...headers, 'Content-Type': 'application/json', }, method: method || 'POST', signal: signal_ || (timeout > 0 ? signal : undefined), }); return response; }, { errorInstance: new request_js_1.TimeoutError({ body, url }), timeout, signal: true, }); let data; if (response.headers.get('Content-Type')?.startsWith('application/json')) { data = await response.json(); } else { data = await response.text(); } if (!response.ok) { throw new request_js_1.HttpRequestError({ body, details: (0, stringify_js_1.stringify)(data.error) || response.statusText, headers: response.headers, status: response.status, url, }); } return data; } catch (err) { if (err instanceof request_js_1.HttpRequestError) throw err; if (err instanceof request_js_1.TimeoutError) throw err; throw new request_js_1.HttpRequestError({ body, details: err.message, url, }); } } exports.socketsCache = new Map(); async function getSocket(url) { let socket = exports.socketsCache.get(url); if (socket) return socket; const { schedule } = (0, createBatchScheduler_js_1.createBatchScheduler)({ id: url, fn: async () => { const WebSocket = await Promise.resolve().then(() => require('isows')).then((module) => module.WebSocket); const webSocket = new WebSocket(url); const requests = new Map(); const subscriptions = new Map(); const onMessage = ({ data }) => { const message = JSON.parse(data); const isSubscription = message.method === 'eth_subscription'; const id = isSubscription ? message.params.subscription : message.id; const cache = isSubscription ? subscriptions : requests; const callback = cache.get(id); if (callback) callback({ data }); if (!isSubscription) cache.delete(id); }; const onClose = () => { exports.socketsCache.delete(url); webSocket.removeEventListener('close', onClose); webSocket.removeEventListener('message', onMessage); }; webSocket.addEventListener('close', onClose); webSocket.addEventListener('message', onMessage); if (webSocket.readyState === WebSocket.CONNECTING) { await new Promise((resolve, reject) => { if (!webSocket) return; webSocket.onopen = resolve; webSocket.onerror = reject; }); } socket = Object.assign(webSocket, { requests, subscriptions, }); exports.socketsCache.set(url, socket); return [socket]; }, }); const [_, [socket_]] = await schedule(); return socket_; } exports.getSocket = getSocket; function webSocket(socket, { body, onResponse }) { if (socket.readyState === socket.CLOSED || socket.readyState === socket.CLOSING) throw new request_js_1.WebSocketRequestError({ body, url: socket.url, details: 'Socket is closed.', }); const id_ = id++; const callback = ({ data }) => { const message = JSON.parse(data); if (typeof message.id === 'number' && id_ !== message.id) return; onResponse?.(message); if (body.method === 'eth_subscribe' && typeof message.result === 'string') { socket.subscriptions.set(message.result, callback); } if (body.method === 'eth_unsubscribe') { socket.subscriptions.delete(body.params?.[0]); } }; socket.requests.set(id_, callback); socket.send(JSON.stringify({ jsonrpc: '2.0', ...body, id: id_ })); return socket; } async function webSocketAsync(socket, { body, timeout = 10000 }) { return (0, withTimeout_js_1.withTimeout)(() => new Promise((onResponse) => exports.rpc.webSocket(socket, { body, onResponse, })), { errorInstance: new request_js_1.TimeoutError({ body, url: socket.url }), timeout, }); } exports.rpc = { http, webSocket, webSocketAsync, }; //# sourceMappingURL=rpc.js.map