diff --git a/.changeset/wet-beans-arrive.md b/.changeset/wet-beans-arrive.md new file mode 100644 index 00000000..a253d53d --- /dev/null +++ b/.changeset/wet-beans-arrive.md @@ -0,0 +1,10 @@ +--- +"@ckb-ccc/core": minor +--- + +refactor(core): rename JSON-RPC transport APIs with the `JsonRpcTransport` prefix + +- `Transport` is now `JsonRpcTransport` +- `TransportHttp` is now `JsonRpcTransportHttp` +- `TransportWebSocket` is now `JsonRpcTransportWebSocket` +- `TransportFallback` is now `JsonRpcTransportFallback` diff --git a/packages/core/src/jsonRpc/requestor.test.ts b/packages/core/src/jsonRpc/requestor.test.ts index 52a038f3..8b75391a 100644 --- a/packages/core/src/jsonRpc/requestor.test.ts +++ b/packages/core/src/jsonRpc/requestor.test.ts @@ -3,7 +3,7 @@ import { RequestorJsonRpc } from "./requestor.js"; import { JsonRpcPayload, JsonRpcResponse, - Transport, + JsonRpcTransport, } from "./transports/index.js"; function response(payload: JsonRpcPayload, result: unknown): JsonRpcResponse { @@ -36,7 +36,7 @@ describe("RequestorJsonRpc", () => { }); let active = 0; let maxActive = 0; - const transport: Transport = { + const transport: JsonRpcTransport = { async close() {}, async request(payload) { active += 1; @@ -64,7 +64,7 @@ describe("RequestorJsonRpc", () => { it("advances the queue after a transport error", async () => { let calls = 0; - const transport: Transport = { + const transport: JsonRpcTransport = { async close() {}, async request(payload) { calls += 1; @@ -91,7 +91,7 @@ describe("RequestorJsonRpc", () => { it("does not exhaust a larger concurrency limit after transport errors", async () => { let calls = 0; - const transport: Transport = { + const transport: JsonRpcTransport = { async close() {}, async request(payload) { calls += 1; diff --git a/packages/core/src/jsonRpc/requestor.ts b/packages/core/src/jsonRpc/requestor.ts index b160724a..85ef1182 100644 --- a/packages/core/src/jsonRpc/requestor.ts +++ b/packages/core/src/jsonRpc/requestor.ts @@ -1,9 +1,9 @@ -import { transportFromUri } from "./transports/factory.js"; +import { jsonRpcTransportFromUri } from "./transports/factory.js"; import { JsonRpcPayload, JsonRpcResponse, - Transport, - TransportFallback, + JsonRpcTransport, + JsonRpcTransportFallback, } from "./transports/index.js"; /** @@ -30,7 +30,7 @@ export type RequestorJsonRpcConfig = { fallbacks?: string[]; timeout?: number; maxConcurrent?: number; - transport?: Transport; + transport?: JsonRpcTransport; }; export class RequestorJsonRpc { @@ -38,7 +38,7 @@ export class RequestorJsonRpc { private concurrent = 0; private readonly pending: (() => void)[] = []; - public readonly transport: Transport; + public readonly transport: JsonRpcTransport; private id = 0; @@ -56,10 +56,10 @@ export class RequestorJsonRpc { this.maxConcurrent = config?.maxConcurrent; this.transport = config?.transport ?? - new TransportFallback( + new JsonRpcTransportFallback( Array.from( new Set([url_, ...(config?.fallbacks ?? [])]).values(), - (url) => transportFromUri(url, config), + (url) => jsonRpcTransportFromUri(url, config), ), ); } diff --git a/packages/core/src/jsonRpc/transports/factory.ts b/packages/core/src/jsonRpc/transports/factory.ts index 7b8d7829..bf8134af 100644 --- a/packages/core/src/jsonRpc/transports/factory.ts +++ b/packages/core/src/jsonRpc/transports/factory.ts @@ -1,10 +1,13 @@ -import { TransportHttp } from "./http.js"; -import { TransportWebSocket } from "./webSocket.js"; +import { JsonRpcTransportHttp } from "./http.js"; +import { JsonRpcTransportWebSocket } from "./webSocket.js"; -export function transportFromUri(uri: string, config?: { timeout?: number }) { +export function jsonRpcTransportFromUri( + uri: string, + config?: { timeout?: number }, +) { if (uri.startsWith("wss://") || uri.startsWith("ws://")) { - return new TransportWebSocket(uri, config?.timeout); + return new JsonRpcTransportWebSocket(uri, config?.timeout); } - return new TransportHttp(uri, config?.timeout); + return new JsonRpcTransportHttp(uri, config?.timeout); } diff --git a/packages/core/src/jsonRpc/transports/fallback.test.ts b/packages/core/src/jsonRpc/transports/fallback.test.ts index 1b14e963..1483f420 100644 --- a/packages/core/src/jsonRpc/transports/fallback.test.ts +++ b/packages/core/src/jsonRpc/transports/fallback.test.ts @@ -1,6 +1,10 @@ import { describe, expect, it, vi } from "vitest"; -import { TransportFallback } from "./fallback.js"; -import { JsonRpcPayload, JsonRpcResponse, Transport } from "./transport.js"; +import { JsonRpcTransportFallback } from "./fallback.js"; +import { + JsonRpcPayload, + JsonRpcResponse, + JsonRpcTransport, +} from "./transport.js"; const payload: JsonRpcPayload = { id: 0, @@ -14,15 +18,17 @@ const response: JsonRpcResponse = { result: "ok", }; -function makeTransport(handler: () => Promise): Transport { +function makeTransport( + handler: () => Promise, +): JsonRpcTransport { return { request: () => handler(), async close() {} }; } -describe("TransportFallback", () => { +describe("JsonRpcTransportFallback", () => { it("closes every transport", async () => { const closeA = vi.fn(); const closeB = vi.fn(); - const transport = new TransportFallback([ + const transport = new JsonRpcTransportFallback([ { request: async () => response, close: closeA }, { request: async () => response, close: closeB }, ]); @@ -39,7 +45,7 @@ describe("TransportFallback", () => { throw error; }); const closeB = vi.fn(async () => {}); - const transport = new TransportFallback([ + const transport = new JsonRpcTransportFallback([ { request: async () => response, close: closeA }, { request: async () => response, close: closeB }, ]); @@ -51,14 +57,14 @@ describe("TransportFallback", () => { }); it("returns result from the first healthy transport", async () => { - const transport = new TransportFallback([ + const transport = new JsonRpcTransportFallback([ makeTransport(() => Promise.resolve(response)), ]); expect(await transport.request(payload)).toBe(response); }); it("falls back to the next transport when the first fails", async () => { - const transport = new TransportFallback([ + const transport = new JsonRpcTransportFallback([ makeTransport(() => Promise.reject(new Error("fail"))), makeTransport(() => Promise.resolve(response)), ]); @@ -66,7 +72,7 @@ describe("TransportFallback", () => { }); it("throws when all transports fail", async () => { - const transport = new TransportFallback([ + const transport = new JsonRpcTransportFallback([ makeTransport(() => Promise.reject(new Error("fail A"))), makeTransport(() => Promise.reject(new Error("fail B"))), ]); @@ -76,7 +82,7 @@ describe("TransportFallback", () => { it("concurrent requests both succeed when the first transport is down", async () => { // Transport A is always unavailable; transport B always succeeds. // Two concurrent requests should each fall back to B independently. - const transport = new TransportFallback([ + const transport = new JsonRpcTransportFallback([ makeTransport(() => Promise.reject(new Error("A unavailable"))), makeTransport(() => Promise.resolve(response)), ]); @@ -100,7 +106,7 @@ describe("TransportFallback", () => { let callsToA = 0; let callsToB = 0; - const transport = new TransportFallback([ + const transport = new JsonRpcTransportFallback([ makeTransport(() => { callsToA += 1; return Promise.reject(new Error("A unavailable")); diff --git a/packages/core/src/jsonRpc/transports/fallback.ts b/packages/core/src/jsonRpc/transports/fallback.ts index bcf5443f..ab0051c1 100644 --- a/packages/core/src/jsonRpc/transports/fallback.ts +++ b/packages/core/src/jsonRpc/transports/fallback.ts @@ -1,15 +1,19 @@ -import { JsonRpcPayload, JsonRpcResponse, Transport } from "./transport.js"; +import { + JsonRpcPayload, + JsonRpcResponse, + JsonRpcTransport, +} from "./transport.js"; -export class TransportFallback implements Transport { +export class JsonRpcTransportFallback implements JsonRpcTransport { // Current transport index private i = 0; - constructor(private readonly transports: Transport[]) {} + constructor(private readonly transports: JsonRpcTransport[]) {} async request(data: JsonRpcPayload): Promise { const startI = this.i; let lastErr: unknown = new Error( - "TransportFallback requires at least one transport", + "JsonRpcTransportFallback requires at least one transport", ); for (let tried = 0; tried < this.transports.length; tried += 1) { diff --git a/packages/core/src/jsonRpc/transports/http.test.ts b/packages/core/src/jsonRpc/transports/http.test.ts index 83dc88f0..541933d1 100644 --- a/packages/core/src/jsonRpc/transports/http.test.ts +++ b/packages/core/src/jsonRpc/transports/http.test.ts @@ -1,5 +1,5 @@ import { afterEach, describe, expect, it, vi } from "vitest"; -import { TransportHttp } from "./http.js"; +import { JsonRpcTransportHttp } from "./http.js"; import type { JsonRpcPayload, JsonRpcResponse } from "./transport.js"; const payload: JsonRpcPayload = { @@ -19,7 +19,7 @@ afterEach(() => { vi.unstubAllGlobals(); }); -describe("TransportHttp", () => { +describe("JsonRpcTransportHttp", () => { it("clears its timeout after a successful response", async () => { vi.useFakeTimers(); vi.stubGlobal( @@ -28,7 +28,7 @@ describe("TransportHttp", () => { ); await expect( - new TransportHttp("https://example.com").request(payload), + new JsonRpcTransportHttp("https://example.com").request(payload), ).resolves.toEqual(response); expect(vi.getTimerCount()).toBe(0); }); @@ -41,7 +41,7 @@ describe("TransportHttp", () => { ); await expect( - new TransportHttp("https://example.com").request(payload), + new JsonRpcTransportHttp("https://example.com").request(payload), ).rejects.toThrow("unavailable"); expect(vi.getTimerCount()).toBe(0); }); @@ -56,7 +56,7 @@ describe("TransportHttp", () => { ); await expect( - new TransportHttp("https://example.com").request(payload), + new JsonRpcTransportHttp("https://example.com").request(payload), ).rejects.toThrow("invalid JSON"); expect(vi.getTimerCount()).toBe(0); }); @@ -75,9 +75,10 @@ describe("TransportHttp", () => { ), ); - const request = new TransportHttp("https://example.com", 1000).request( - payload, - ); + const request = new JsonRpcTransportHttp( + "https://example.com", + 1000, + ).request(payload); const rejection = expect(request).rejects.toThrow("aborted"); await vi.advanceTimersByTimeAsync(1000); diff --git a/packages/core/src/jsonRpc/transports/http.ts b/packages/core/src/jsonRpc/transports/http.ts index b6e2eac6..b17a824b 100644 --- a/packages/core/src/jsonRpc/transports/http.ts +++ b/packages/core/src/jsonRpc/transports/http.ts @@ -1,6 +1,10 @@ -import { JsonRpcPayload, JsonRpcResponse, Transport } from "./transport.js"; +import { + JsonRpcPayload, + JsonRpcResponse, + JsonRpcTransport, +} from "./transport.js"; -export class TransportHttp implements Transport { +export class JsonRpcTransportHttp implements JsonRpcTransport { constructor( private readonly url: string, private readonly timeout = 30000, diff --git a/packages/core/src/jsonRpc/transports/transport.ts b/packages/core/src/jsonRpc/transports/transport.ts index 682a8bca..9b011d02 100644 --- a/packages/core/src/jsonRpc/transports/transport.ts +++ b/packages/core/src/jsonRpc/transports/transport.ts @@ -19,14 +19,14 @@ export type JsonRpcResponse = { | { result?: never; error: JsonRpcError } ); -export interface Transport { +export interface JsonRpcTransport { /** * Sends a JSON-RPC request to the server. * * @param payload - The JSON-RPC payload to send. * @returns The JSON-RPC response. */ - request(data: JsonRpcPayload): Promise; + request(payload: JsonRpcPayload): Promise; /** Releases resources held by the transport. */ close(): Promise; diff --git a/packages/core/src/jsonRpc/transports/webSocket.test.ts b/packages/core/src/jsonRpc/transports/webSocket.test.ts index 2cc219aa..7f0838c6 100644 --- a/packages/core/src/jsonRpc/transports/webSocket.test.ts +++ b/packages/core/src/jsonRpc/transports/webSocket.test.ts @@ -71,9 +71,9 @@ vi.mock("isomorphic-ws", () => { return { default: WebSocket }; }); -import { TransportWebSocket } from "./webSocket.js"; +import { JsonRpcTransportWebSocket } from "./webSocket.js"; -describe("TransportWebSocket", () => { +describe("JsonRpcTransportWebSocket", () => { beforeEach(() => { mock.deferOpen = false; mock.invalidResponse = false; @@ -83,7 +83,7 @@ describe("TransportWebSocket", () => { }); it("closes its socket", async () => { - const transport = new TransportWebSocket("ws://example.com"); + const transport = new JsonRpcTransportWebSocket("ws://example.com"); await transport.request({ id: 0, jsonrpc: "2.0", @@ -101,7 +101,7 @@ describe("TransportWebSocket", () => { it("ignores invalid JSON until the request times out", async () => { vi.useFakeTimers(); mock.invalidResponse = true; - const transport = new TransportWebSocket("ws://example.com", 100); + const transport = new JsonRpcTransportWebSocket("ws://example.com", 100); const request = expect( transport.request({ @@ -122,7 +122,7 @@ describe("TransportWebSocket", () => { vi.useFakeTimers(); const error = new Error("send failed"); mock.sendError = error; - const transport = new TransportWebSocket("ws://example.com", 100); + const transport = new JsonRpcTransportWebSocket("ws://example.com", 100); await expect( transport.request({ @@ -141,7 +141,7 @@ describe("TransportWebSocket", () => { it("closes a connecting socket without sending after timeout", async () => { vi.useFakeTimers(); mock.deferOpen = true; - const transport = new TransportWebSocket("ws://example.com", 100); + const transport = new JsonRpcTransportWebSocket("ws://example.com", 100); const request = expect( transport.request({ diff --git a/packages/core/src/jsonRpc/transports/webSocket.ts b/packages/core/src/jsonRpc/transports/webSocket.ts index 1839c9bd..61285446 100644 --- a/packages/core/src/jsonRpc/transports/webSocket.ts +++ b/packages/core/src/jsonRpc/transports/webSocket.ts @@ -1,7 +1,11 @@ import WebSocket from "isomorphic-ws"; -import { JsonRpcPayload, JsonRpcResponse, Transport } from "./transport.js"; +import { + JsonRpcPayload, + JsonRpcResponse, + JsonRpcTransport, +} from "./transport.js"; -export class TransportWebSocket implements Transport { +export class JsonRpcTransportWebSocket implements JsonRpcTransport { private ongoing: Map< number, [