UNPKG

@arkts/headless-jsonrpc

Version:

Simple and Fast headless JSON-RPC communication libraries support advanced features such as retry and timeout.

147 lines (142 loc) 4.9 kB
import { createBrowserWindowAdapter } from "./browser-window-gQCkOd1V.js"; import { createVSCodeWebviewAdapter } from "./vscode-webview-B5bu8aPw.js"; //#region src/errors/rpc-exception.ts var RpcException = class extends Error { constructor(message, code, data) { super(message); this.code = code; this.data = data; this.name = "RpcException"; } }; //#endregion //#region src/errors/timeout-exception.ts var TimeoutException = class extends Error { constructor(operationType = "sendRequest") { super(`Operation ${operationType} timed out`); this.operationType = operationType; } static isTimeoutException(error) { return error instanceof this; } }; //#endregion //#region src/typings/jsonrpc.ts function isRequest(obj) { if (typeof obj !== "object" || !obj) return false; if (!("jsonrpc" in obj) || !("id" in obj) || !("method" in obj) || !("params" in obj)) return false; return typeof obj.jsonrpc === "string" && typeof obj.method === "string" && typeof obj.params === "object" && obj.params !== null; } function isResponse(obj) { if (typeof obj !== "object" || !obj) return false; return "jsonrpc" in obj && "id" in obj && ("result" in obj || "error" in obj); } //#endregion //#region src/connection.ts var ConnectionImpl = class ConnectionImpl { constructor(options) { this.options = options; } get timeout() { return this.options.timeout ?? 5e3; } static createThrowableTimeout(operationType, timeout) { return new Promise((_, reject) => { const timer = setTimeout(() => { clearTimeout(timer); reject(new TimeoutException(operationType)); }, timeout); }); } sendNotification(notification, options) { return Promise.race([Array.isArray(notification) ? this.options.adapter.sendEvent(notification.map((notify) => ({ ...notify, jsonrpc: notify.jsonrpc ?? "2.0", id: notify.id ?? null }))) : this.options.adapter.sendEvent({ ...notification, jsonrpc: notification.jsonrpc ?? "2.0", id: notification.id ?? null }), ConnectionImpl.createThrowableTimeout("sendNotification", typeof options === "number" ? options : options?.timeout ?? this.timeout)]); } async sendRequest(request, options) { const resolvedRequest = Array.isArray(request) ? request.map((req) => ({ ...req, jsonrpc: req.jsonrpc ?? "2.0", id: req.id ?? null })) : { ...request, jsonrpc: request.jsonrpc ?? "2.0", id: request.id ?? null }; const listenPromise = new Promise((resolve) => this.options.adapter.onEvent((response) => { if (Array.isArray(request) && Array.isArray(response) && response.some((res) => request.some((req) => req.id === res.id))) resolve(response); if (!Array.isArray(request) && !Array.isArray(response) && request.id === response.id) resolve(response); })); this.options.adapter.sendEvent(resolvedRequest); return Promise.race([listenPromise, ConnectionImpl.createThrowableTimeout("sendRequest", typeof options === "number" ? options : options?.timeout ?? this.timeout)]); } onResponse(callback) { return this.options.adapter.onEvent((response) => { if (Array.isArray(response)) { if (response.every((res) => isResponse(res))) callback(response); } else if (isResponse(response)) callback(response); }); } convertParamsToArray(params) { if (Array.isArray(params)) return params; else if (typeof params === "object" && params !== null) return Object.values(params); else return []; } getAdapter() { return this.options.adapter; } async listen() { await this.options.adapter.onEvent(async (resOrReq) => { if (Array.isArray(resOrReq)) { if (resOrReq.every((reqs) => isRequest(reqs))) { const res = await Promise.all(resOrReq.map(async (req) => { try { return { jsonrpc: "2.0", id: req.id, result: await this.options.functions?.[req.method]?.(...this.convertParamsToArray(req.params)) ?? null }; } catch (error) { return { jsonrpc: "2.0", id: req.id, error: error instanceof RpcException ? error : { code: -32603, message: String(error) } }; } })); await this.options.adapter.sendEvent(res); } } else if (isRequest(resOrReq)) try { await this.options.adapter.sendEvent({ jsonrpc: "2.0", id: resOrReq.id, result: await this.options.functions?.[resOrReq.method]?.(...this.convertParamsToArray(resOrReq.params)) ?? null }); } catch (error) { await this.options.adapter.sendEvent({ jsonrpc: "2.0", id: resOrReq.id, error: error instanceof RpcException ? error : { code: -32603, message: String(error) } }); } }); } }; function createConnection(options) { return new ConnectionImpl(options); } //#endregion export { TimeoutException, createBrowserWindowAdapter, createConnection, createVSCodeWebviewAdapter }; //# sourceMappingURL=index.js.map