@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
JavaScript
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