From bd33563e40e40464d94028f567f76bb5b297bd22 Mon Sep 17 00:00:00 2001 From: shanejonas Date: Tue, 14 Jul 2020 10:03:56 -0700 Subject: [PATCH 1/8] feat: change public `request` interface to match provider BREAKING --- README.md | 2 +- package.json | 2 +- src/Error.ts | 4 +++- src/ProviderInterface.ts | 14 ++++++++++++++ src/RequestManager.test.ts | 24 ++++++++++++------------ src/RequestManager.ts | 7 ++++--- src/example.ts | 2 +- src/index.test.ts | 4 ++-- src/index.ts | 32 ++++++++++++++++---------------- 9 files changed, 54 insertions(+), 37 deletions(-) create mode 100644 src/ProviderInterface.ts diff --git a/README.md b/README.md index 2d6db0f..13055fe 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ A browser-compatible JSON-RPC client with multiple transports: import { RequestManager, HTTPTransport, Client } from "@open-rpc/client-js"; const transport = new HTTPTransport("http://localhost:8545"); const client = new Client(new RequestManager([transport])); -const result = await client.request("addition", [2, 2]); +const result = await client.request({method: "addition", params: [2, 2]}); // => { jsonrpc: '2.0', id: 1, result: 4 } ``` diff --git a/package.json b/package.json index 2e49366..f763d49 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": " A browser-compatible JSON-RPC client with multiple transports.", "main": "build/index.js", "scripts": { - "build": "tsc && typedoc --out docs", + "build": "tsc && typedoc --out docs --includeDeclarations --excludeExternals --excludeNotExported --excludePrivate --hideGenerator --mode file --readme none", "lint": "tslint --fix -p .", "test": "npm run lint && jest --coverage" }, diff --git a/src/Error.ts b/src/Error.ts index 355e8dc..4943883 100644 --- a/src/Error.ts +++ b/src/Error.ts @@ -1,8 +1,10 @@ +import { ProviderRpcError } from "./ProviderInterface"; + export const ERR_TIMEOUT = 7777; export const ERR_MISSIING_ID = 7878; export const ERR_UNKNOWN = 7979; -export class JSONRPCError extends Error { +export class JSONRPCError extends Error implements ProviderRpcError{ public message: string; public code: number; public data: any; diff --git a/src/ProviderInterface.ts b/src/ProviderInterface.ts new file mode 100644 index 0000000..09a3e42 --- /dev/null +++ b/src/ProviderInterface.ts @@ -0,0 +1,14 @@ +export interface ProviderRequestArguments { + readonly method: string; + readonly params?: readonly unknown[] | object; +} + +export interface Provider { + request(args: ProviderRequestArguments): Promise; +} + +export interface ProviderRpcError extends Error { + message: string; + code: number; + data?: unknown; +} diff --git a/src/RequestManager.test.ts b/src/RequestManager.test.ts index 5ac31e0..07b010d 100644 --- a/src/RequestManager.test.ts +++ b/src/RequestManager.test.ts @@ -25,7 +25,7 @@ describe("client-js", () => { addMockServerTransport(emitter, "from1", "to1://local/rpc-response"); const transport = new EventEmitterTransport(emitter, "from1", "to1://local/rpc-response"); const c = new RequestManager([transport]); - const result = await c.request("foo", ["bar"]); + const result = await c.request({ method: "foo", params: ["bar"] }); expect(result.method).toEqual("foo"); expect(result.params).toEqual(["bar"]); }); @@ -35,8 +35,8 @@ describe("client-js", () => { addMockServerTransport(emitter, "from1", "to1://local/rpc-error"); const transport = new EventEmitterTransport(emitter, "from1", "to1://local/rpc-error"); const c = new RequestManager([transport]); - await expect(c.request("foo", ["bar"])).rejects.toThrowError("Error message"); - }); + await expect(c.request({ method: "foo", params: ["bar"] })).rejects.toThrowError("Error message"); + }); it("can error on malformed response and recieve error", async () => { const emitter = new EventEmitter(); @@ -48,7 +48,7 @@ describe("client-js", () => { resolve(d); }); }); - await expect(c.request("foo", ["bar"], false, 1000)) + await expect(c.request({ method: "foo", params: ["bar"] }, false, 1000)) .rejects.toThrowError("Request timeout request took longer than 1000 ms to resolve"); const formatError = await unknownError as JSONRPCError; expect(formatError.message).toContain("Bad response format"); @@ -69,8 +69,8 @@ describe("client-js", () => { const c = new RequestManager([transport]); c.startBatch(); const requests = [ - c.request("foo", ["bar"]), - c.request("foo", ["bar"]), + c.request({ method: "foo", params: ["bar"] }), + c.request({ method: "foo", params: ["bar"] }), ]; c.stopBatch(); await expect(Promise.all(requests)).rejects.toThrowError("Error message"); @@ -85,8 +85,8 @@ describe("client-js", () => { const c = new RequestManager([transport]); c.startBatch(); const requests = [ - c.request("foo", []), - c.request("foo", ["bar"]), + c.request({ method: "foo", params: [] }), + c.request({ method: "foo", params: ["bar"] }) ]; c.stopBatch(); const [a, b] = await Promise.all(requests); @@ -105,8 +105,8 @@ describe("client-js", () => { const c = new RequestManager([transport]); c.startBatch(); const requests = [ - c.request("foo", [], true), - c.request("foo", ["bar"], true), + c.request({ method: "foo", params: [] }, true), + c.request({ method: "foo", params: ["bar"] }, true), ]; c.stopBatch(); const [a, b] = await Promise.all(requests); @@ -131,10 +131,10 @@ describe("client-js", () => { const c = new RequestManager([transport]); await c.connect(); c.startBatch(); - c.request("foo", []); + c.request({ method: "foo", params: [] }); expect(c.batch.length).toBe(1); c.startBatch(); - c.request("foo", []); + c.request({ method: "foo", params: [] }); expect(c.batch.length).toBe(2); }); }); diff --git a/src/RequestManager.ts b/src/RequestManager.ts index e1e48b2..f8e03ac 100644 --- a/src/RequestManager.ts +++ b/src/RequestManager.ts @@ -3,6 +3,7 @@ import { IJSONRPCRequest, IJSONRPCNotification, IBatchRequest } from "./Request" import { JSONRPCError } from "./Error"; import StrictEventEmitter from "strict-event-emitter-types"; import { EventEmitter } from "events"; +import { ProviderRequestArguments, Provider } from "./ProviderInterface"; export type RequestChannel = StrictEventEmitter; @@ -16,7 +17,7 @@ export interface IRequestEvents { * If a transport fails, or times out, move on to the next. */ -class RequestManager { +class RequestManager implements Provider { public transports: Transport[]; public connectPromise: Promise; public batch: IBatchRequest[] = []; @@ -43,11 +44,11 @@ class RequestManager { return this.transports[0]; } - public async request(method: string, params: any[], notification: boolean = false, timeout?: number): Promise { + public async request(args: ProviderRequestArguments, notification: boolean = false, timeout?: number): Promise { const internalID = (++this.lastId).toString(); const id = notification ? null : internalID; // naively grab first transport and use it - const payload = {request: this.makeRequest(method, params, id) , internalID}; + const payload = {request: this.makeRequest(args.method, args.params || [], id) , internalID}; if (this.batchStarted) { const result = new Promise((resolve, reject) => { this.batch.push({ resolve, reject, request: payload }); diff --git a/src/example.ts b/src/example.ts index b0d8d5e..948a6dc 100644 --- a/src/example.ts +++ b/src/example.ts @@ -2,6 +2,6 @@ import { Client, RequestManager, HTTPTransport } from "."; const t = new HTTPTransport("http://localhost:3333"); const c = new Client(new RequestManager([t])); -c.request("addition", [2, 2]).then((result: any) => { +c.request({method: "addition", params: [2, 2]}).then((result: any) => { console.log('addition result: ', result); // tslint:disable-line }); diff --git a/src/index.test.ts b/src/index.test.ts index a003162..1bdaeea 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -16,14 +16,14 @@ describe("client-js", () => { const emitter = new EventEmitter(); const c = new Client(new RequestManager([new EventEmitterTransport(emitter, "from1", "to1")])); expect(typeof c.request).toEqual("function"); - expect(typeof c.request("my_method", null).then).toEqual("function"); + expect(typeof c.request({ method: "my_method"}).then).toEqual("function"); }); it("has a notify method that returns a promise", () => { const emitter = new EventEmitter(); const c = new Client(new RequestManager([new EventEmitterTransport(emitter, "from1", "to1")])); expect(typeof c.request).toEqual("function"); - expect(typeof c.notify("my_method", null).then).toEqual("function"); + expect(typeof c.notify({method: "my_method"}).then).toEqual("function"); }); it("can recieve notifications", (done) => { diff --git a/src/index.ts b/src/index.ts index afa7c33..9f17b8e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,10 +5,7 @@ import WebSocketTransport from "./transports/WebSocketTransport"; import PostMessageWindowTransport from "./transports/PostMessageWindowTransport"; import PostMessageIframeTransport from "./transports/PostMessageIframeTransport"; import { JSONRPCError } from "./Error"; - -interface IClient { - request(method: string, params: any): Promise; -} +import { Provider, ProviderRequestArguments } from "./ProviderInterface"; /** * OpenRPC Client JS is a browser-compatible JSON-RPC client with multiple transports and @@ -19,12 +16,12 @@ interface IClient { * import { RequestManager, HTTPTransport, Client } from '@open-rpc/client-js'; * const transport = new HTTPTransport('http://localhost:3333'); * const client = new Client(new RequestManager([transport])); - * const result = await client.request(‘addition’, [2, 2]); + * const result = await client.request({method: 'addition', params: [2, 2]}); * // => { jsonrpc: '2.0', id: 1, result: 4 } * ``` * */ -class Client implements IClient { +class Client implements Provider { public requestManager: RequestManager; constructor(requestManager: RequestManager) { this.requestManager = requestManager; @@ -39,8 +36,8 @@ class Client implements IClient { * * @example * myClient.startBatch(); - * myClient.request("foo", ["bar"]).then(() => console.log('foobar')); - * myClient.request("foo", ["baz"]).then(() => console.log('foobaz')); + * myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar')); + * myClient.request({method: "foo", params: ["baz"]}).then(() => console.log('foobaz')); * myClient.stopBatch(); */ public startBatch(): void { @@ -55,8 +52,8 @@ class Client implements IClient { * * @example * myClient.startBatch(); - * myClient.request("foo", ["bar"]).then(() => console.log('foobar')); - * myClient.request("foo", ["baz"]).then(() => console.log('foobaz')); + * myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar')); + * myClient.request({method: "foo", params: ["baz"]}).then(() => console.log('foobaz')); * myClient.stopBatch(); */ public stopBatch(): void { @@ -66,19 +63,22 @@ class Client implements IClient { /** * A JSON-RPC call is represented by sending a Request object to a Server. * - * @param method A String containing the name of the method to be invoked. Method names that begin with the word rpc + * @param requestObject.method A String containing the name of the method to be invoked. Method names that begin with the word rpc * followed by a period character (U+002E or ASCII 46) are reserved for rpc-internal methods and extensions and * MUST NOT be used for anything else. - * @param params A Structured value that holds the parameter values to be used during the invocation of the method. + * @param requestObject.params A Structured value that holds the parameter values to be used during the invocation of the method. + * + * @example + * myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar')); */ - public async request(method: string, params: any, timeout?: number) { + public async request(requestObject: ProviderRequestArguments, timeout?: number) { await this.requestManager.connectPromise; - return this.requestManager.request(method, params, false, timeout); + return this.requestManager.request(requestObject, false, timeout); } - public async notify(method: string, params: any) { + public async notify(requestObject: ProviderRequestArguments) { await this.requestManager.connectPromise; - return this.requestManager.request(method, params, true); + return this.requestManager.request(requestObject, true); } public onNotification(callback: (data: any) => void) { From 21243827b0f6a8b035a117c86ef6a4946fef442b Mon Sep 17 00:00:00 2001 From: shanejonas Date: Tue, 14 Jul 2020 11:06:34 -0700 Subject: [PATCH 2/8] fix(provider): type --- src/ProviderInterface.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ProviderInterface.ts b/src/ProviderInterface.ts index 09a3e42..7fb30a9 100644 --- a/src/ProviderInterface.ts +++ b/src/ProviderInterface.ts @@ -4,7 +4,7 @@ export interface ProviderRequestArguments { } export interface Provider { - request(args: ProviderRequestArguments): Promise; + request(args: ProviderRequestArguments): Promise; } export interface ProviderRpcError extends Error { From 3bfe84981aa6e37395a732269085d744cef05f68 Mon Sep 17 00:00:00 2001 From: shanejonas Date: Wed, 22 Jul 2020 15:30:35 -0700 Subject: [PATCH 3/8] fix(provider): rename to ClientInterface --- src/ClientInterface.ts | 21 +++++++++++++++++++++ src/Error.ts | 4 ++-- src/ProviderInterface.ts | 14 -------------- src/RequestManager.ts | 8 ++++---- src/index.ts | 16 ++++++++++------ 5 files changed, 37 insertions(+), 26 deletions(-) create mode 100644 src/ClientInterface.ts delete mode 100644 src/ProviderInterface.ts diff --git a/src/ClientInterface.ts b/src/ClientInterface.ts new file mode 100644 index 0000000..d3fb25e --- /dev/null +++ b/src/ClientInterface.ts @@ -0,0 +1,21 @@ +interface Arguments { + readonly method: string; + readonly params?: readonly unknown[] | object; +} + +export type RequestArguments = Arguments; + +export type NotificationArguments = Arguments; + +export type JSONRPCMessage = RequestArguments | NotificationArguments; + +export interface IClient { + request(args: RequestArguments): Promise; + notify(args: NotificationArguments): Promise; +} + +export interface JSONRpcError extends Error { + message: string; + code: number; + data?: unknown; +} diff --git a/src/Error.ts b/src/Error.ts index 4943883..6527226 100644 --- a/src/Error.ts +++ b/src/Error.ts @@ -1,10 +1,10 @@ -import { ProviderRpcError } from "./ProviderInterface"; +import { JSONRpcError } from "./ClientInterface"; export const ERR_TIMEOUT = 7777; export const ERR_MISSIING_ID = 7878; export const ERR_UNKNOWN = 7979; -export class JSONRPCError extends Error implements ProviderRpcError{ +export class JSONRPCError extends Error implements JSONRpcError { public message: string; public code: number; public data: any; diff --git a/src/ProviderInterface.ts b/src/ProviderInterface.ts deleted file mode 100644 index 7fb30a9..0000000 --- a/src/ProviderInterface.ts +++ /dev/null @@ -1,14 +0,0 @@ -export interface ProviderRequestArguments { - readonly method: string; - readonly params?: readonly unknown[] | object; -} - -export interface Provider { - request(args: ProviderRequestArguments): Promise; -} - -export interface ProviderRpcError extends Error { - message: string; - code: number; - data?: unknown; -} diff --git a/src/RequestManager.ts b/src/RequestManager.ts index f8e03ac..a16668f 100644 --- a/src/RequestManager.ts +++ b/src/RequestManager.ts @@ -3,7 +3,7 @@ import { IJSONRPCRequest, IJSONRPCNotification, IBatchRequest } from "./Request" import { JSONRPCError } from "./Error"; import StrictEventEmitter from "strict-event-emitter-types"; import { EventEmitter } from "events"; -import { ProviderRequestArguments, Provider } from "./ProviderInterface"; +import { RequestArguments } from "./ClientInterface"; export type RequestChannel = StrictEventEmitter; @@ -17,7 +17,7 @@ export interface IRequestEvents { * If a transport fails, or times out, move on to the next. */ -class RequestManager implements Provider { +class RequestManager { public transports: Transport[]; public connectPromise: Promise; public batch: IBatchRequest[] = []; @@ -44,11 +44,11 @@ class RequestManager implements Provider { return this.transports[0]; } - public async request(args: ProviderRequestArguments, notification: boolean = false, timeout?: number): Promise { + public async request(requestObject: RequestArguments, notification: boolean = false, timeout?: number): Promise { const internalID = (++this.lastId).toString(); const id = notification ? null : internalID; // naively grab first transport and use it - const payload = {request: this.makeRequest(args.method, args.params || [], id) , internalID}; + const payload = {request: this.makeRequest(requestObject.method, requestObject.params || [], id) , internalID}; if (this.batchStarted) { const result = new Promise((resolve, reject) => { this.batch.push({ resolve, reject, request: payload }); diff --git a/src/index.ts b/src/index.ts index 9f17b8e..a37f5af 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,7 @@ import WebSocketTransport from "./transports/WebSocketTransport"; import PostMessageWindowTransport from "./transports/PostMessageWindowTransport"; import PostMessageIframeTransport from "./transports/PostMessageIframeTransport"; import { JSONRPCError } from "./Error"; -import { Provider, ProviderRequestArguments } from "./ProviderInterface"; +import { IClient, RequestArguments, NotificationArguments } from "./ClientInterface"; /** * OpenRPC Client JS is a browser-compatible JSON-RPC client with multiple transports and @@ -21,7 +21,7 @@ import { Provider, ProviderRequestArguments } from "./ProviderInterface"; * ``` * */ -class Client implements Provider { +class Client implements IClient { public requestManager: RequestManager; constructor(requestManager: RequestManager) { this.requestManager = requestManager; @@ -71,13 +71,17 @@ class Client implements Provider { * @example * myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar')); */ - public async request(requestObject: ProviderRequestArguments, timeout?: number) { - await this.requestManager.connectPromise; + public async request(requestObject: RequestArguments, timeout?: number) { + if (this.requestManager.connectPromise) { + await this.requestManager.connectPromise; + } return this.requestManager.request(requestObject, false, timeout); } - public async notify(requestObject: ProviderRequestArguments) { - await this.requestManager.connectPromise; + public async notify(requestObject: NotificationArguments) { + if (this.requestManager.connectPromise) { + await this.requestManager.connectPromise; + } return this.requestManager.request(requestObject, true); } From be1669fc548b5e542c2b92a24730dfeecbefc4fe Mon Sep 17 00:00:00 2001 From: shanejonas Date: Fri, 24 Jul 2020 10:39:47 -0700 Subject: [PATCH 4/8] fix: remove duplicate JSONRpcError type --- src/ClientInterface.ts | 6 ------ src/Error.ts | 6 ++---- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/ClientInterface.ts b/src/ClientInterface.ts index d3fb25e..2c40f34 100644 --- a/src/ClientInterface.ts +++ b/src/ClientInterface.ts @@ -13,9 +13,3 @@ export interface IClient { request(args: RequestArguments): Promise; notify(args: NotificationArguments): Promise; } - -export interface JSONRpcError extends Error { - message: string; - code: number; - data?: unknown; -} diff --git a/src/Error.ts b/src/Error.ts index 6527226..2bf30ce 100644 --- a/src/Error.ts +++ b/src/Error.ts @@ -1,13 +1,11 @@ -import { JSONRpcError } from "./ClientInterface"; - export const ERR_TIMEOUT = 7777; export const ERR_MISSIING_ID = 7878; export const ERR_UNKNOWN = 7979; -export class JSONRPCError extends Error implements JSONRpcError { +export class JSONRPCError extends Error { public message: string; public code: number; - public data: any; + public data?: unknown; constructor(message: string, code: number, data?: any) { super(message); this.message = message; From 7f7c94cd793ca7441087ef1ddc91db8bef87205c Mon Sep 17 00:00:00 2001 From: shanejonas Date: Fri, 24 Jul 2020 12:24:55 -0700 Subject: [PATCH 5/8] fix(typdoc): add typedoc.jsosn and use library mode --- package-lock.json | 74 ++++++++++++++++++++++++++------------ package.json | 4 +-- src/Client.ts | 92 +++++++++++++++++++++++++++++++++++++++++++++++ src/index.test.ts | 6 ++-- src/index.ts | 90 +--------------------------------------------- typedoc.json | 5 +++ 6 files changed, 155 insertions(+), 116 deletions(-) create mode 100644 src/Client.ts create mode 100644 typedoc.json diff --git a/package-lock.json b/package-lock.json index 9af069c..c6feead 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1318,6 +1318,12 @@ "pretty-format": "^25.2.1" } }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, "@types/node": { "version": "12.6.8", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz", @@ -1678,6 +1684,15 @@ "babel-preset-current-node-syntax": "^0.1.2" } }, + "backbone": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/backbone/-/backbone-1.4.0.tgz", + "integrity": "sha512-RLmDrRXkVdouTg38jcgHhyQ/2zjg7a8E6sz2zxfz21Hh17xDJYUHBZimVIt5fUyS8vbfpeSmTL3gUjTEvUV3qQ==", + "dev": true, + "requires": { + "underscore": ">=1.8.3" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -2774,9 +2789,9 @@ } }, "highlight.js": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.1.1.tgz", - "integrity": "sha512-b4L09127uVa+9vkMgPpdUQP78ickGbHEQTWeBrQFTJZ4/n2aihWOGS0ZoUqAwjVmfjhq/C76HRzkqwZhK4sBbg==", + "version": "9.18.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.18.1.tgz", + "integrity": "sha512-OrVKYz70LHsnCgmbXctv/bfuvntIKDz177h0Co37DQ5jamGZLVmoCVMtjMtNZY3X9DrCcKfklHPNeA0uPZhSJg==", "dev": true }, "hosted-git-info": { @@ -5340,6 +5355,12 @@ } } }, + "jquery": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5554,9 +5575,9 @@ } }, "marked": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-1.0.0.tgz", - "integrity": "sha512-Wo+L1pWTVibfrSr+TTtMuiMfNzmZWiOPeO7rZsQUY5bgsxpHesBEcIWJloWVTFnrMXnf/TL30eTFSGJddmQAng==", + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", + "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==", "dev": true }, "merge-stream": { @@ -5673,9 +5694,9 @@ "dev": true }, "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true }, "nice-try": { @@ -7159,30 +7180,33 @@ } }, "typedoc": { - "version": "0.17.8", - "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.17.8.tgz", - "integrity": "sha512-/OyrHCJ8jtzu+QZ+771YaxQ9s4g5Z3XsQE3Ma7q+BL392xxBn4UMvvCdVnqKC2T/dz03/VXSLVKOP3lHmDdc/w==", + "version": "0.17.0-3", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.17.0-3.tgz", + "integrity": "sha512-DO2djkR4NHgzAWfNbJb2eQKsFMs+gOuYBXlQ8dOSCjkAK5DRI7ZywDufBGPUw7Ue9Qwi2Cw1DxLd3reDq8wFuQ==", "dev": true, "requires": { + "@types/minimatch": "3.0.3", "fs-extra": "^8.1.0", - "handlebars": "^4.7.6", - "highlight.js": "^10.0.0", + "handlebars": "^4.7.2", + "highlight.js": "^9.18.0", "lodash": "^4.17.15", - "lunr": "^2.3.8", - "marked": "1.0.0", + "marked": "^0.8.0", "minimatch": "^3.0.0", "progress": "^2.0.3", - "shelljs": "^0.8.4", - "typedoc-default-themes": "^0.10.2" + "shelljs": "^0.8.3", + "typedoc-default-themes": "0.8.0-0" } }, "typedoc-default-themes": { - "version": "0.10.2", - "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.10.2.tgz", - "integrity": "sha512-zo09yRj+xwLFE3hyhJeVHWRSPuKEIAsFK5r2u47KL/HBKqpwdUSanoaz5L34IKiSATFrjG5ywmIu98hPVMfxZg==", + "version": "0.8.0-0", + "resolved": "https://registry.npmjs.org/typedoc-default-themes/-/typedoc-default-themes-0.8.0-0.tgz", + "integrity": "sha512-blFWppm5aKnaPOa1tpGO9MLu+njxq7P3rtkXK4QxJBNszA+Jg7x0b+Qx0liXU1acErur6r/iZdrwxp5DUFdSXw==", "dev": true, "requires": { - "lunr": "^2.3.8" + "backbone": "^1.4.0", + "jquery": "^3.4.1", + "lunr": "^2.3.8", + "underscore": "^1.9.1" } }, "typescript": { @@ -7198,6 +7222,12 @@ "dev": true, "optional": true }, + "underscore": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==", + "dev": true + }, "union-value": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", diff --git a/package.json b/package.json index f763d49..91b79e5 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": " A browser-compatible JSON-RPC client with multiple transports.", "main": "build/index.js", "scripts": { - "build": "tsc && typedoc --out docs --includeDeclarations --excludeExternals --excludeNotExported --excludePrivate --hideGenerator --mode file --readme none", + "build": "tsc && typedoc src/index.ts", "lint": "tslint --fix -p .", "test": "npm run lint && jest --coverage" }, @@ -29,7 +29,7 @@ "serve-handler": "^6.1.3", "ts-jest": "^25.0.0", "tslint": "^6.0.0", - "typedoc": "^0.17.0", + "typedoc": "^0.17.0-3", "typescript": "^3.4.5" }, "dependencies": { diff --git a/src/Client.ts b/src/Client.ts new file mode 100644 index 0000000..24c5502 --- /dev/null +++ b/src/Client.ts @@ -0,0 +1,92 @@ +import RequestManager from "./RequestManager"; +import { JSONRPCError } from "./Error"; +import { IClient, RequestArguments, NotificationArguments } from "./ClientInterface"; + +/** + * OpenRPC Client JS is a browser-compatible JSON-RPC client with multiple transports and + * multiple request managers to enable features like round-robin or fallback-by-position. + * + * @example + * ```typescript + * import { RequestManager, HTTPTransport, Client } from '@open-rpc/client-js'; + * const transport = new HTTPTransport('http://localhost:3333'); + * const client = new Client(new RequestManager([transport])); + * const result = await client.request({method: 'addition', params: [2, 2]}); + * // => { jsonrpc: '2.0', id: 1, result: 4 } + * ``` + * + */ +class Client implements IClient { + public requestManager: RequestManager; + constructor(requestManager: RequestManager) { + this.requestManager = requestManager; + } + + /** + * Initiates [[RequestManager.startBatch]] in order to build a batch call. + * + * Subsequent calls to [[Client.request]] will be added to the batch. Once [[Client.stopBatch]] is called, the + * promises for the [[Client.request]] will then be resolved. If the [[RequestManager]] already has a batch in + * progress, this method is a noop. + * + * @example + * myClient.startBatch(); + * myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar')); + * myClient.request({method: "foo", params: ["baz"]}).then(() => console.log('foobaz')); + * myClient.stopBatch(); + */ + public startBatch(): void { + return this.requestManager.startBatch(); + } + + /** + * Initiates [[RequestManager.stopBatch]] in order to finalize and send the batch to the underlying transport. + * + * [[Client.stopBatch]] will send the [[Client.request]] calls made since the last [[Client.startBatch]] call. For + * that reason, [[Client.startBatch]] MUST be called before [[Client.stopBatch]]. + * + * @example + * myClient.startBatch(); + * myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar')); + * myClient.request({method: "foo", params: ["baz"]}).then(() => console.log('foobaz')); + * myClient.stopBatch(); + */ + public stopBatch(): void { + return this.requestManager.stopBatch(); + } + + /** + * A JSON-RPC call is represented by sending a Request object to a Server. + * + * @param requestObject.method A String containing the name of the method to be invoked. Method names that begin with the word rpc + * followed by a period character (U+002E or ASCII 46) are reserved for rpc-internal methods and extensions and + * MUST NOT be used for anything else. + * @param requestObject.params A Structured value that holds the parameter values to be used during the invocation of the method. + * + * @example + * myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar')); + */ + public async request(requestObject: RequestArguments, timeout?: number) { + if (this.requestManager.connectPromise) { + await this.requestManager.connectPromise; + } + return this.requestManager.request(requestObject, false, timeout); + } + + public async notify(requestObject: NotificationArguments) { + if (this.requestManager.connectPromise) { + await this.requestManager.connectPromise; + } + return this.requestManager.request(requestObject, true); + } + + public onNotification(callback: (data: any) => void) { + this.requestManager.requestChannel.addListener("notification", callback); + } + + public onError(callback: (data: JSONRPCError) => void) { + this.requestManager.requestChannel.addListener("error", callback); + } +} + +export default Client; diff --git a/src/index.test.ts b/src/index.test.ts index 1bdaeea..28b1a99 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,4 +1,4 @@ -import Client from "."; +import { Client } from "."; import RequestManager from "./RequestManager"; import EventEmitterTransport from "./transports/EventEmitterTransport"; import { EventEmitter } from "events"; @@ -16,14 +16,14 @@ describe("client-js", () => { const emitter = new EventEmitter(); const c = new Client(new RequestManager([new EventEmitterTransport(emitter, "from1", "to1")])); expect(typeof c.request).toEqual("function"); - expect(typeof c.request({ method: "my_method"}).then).toEqual("function"); + expect(typeof c.request({ method: "my_method" }).then).toEqual("function"); }); it("has a notify method that returns a promise", () => { const emitter = new EventEmitter(); const c = new Client(new RequestManager([new EventEmitterTransport(emitter, "from1", "to1")])); expect(typeof c.request).toEqual("function"); - expect(typeof c.notify({method: "my_method"}).then).toEqual("function"); + expect(typeof c.notify({ method: "my_method" }).then).toEqual("function"); }); it("can recieve notifications", (done) => { diff --git a/src/index.ts b/src/index.ts index a37f5af..82b317b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,96 +5,8 @@ import WebSocketTransport from "./transports/WebSocketTransport"; import PostMessageWindowTransport from "./transports/PostMessageWindowTransport"; import PostMessageIframeTransport from "./transports/PostMessageIframeTransport"; import { JSONRPCError } from "./Error"; -import { IClient, RequestArguments, NotificationArguments } from "./ClientInterface"; +import Client from "./Client"; -/** - * OpenRPC Client JS is a browser-compatible JSON-RPC client with multiple transports and - * multiple request managers to enable features like round-robin or fallback-by-position. - * - * @example - * ```typescript - * import { RequestManager, HTTPTransport, Client } from '@open-rpc/client-js'; - * const transport = new HTTPTransport('http://localhost:3333'); - * const client = new Client(new RequestManager([transport])); - * const result = await client.request({method: 'addition', params: [2, 2]}); - * // => { jsonrpc: '2.0', id: 1, result: 4 } - * ``` - * - */ -class Client implements IClient { - public requestManager: RequestManager; - constructor(requestManager: RequestManager) { - this.requestManager = requestManager; - } - - /** - * Initiates [[RequestManager.startBatch]] in order to build a batch call. - * - * Subsequent calls to [[Client.request]] will be added to the batch. Once [[Client.stopBatch]] is called, the - * promises for the [[Client.request]] will then be resolved. If the [[RequestManager]] already has a batch in - * progress, this method is a noop. - * - * @example - * myClient.startBatch(); - * myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar')); - * myClient.request({method: "foo", params: ["baz"]}).then(() => console.log('foobaz')); - * myClient.stopBatch(); - */ - public startBatch(): void { - return this.requestManager.startBatch(); - } - - /** - * Initiates [[RequestManager.stopBatch]] in order to finalize and send the batch to the underlying transport. - * - * [[Client.stopBatch]] will send the [[Client.request]] calls made since the last [[Client.startBatch]] call. For - * that reason, [[Client.startBatch]] MUST be called before [[Client.stopBatch]]. - * - * @example - * myClient.startBatch(); - * myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar')); - * myClient.request({method: "foo", params: ["baz"]}).then(() => console.log('foobaz')); - * myClient.stopBatch(); - */ - public stopBatch(): void { - return this.requestManager.stopBatch(); - } - - /** - * A JSON-RPC call is represented by sending a Request object to a Server. - * - * @param requestObject.method A String containing the name of the method to be invoked. Method names that begin with the word rpc - * followed by a period character (U+002E or ASCII 46) are reserved for rpc-internal methods and extensions and - * MUST NOT be used for anything else. - * @param requestObject.params A Structured value that holds the parameter values to be used during the invocation of the method. - * - * @example - * myClient.request({method: "foo", params: ["bar"]}).then(() => console.log('foobar')); - */ - public async request(requestObject: RequestArguments, timeout?: number) { - if (this.requestManager.connectPromise) { - await this.requestManager.connectPromise; - } - return this.requestManager.request(requestObject, false, timeout); - } - - public async notify(requestObject: NotificationArguments) { - if (this.requestManager.connectPromise) { - await this.requestManager.connectPromise; - } - return this.requestManager.request(requestObject, true); - } - - public onNotification(callback: (data: any) => void) { - this.requestManager.requestChannel.addListener("notification", callback); - } - - public onError(callback: (data: JSONRPCError) => void) { - this.requestManager.requestChannel.addListener("error", callback); - } -} - -export default Client; export { Client, RequestManager, diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 0000000..7326e6a --- /dev/null +++ b/typedoc.json @@ -0,0 +1,5 @@ +{ + "name": "@open-rpc/client-js", + "out": "docs", + "mode": "library" +} From d2b4dc2d4b7e12d38b6b9947467e3a11d7f6ca22 Mon Sep 17 00:00:00 2001 From: shanejonas Date: Fri, 24 Jul 2020 12:29:15 -0700 Subject: [PATCH 6/8] fix: add default export back --- src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.ts b/src/index.ts index 82b317b..b47516e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,6 +7,7 @@ import PostMessageIframeTransport from "./transports/PostMessageIframeTransport" import { JSONRPCError } from "./Error"; import Client from "./Client"; +export default Client; export { Client, RequestManager, From 6904bab4b534336778e117dd0a41abb22700b147 Mon Sep 17 00:00:00 2001 From: shanejonas Date: Fri, 24 Jul 2020 12:32:08 -0700 Subject: [PATCH 7/8] fix(README): change example interfaces --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 13055fe..55f3be8 100644 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ emitter.on(chan1, (jsonrpcRequest) => { }); const main = async () => { - const result = await client.request("addition", [2, 2]); + const result = await client.request({method: "addition", params: [2, 2]}); console.log(result); }; @@ -78,7 +78,7 @@ const requestManager = new RequestManager([transport]); const client = new Client(requestManager); const main = async () => { - const result = await client.request("addition", [2, 2]); + const result = await client.request({method: "addition", params: [2, 2]}); console.log(result); }; @@ -101,7 +101,7 @@ const requestManager = new RequestManager([transport]); const client = new Client(requestManager); const main = async () => { - const result = await client.request("addition", [2, 2]); + const result = await client.request({method: "addition", params: [2, 2]}); console.log(result); }; From 5a3c1949a7e03363c4c5809082ea304274817245 Mon Sep 17 00:00:00 2001 From: shanejonas Date: Mon, 27 Jul 2020 09:35:13 -0700 Subject: [PATCH 8/8] fix: request manager requestObject type should be JSONRPCMessage --- src/RequestManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/RequestManager.ts b/src/RequestManager.ts index a16668f..285f95c 100644 --- a/src/RequestManager.ts +++ b/src/RequestManager.ts @@ -3,7 +3,7 @@ import { IJSONRPCRequest, IJSONRPCNotification, IBatchRequest } from "./Request" import { JSONRPCError } from "./Error"; import StrictEventEmitter from "strict-event-emitter-types"; import { EventEmitter } from "events"; -import { RequestArguments } from "./ClientInterface"; +import { JSONRPCMessage } from "./ClientInterface"; export type RequestChannel = StrictEventEmitter; @@ -44,7 +44,7 @@ class RequestManager { return this.transports[0]; } - public async request(requestObject: RequestArguments, notification: boolean = false, timeout?: number): Promise { + public async request(requestObject: JSONRPCMessage, notification: boolean = false, timeout?: number): Promise { const internalID = (++this.lastId).toString(); const id = notification ? null : internalID; // naively grab first transport and use it