rpc-ws
Version:
JSON-RPC 2.0 implementation with websockets
169 lines (167 loc) • 5.07 kB
JavaScript
;
var RPCWebSocket = (() => {
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// lib/client-browser.ts
var client_browser_exports = {};
__export(client_browser_exports, {
Client: () => Client
});
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);
}
});
}
return __toCommonJS(client_browser_exports);
})();