Merge pull request #248 from CosmWasm/247-fork-jsonrpc

Fork @iov/jsonrpc
This commit is contained in:
Simon Warta 2020-06-24 16:45:04 +02:00 committed by GitHub
commit 06142599ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 1461 additions and 35 deletions

2
NOTICE
View File

@ -19,6 +19,8 @@ on 2020-06-05.
The code in packages/tendermint-rpc and scripts/tendermint was forked from the folders packages/iov-tendermint-rpc and scripts/tendermint respectively of https://github.com/iov-one/iov-core at tag v2.5.0 on 2020-06-15.
The code in packages/json-rpc was forked from https://github.com/iov-one/iov-core/tree/v2.5.0/packages/iov-jsonrpc, with additional code from https://github.com/iov-one/iov-core/tree/v2.5.0/packages/iov-encoding on 2020-06-24.
Copyright 2018-2020 IOV SAS
Copyright 2020 Confio UO
Copyright 2020 Simon Warta

View File

@ -0,0 +1 @@
../../.eslintignore

3
packages/json-rpc/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
build/
dist/
docs/

View File

@ -0,0 +1,12 @@
# @cosmjs/json-rpc
[![npm version](https://img.shields.io/npm/v/@cosmjs/json-rpc.svg)](https://www.npmjs.com/package/@cosmjs/json-rpc)
This package provides a light framework for implementing a
[JSON-RPC 2.0 API](https://www.jsonrpc.org/specification).
## License
This package is part of the cosmjs repository, licensed under the Apache License
2.0 (see [NOTICE](https://github.com/CosmWasm/cosmjs/blob/master/NOTICE) and
[LICENSE](https://github.com/CosmWasm/cosmjs/blob/master/LICENSE)).

View File

@ -0,0 +1,26 @@
#!/usr/bin/env node
require("source-map-support").install();
const defaultSpecReporterConfig = require("../../jasmine-spec-reporter.config.json");
// setup Jasmine
const Jasmine = require("jasmine");
const jasmine = new Jasmine();
jasmine.loadConfig({
spec_dir: "build",
spec_files: ["**/*.spec.js"],
helpers: [],
random: false,
seed: null,
stopSpecOnExpectationFailure: false,
});
jasmine.jasmine.DEFAULT_TIMEOUT_INTERVAL = 15 * 1000;
// setup reporter
const { SpecReporter } = require("jasmine-spec-reporter");
const reporter = new SpecReporter({ ...defaultSpecReporterConfig });
// initialize and execute
jasmine.env.clearReporters();
jasmine.addReporter(reporter);
jasmine.execute();

View File

@ -0,0 +1,56 @@
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: ".",
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ["jasmine"],
// list of files / patterns to load in the browser
files: [
"dist/web/tests.js",
{
pattern: "dist/web/dummyservice.worker.js",
included: false,
served: true,
watched: false,
nocache: true,
},
],
client: {
jasmine: {
random: false,
timeoutInterval: 15000,
},
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ["progress", "kjhtml"],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ["Firefox"],
browserNoActivityTimeout: 30000,
// Keep brower open for debugging. This is overridden by yarn scripts
singleRun: false,
});
};

View File

@ -0,0 +1 @@
Directory used to trigger lerna package updates for all packages

View File

@ -0,0 +1,49 @@
{
"name": "@cosmjs/json-rpc",
"version": "0.20.0",
"description": "Framework for implementing a JSON-RPC 2.0 API",
"contributors": [
"IOV SAS <admin@iov.one>",
"Confio UO <hello@confio.tech>",
"Will Clark <willclarktech@users.noreply.github.com>"
],
"license": "Apache-2.0",
"main": "build/index.js",
"types": "types/index.d.ts",
"files": [
"build/",
"types/",
"*.md",
"!*.spec.*",
"!**/testdata/"
],
"repository": {
"type": "git",
"url": "https://github.com/CosmWasm/cosmjs/tree/master/packages/json-rpc"
},
"publishConfig": {
"access": "public"
},
"scripts": {
"docs": "shx rm -rf docs && typedoc --options typedoc.js",
"lint": "eslint --max-warnings 0 \"**/*.{js,ts}\"",
"lint-fix": "eslint --max-warnings 0 \"**/*.{js,ts}\" --fix",
"format": "prettier --write --loglevel warn \"./src/**/*.ts\"",
"format-text": "prettier --write --prose-wrap always --print-width 80 \"./*.md\"",
"test-node": "node jasmine-testrunner.js",
"test-edge": "yarn pack-web && karma start --single-run --browsers Edge",
"test-firefox": "yarn pack-web && karma start --single-run --browsers Firefox",
"test-chrome": "yarn pack-web && karma start --single-run --browsers ChromeHeadless",
"test-safari": "yarn pack-web && karma start --single-run --browsers Safari",
"test": "yarn build-or-skip && yarn test-node",
"move-types": "shx rm -r ./types/* && shx mv build/types/* ./types && rm -rf ./types/testdata && shx rm -f ./types/*.spec.d.ts",
"format-types": "prettier --write --loglevel warn \"./types/**/*.d.ts\"",
"build": "shx rm -rf ./build && tsc && tsc -p tsconfig.workers.json && yarn move-types && yarn format-types",
"build-or-skip": "[ -n \"$SKIP_BUILD\" ] || yarn build",
"pack-web": "yarn build-or-skip && webpack --mode development --config webpack.web.config.js"
},
"dependencies": {
"@iov/stream": "^2.3.2",
"xstream": "^11.10.0"
}
}

View File

@ -0,0 +1,125 @@
import { isJsonCompatibleArray, isJsonCompatibleDictionary, isJsonCompatibleValue } from "./compatibility";
describe("json", () => {
function sum(a: number, b: number): number {
return a + b;
}
describe("isJsonCompatibleValue", () => {
it("returns true for primitive types", () => {
expect(isJsonCompatibleValue(null)).toEqual(true);
expect(isJsonCompatibleValue(0)).toEqual(true);
expect(isJsonCompatibleValue(1)).toEqual(true);
expect(isJsonCompatibleValue("abc")).toEqual(true);
expect(isJsonCompatibleValue(true)).toEqual(true);
expect(isJsonCompatibleValue(false)).toEqual(true);
});
it("returns true for arrays", () => {
expect(isJsonCompatibleValue([1, 2, 3])).toEqual(true);
expect(isJsonCompatibleValue([1, "2", true, null])).toEqual(true);
expect(isJsonCompatibleValue([1, "2", true, null, [1, "2", true, null]])).toEqual(true);
expect(isJsonCompatibleValue([{ a: 123 }])).toEqual(true);
});
it("returns true for simple dicts", () => {
expect(isJsonCompatibleValue({ a: 123 })).toEqual(true);
expect(isJsonCompatibleValue({ a: "abc" })).toEqual(true);
expect(isJsonCompatibleValue({ a: true })).toEqual(true);
expect(isJsonCompatibleValue({ a: null })).toEqual(true);
});
it("returns true for dict with array", () => {
expect(isJsonCompatibleValue({ a: [1, 2, 3] })).toEqual(true);
expect(isJsonCompatibleValue({ a: [1, "2", true, null] })).toEqual(true);
});
it("returns true for nested dicts", () => {
expect(isJsonCompatibleValue({ a: { b: 123 } })).toEqual(true);
});
it("returns false for functions", () => {
expect(isJsonCompatibleValue(sum)).toEqual(false);
});
it("returns true for empty dicts", () => {
expect(isJsonCompatibleValue({})).toEqual(true);
});
});
describe("isJsonCompatibleArray", () => {
it("returns false for primitive types", () => {
expect(isJsonCompatibleArray(null)).toEqual(false);
expect(isJsonCompatibleArray(undefined)).toEqual(false);
expect(isJsonCompatibleArray(0)).toEqual(false);
expect(isJsonCompatibleArray(1)).toEqual(false);
expect(isJsonCompatibleArray("abc")).toEqual(false);
expect(isJsonCompatibleArray(true)).toEqual(false);
expect(isJsonCompatibleArray(false)).toEqual(false);
});
it("returns true for arrays", () => {
expect(isJsonCompatibleArray([1, 2, 3])).toEqual(true);
expect(isJsonCompatibleArray([1, "2", true, null])).toEqual(true);
expect(isJsonCompatibleArray([1, "2", true, null, [1, "2", true, null]])).toEqual(true);
expect(isJsonCompatibleArray([{ a: 123 }])).toEqual(true);
});
it("returns false for dicts", () => {
expect(isJsonCompatibleArray({ a: 123 })).toEqual(false);
expect(isJsonCompatibleArray({ a: "abc" })).toEqual(false);
expect(isJsonCompatibleArray({ a: true })).toEqual(false);
expect(isJsonCompatibleArray({ a: null })).toEqual(false);
});
it("returns false for functions", () => {
expect(isJsonCompatibleArray(sum)).toEqual(false);
});
});
describe("isJsonCompatibleDictionary", () => {
it("returns false for primitive types", () => {
expect(isJsonCompatibleDictionary(null)).toEqual(false);
expect(isJsonCompatibleDictionary(undefined)).toEqual(false);
expect(isJsonCompatibleDictionary(0)).toEqual(false);
expect(isJsonCompatibleDictionary(1)).toEqual(false);
expect(isJsonCompatibleDictionary("abc")).toEqual(false);
expect(isJsonCompatibleDictionary(true)).toEqual(false);
expect(isJsonCompatibleDictionary(false)).toEqual(false);
});
it("returns false for other objects", () => {
expect(isJsonCompatibleDictionary(new Uint8Array([0x00]))).toEqual(false);
expect(isJsonCompatibleDictionary(/123/)).toEqual(false);
expect(isJsonCompatibleDictionary(new Date())).toEqual(false);
});
it("returns false for arrays", () => {
expect(isJsonCompatibleDictionary([1, 2, 3])).toEqual(false);
});
it("returns false for functions", () => {
expect(isJsonCompatibleDictionary(sum)).toEqual(false);
});
it("returns true for empty dicts", () => {
expect(isJsonCompatibleDictionary({})).toEqual(true);
});
it("returns true for simple dicts", () => {
expect(isJsonCompatibleDictionary({ a: 123 })).toEqual(true);
expect(isJsonCompatibleDictionary({ a: "abc" })).toEqual(true);
expect(isJsonCompatibleDictionary({ a: true })).toEqual(true);
expect(isJsonCompatibleDictionary({ a: null })).toEqual(true);
});
it("returns true for dict with array", () => {
expect(isJsonCompatibleDictionary({ a: [1, 2, 3] })).toEqual(true);
expect(isJsonCompatibleDictionary({ a: [1, "2", true, null] })).toEqual(true);
});
it("returns true for nested dicts", () => {
expect(isJsonCompatibleDictionary({ a: { b: 123 } })).toEqual(true);
});
});
});

View File

@ -0,0 +1,73 @@
/**
* A single JSON value. This is the missing return type of JSON.parse().
*/
export type JsonCompatibleValue =
| JsonCompatibleDictionary
| JsonCompatibleArray
| string
| number
| boolean
| null;
/**
* An array of JsonCompatibleValue
*/
// Use interface extension instead of type alias to make circular declaration possible.
export interface JsonCompatibleArray extends ReadonlyArray<JsonCompatibleValue> {}
/**
* A string to json value dictionary.
*/
export interface JsonCompatibleDictionary {
readonly [key: string]: JsonCompatibleValue | readonly JsonCompatibleValue[];
}
export function isJsonCompatibleValue(value: unknown): value is JsonCompatibleValue {
if (
typeof value === "string" ||
typeof value === "number" ||
typeof value === "boolean" ||
value === null ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
isJsonCompatibleArray(value) ||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
isJsonCompatibleDictionary(value)
) {
return true;
} else {
return false;
}
}
export function isJsonCompatibleArray(value: unknown): value is JsonCompatibleArray {
if (!Array.isArray(value)) {
return false;
}
for (const item of value) {
if (!isJsonCompatibleValue(item)) {
return false;
}
}
// all items okay
return true;
}
export function isJsonCompatibleDictionary(data: unknown): data is JsonCompatibleDictionary {
if (typeof data !== "object" || data === null) {
// data must be a non-null object
return false;
}
// Exclude special kind of objects like Array, Date or Uint8Array
// Object.prototype.toString() returns a specified value:
// http://www.ecma-international.org/ecma-262/7.0/index.html#sec-object.prototype.tostring
if (Object.prototype.toString.call(data) !== "[object Object]") {
return false;
}
// TODO: replace with Object.values when available (ES2017+)
const values = Object.getOwnPropertyNames(data).map((key) => (data as any)[key]);
return values.every(isJsonCompatibleValue);
}

View File

@ -0,0 +1,21 @@
import { makeJsonRpcId } from "./id";
describe("id", () => {
describe("makeJsonRpcId", () => {
it("returns a string or number", () => {
const id = makeJsonRpcId();
expect(["string", "number"]).toContain(typeof id);
});
it("returns unique values", () => {
const ids = new Set([
makeJsonRpcId(),
makeJsonRpcId(),
makeJsonRpcId(),
makeJsonRpcId(),
makeJsonRpcId(),
]);
expect(ids.size).toEqual(5);
});
});
});

View File

@ -0,0 +1,13 @@
// Start with 10001 to avoid possible collisions with all hand-selected values like e.g. 1,2,3,42,100
let counter = 10000;
/**
* Creates a new ID to be used for creating a JSON-RPC request.
*
* Multiple calls of this produce unique values.
*
* The output may be any value compatible to JSON-RPC request IDs with an undefined output format and generation logic.
*/
export function makeJsonRpcId(): number {
return (counter += 1);
}

View File

@ -0,0 +1,20 @@
export { makeJsonRpcId } from "./id";
export { JsonRpcClient, SimpleMessagingConnection } from "./jsonrpcclient";
export {
parseJsonRpcId,
parseJsonRpcRequest,
parseJsonRpcResponse,
parseJsonRpcErrorResponse,
parseJsonRpcSuccessResponse,
} from "./parse";
export {
isJsonRpcErrorResponse,
isJsonRpcSuccessResponse,
JsonRpcError,
JsonRpcErrorResponse,
JsonRpcId,
JsonRpcRequest,
JsonRpcResponse,
JsonRpcSuccessResponse,
jsonRpcCode,
} from "./types";

View File

@ -0,0 +1,86 @@
/// <reference lib="dom" />
import { Producer, Stream } from "xstream";
import { JsonRpcClient, SimpleMessagingConnection } from "./jsonrpcclient";
import { parseJsonRpcResponse } from "./parse";
import { JsonRpcRequest, JsonRpcResponse } from "./types";
function pendingWithoutWorker(): void {
if (typeof Worker === "undefined") {
pending("Environment without WebWorker support detected. Marked as pending.");
}
}
function makeSimpleMessagingConnection(
worker: Worker,
): SimpleMessagingConnection<JsonRpcRequest, JsonRpcResponse> {
const producer: Producer<JsonRpcResponse> = {
start: (listener) => {
worker.onmessage = (event) => {
listener.next(parseJsonRpcResponse(event.data));
};
},
stop: () => {
worker.onmessage = null;
},
};
return {
responseStream: Stream.create(producer),
sendRequest: (request) => worker.postMessage(request),
};
}
describe("JsonRpcClient", () => {
const dummyserviceKarmaUrl = "/base/dist/web/dummyservice.worker.js";
it("can be constructed with a Worker", () => {
pendingWithoutWorker();
const worker = new Worker(dummyserviceKarmaUrl);
const client = new JsonRpcClient(makeSimpleMessagingConnection(worker));
expect(client).toBeTruthy();
worker.terminate();
});
it("can communicate with worker", async () => {
pendingWithoutWorker();
const worker = new Worker(dummyserviceKarmaUrl);
const client = new JsonRpcClient(makeSimpleMessagingConnection(worker));
const response = await client.run({
jsonrpc: "2.0",
id: 123,
method: "getIdentities",
params: ["Who are you?"],
});
expect(response.jsonrpc).toEqual("2.0");
expect(response.id).toEqual(123);
expect(response.result).toEqual(`Called getIdentities("Who are you?")`);
worker.terminate();
});
it("supports params as dictionary", async () => {
pendingWithoutWorker();
const worker = new Worker(dummyserviceKarmaUrl);
const client = new JsonRpcClient(makeSimpleMessagingConnection(worker));
const response = await client.run({
jsonrpc: "2.0",
id: 123,
method: "getIdentities",
params: {
a: "Who are you?",
},
});
expect(response.jsonrpc).toEqual("2.0");
expect(response.id).toEqual(123);
expect(response.result).toEqual(`Called getIdentities({"a":"Who are you?"})`);
worker.terminate();
});
});

View File

@ -0,0 +1,37 @@
import { firstEvent } from "@iov/stream";
import { Stream } from "xstream";
import { isJsonRpcErrorResponse, JsonRpcRequest, JsonRpcResponse, JsonRpcSuccessResponse } from "./types";
export interface SimpleMessagingConnection<Request, Response> {
readonly responseStream: Stream<Response>;
readonly sendRequest: (request: Request) => void;
}
/**
* A thin wrapper that is used to bring together requests and responses by ID.
*
* Using this class is only advised for continous communication channels like
* WebSockets or WebWorker messaging.
*/
export class JsonRpcClient {
private readonly connection: SimpleMessagingConnection<JsonRpcRequest, JsonRpcResponse>;
public constructor(connection: SimpleMessagingConnection<JsonRpcRequest, JsonRpcResponse>) {
this.connection = connection;
}
public async run(request: JsonRpcRequest): Promise<JsonRpcSuccessResponse> {
const filteredStream = this.connection.responseStream.filter((r) => r.id === request.id);
const pendingResponses = firstEvent(filteredStream);
this.connection.sendRequest(request);
const response = await pendingResponses;
if (isJsonRpcErrorResponse(response)) {
const error = response.error;
throw new Error(`JSON RPC error: code=${error.code}; message='${error.message}'`);
}
return response;
}
}

View File

@ -0,0 +1,429 @@
import {
parseJsonRpcErrorResponse,
parseJsonRpcId,
parseJsonRpcResponse,
parseJsonRpcSuccessResponse,
} from "./parse";
import { jsonRpcCode, JsonRpcErrorResponse, JsonRpcRequest, JsonRpcSuccessResponse } from "./types";
describe("parse", () => {
describe("parseJsonRpcId", () => {
it("works for number IDs", () => {
const request: JsonRpcRequest = {
jsonrpc: "2.0",
id: 123,
method: "foo",
params: {},
};
expect(parseJsonRpcId(request)).toEqual(123);
});
it("works for string IDs", () => {
const request: JsonRpcRequest = {
jsonrpc: "2.0",
id: "329fg3b",
method: "foo",
params: {},
};
expect(parseJsonRpcId(request)).toEqual("329fg3b");
});
it("returns null for invaid IDs", () => {
// unset
{
const request = {
jsonrpc: "2.0",
method: "foo",
params: {},
};
expect(parseJsonRpcId(request)).toBeNull();
}
// wrong type (object)
{
const request = {
jsonrpc: "2.0",
id: { content: 123 },
method: "foo",
params: {},
};
expect(parseJsonRpcId(request)).toBeNull();
}
// wrong type (Array)
{
const request = {
jsonrpc: "2.0",
id: [1, 2, 3],
method: "foo",
params: {},
};
expect(parseJsonRpcId(request)).toBeNull();
}
// wrong type (null)
{
const request = {
jsonrpc: "2.0",
id: null,
method: "foo",
params: {},
};
expect(parseJsonRpcId(request)).toBeNull();
}
});
});
describe("parseJsonRpcErrorResponse", () => {
it("works for valid error", () => {
const response: any = {
jsonrpc: "2.0",
id: 123,
error: {
code: jsonRpcCode.serverError.default,
message: "Something bad happened",
data: [2, 3, 4],
},
};
expect(parseJsonRpcErrorResponse(response)).toEqual(response);
});
it("works for error with string ID", () => {
const response: any = {
jsonrpc: "2.0",
id: "a3g4g34g",
error: {
code: jsonRpcCode.parseError,
message: "Could not parse request ID",
},
};
expect(parseJsonRpcErrorResponse(response)).toEqual(response);
});
it("works for error with null ID", () => {
const response: any = {
jsonrpc: "2.0",
id: null,
error: {
code: jsonRpcCode.parseError,
message: "Could not parse request ID",
},
};
expect(parseJsonRpcErrorResponse(response)).toEqual(response);
});
it("works for error with null data", () => {
const response: any = {
jsonrpc: "2.0",
id: 123,
error: {
code: jsonRpcCode.serverError.default,
message: "Something bad happened",
data: null,
},
};
expect(parseJsonRpcErrorResponse(response)).toEqual(response);
});
it("works for error with unset data", () => {
const response: any = {
jsonrpc: "2.0",
id: 123,
error: {
code: jsonRpcCode.serverError.default,
message: "Something bad happened",
},
};
expect(parseJsonRpcErrorResponse(response)).toEqual(response);
});
it("throws for invalid type", () => {
const expectedError = /data must be JSON compatible dictionary/i;
expect(() => parseJsonRpcErrorResponse(undefined)).toThrowError(expectedError);
expect(() => parseJsonRpcErrorResponse(null)).toThrowError(expectedError);
expect(() => parseJsonRpcErrorResponse(false)).toThrowError(expectedError);
expect(() => parseJsonRpcErrorResponse("error")).toThrowError(expectedError);
expect(() => parseJsonRpcErrorResponse(42)).toThrowError(expectedError);
expect(() => parseJsonRpcErrorResponse(() => true)).toThrowError(expectedError);
expect(() => parseJsonRpcErrorResponse({ foo: () => true })).toThrowError(expectedError);
expect(() => parseJsonRpcErrorResponse({ foo: () => new Uint8Array([]) })).toThrowError(expectedError);
});
it("throws for invalid version", () => {
// wrong type
{
const response: any = {
jsonrpc: 2.0,
id: 123,
error: {
code: jsonRpcCode.serverError.default,
message: "Something bad happened",
},
};
expect(() => parseJsonRpcErrorResponse(response)).toThrowError(/got unexpected jsonrpc version/i);
}
// wrong version
{
const response: any = {
jsonrpc: "1.0",
id: 123,
error: {
code: jsonRpcCode.serverError.default,
message: "Something bad happened",
},
};
expect(() => parseJsonRpcErrorResponse(response)).toThrowError(/got unexpected jsonrpc version/i);
}
// unset
{
const response: any = {
id: 123,
error: {
code: jsonRpcCode.serverError.default,
message: "Something bad happened",
},
};
expect(() => parseJsonRpcErrorResponse(response)).toThrowError(/got unexpected jsonrpc version/i);
}
});
it("throws for invalid ID", () => {
// wrong type
{
const response: any = {
jsonrpc: "2.0",
id: [1, 2, 3],
error: {
code: jsonRpcCode.serverError.default,
message: "Something bad happened",
},
};
expect(() => parseJsonRpcErrorResponse(response)).toThrowError(/invalid id field/i);
}
// unset
{
const response: any = {
jsonrpc: "2.0",
error: {
code: jsonRpcCode.serverError.default,
message: "Something bad happened",
},
};
expect(() => parseJsonRpcErrorResponse(response)).toThrowError(/invalid id field/i);
}
});
it("throws for success response", () => {
const response: JsonRpcSuccessResponse = {
jsonrpc: "2.0",
id: 123,
result: 3000,
};
expect(() => parseJsonRpcErrorResponse(response)).toThrowError(/invalid error field/i);
});
});
describe("parseJsonRpcSuccessResponse", () => {
it("works for response with dict result", () => {
const response: any = {
jsonrpc: "2.0",
id: 123,
result: {
foo: "bar",
},
};
expect(parseJsonRpcSuccessResponse(response)).toEqual(response);
});
it("works for response with null result", () => {
const response: any = {
jsonrpc: "2.0",
id: 123,
result: null,
};
expect(parseJsonRpcSuccessResponse(response)).toEqual(response);
});
it("works for response with number ID", () => {
const response: any = {
jsonrpc: "2.0",
id: 123,
result: {},
};
expect(parseJsonRpcSuccessResponse(response)).toEqual(response);
});
it("works for response with string ID", () => {
const response: any = {
jsonrpc: "2.0",
id: "40gfh408g",
result: {},
};
expect(parseJsonRpcSuccessResponse(response)).toEqual(response);
});
it("throws for invalid type", () => {
const expectedError = /data must be JSON compatible dictionary/i;
expect(() => parseJsonRpcSuccessResponse(undefined)).toThrowError(expectedError);
expect(() => parseJsonRpcSuccessResponse(null)).toThrowError(expectedError);
expect(() => parseJsonRpcSuccessResponse(false)).toThrowError(expectedError);
expect(() => parseJsonRpcSuccessResponse("success")).toThrowError(expectedError);
expect(() => parseJsonRpcSuccessResponse(42)).toThrowError(expectedError);
expect(() => parseJsonRpcSuccessResponse(() => true)).toThrowError(expectedError);
expect(() => parseJsonRpcSuccessResponse({ foo: () => true })).toThrowError(expectedError);
});
it("throws for invalid version", () => {
// wrong type
{
const response: any = {
jsonrpc: 2.0,
id: 123,
result: 3000,
};
expect(() => parseJsonRpcSuccessResponse(response)).toThrowError(/got unexpected jsonrpc version/i);
}
// wrong version
{
const response: any = {
jsonrpc: "1.0",
id: 123,
result: 3000,
};
expect(() => parseJsonRpcSuccessResponse(response)).toThrowError(/got unexpected jsonrpc version/i);
}
// unset
{
const response: any = {
id: 123,
result: 3000,
};
expect(() => parseJsonRpcSuccessResponse(response)).toThrowError(/got unexpected jsonrpc version/i);
}
});
it("throws for invalid ID", () => {
// wrong type
{
const response: any = {
jsonrpc: "2.0",
id: [1, 2, 3],
result: 3000,
};
expect(() => parseJsonRpcSuccessResponse(response)).toThrowError(/invalid id field/i);
}
// wrong type
{
const response: any = {
jsonrpc: "2.0",
id: null,
result: 3000,
};
expect(() => parseJsonRpcSuccessResponse(response)).toThrowError(/invalid id field/i);
}
// unset
{
const response: any = {
jsonrpc: "2.0",
result: 3000,
};
expect(() => parseJsonRpcSuccessResponse(response)).toThrowError(/invalid id field/i);
}
});
it("throws for error response", () => {
const response: JsonRpcErrorResponse = {
jsonrpc: "2.0",
id: 123,
error: {
code: jsonRpcCode.parseError,
message: "Could not parse request ID",
},
};
expect(() => parseJsonRpcSuccessResponse(response)).toThrowError(/invalid result field/i);
});
});
describe("parseJsonRpcResponse", () => {
it("works for success response", () => {
const response: any = {
jsonrpc: "2.0",
id: 123,
result: 3000,
};
expect(parseJsonRpcResponse(response)).toEqual(response);
});
it("works for error response", () => {
const response: any = {
jsonrpc: "2.0",
id: 123,
error: {
code: jsonRpcCode.serverError.default,
message: "Something bad happened",
data: [2, 3, 4],
},
};
expect(parseJsonRpcResponse(response)).toEqual(response);
});
it("favours error if response is error and success at the same time", () => {
const response: any = {
jsonrpc: "2.0",
id: 123,
result: 3000,
error: {
code: jsonRpcCode.serverError.default,
message: "Something bad happened",
},
};
expect(parseJsonRpcResponse(response)).toEqual({
jsonrpc: "2.0",
id: 123,
error: {
code: jsonRpcCode.serverError.default,
message: "Something bad happened",
},
});
});
it("throws for invalid type", () => {
const expectedError = /data must be JSON compatible dictionary/i;
expect(() => parseJsonRpcResponse(undefined)).toThrowError(expectedError);
expect(() => parseJsonRpcResponse(null)).toThrowError(expectedError);
expect(() => parseJsonRpcResponse(false)).toThrowError(expectedError);
expect(() => parseJsonRpcResponse("error")).toThrowError(expectedError);
expect(() => parseJsonRpcResponse(42)).toThrowError(expectedError);
expect(() => parseJsonRpcResponse(() => true)).toThrowError(expectedError);
expect(() => parseJsonRpcResponse({ foo: () => true })).toThrowError(expectedError);
expect(() => parseJsonRpcResponse({ foo: () => new Uint8Array([]) })).toThrowError(expectedError);
});
it("throws for invalid version", () => {
const expectedError = /got unexpected jsonrpc version/i;
// wrong type
{
const response: any = {
jsonrpc: 2.0,
id: 123,
result: 3000,
};
expect(() => parseJsonRpcResponse(response)).toThrowError(expectedError);
}
// wrong version
{
const response: any = {
jsonrpc: "1.0",
id: 123,
result: 3000,
};
expect(() => parseJsonRpcResponse(response)).toThrowError(expectedError);
}
// unset
{
const response: any = {
id: 123,
result: 3000,
};
expect(() => parseJsonRpcResponse(response)).toThrowError(expectedError);
}
});
});
});

View File

@ -0,0 +1,157 @@
import {
isJsonCompatibleArray,
isJsonCompatibleDictionary,
isJsonCompatibleValue,
JsonCompatibleDictionary,
JsonCompatibleValue,
} from "./compatibility";
import {
JsonRpcError,
JsonRpcErrorResponse,
JsonRpcId,
JsonRpcRequest,
JsonRpcResponse,
JsonRpcSuccessResponse,
} from "./types";
/**
* Extracts ID field from request or response object.
*
* Returns `null` when no valid ID was found.
*/
export function parseJsonRpcId(data: unknown): JsonRpcId | null {
if (!isJsonCompatibleDictionary(data)) {
throw new Error("Data must be JSON compatible dictionary");
}
const id = data.id;
if (typeof id !== "number" && typeof id !== "string") {
return null;
}
return id;
}
export function parseJsonRpcRequest(data: unknown): JsonRpcRequest {
if (!isJsonCompatibleDictionary(data)) {
throw new Error("Data must be JSON compatible dictionary");
}
if (data.jsonrpc !== "2.0") {
throw new Error(`Got unexpected jsonrpc version: ${data.jsonrpc}`);
}
const id = parseJsonRpcId(data);
if (id === null) {
throw new Error("Invalid id field");
}
const method = data.method;
if (typeof method !== "string") {
throw new Error("Invalid method field");
}
if (!isJsonCompatibleArray(data.params) && !isJsonCompatibleDictionary(data.params)) {
throw new Error("Invalid params field");
}
return {
jsonrpc: "2.0",
id: id,
method: method,
params: data.params,
};
}
function parseError(error: JsonCompatibleDictionary): JsonRpcError {
if (typeof error.code !== "number") {
throw new Error("Error property 'code' is not a number");
}
if (typeof error.message !== "string") {
throw new Error("Error property 'message' is not a string");
}
let maybeUndefinedData: JsonCompatibleValue | undefined;
if (error.data === undefined) {
maybeUndefinedData = undefined;
} else if (isJsonCompatibleValue(error.data)) {
maybeUndefinedData = error.data;
} else {
throw new Error("Error property 'data' is defined but not a JSON compatible value.");
}
return {
code: error.code,
message: error.message,
...(maybeUndefinedData !== undefined ? { data: maybeUndefinedData } : {}),
};
}
/** Throws if data is not a JsonRpcErrorResponse */
export function parseJsonRpcErrorResponse(data: unknown): JsonRpcErrorResponse {
if (!isJsonCompatibleDictionary(data)) {
throw new Error("Data must be JSON compatible dictionary");
}
if (data.jsonrpc !== "2.0") {
throw new Error(`Got unexpected jsonrpc version: ${JSON.stringify(data)}`);
}
const id = data.id;
if (typeof id !== "number" && typeof id !== "string" && id !== null) {
throw new Error("Invalid id field");
}
if (typeof data.error === "undefined" || !isJsonCompatibleDictionary(data.error)) {
throw new Error("Invalid error field");
}
return {
jsonrpc: "2.0",
id: id,
error: parseError(data.error),
};
}
/** Throws if data is not a JsonRpcSuccessResponse */
export function parseJsonRpcSuccessResponse(data: unknown): JsonRpcSuccessResponse {
if (!isJsonCompatibleDictionary(data)) {
throw new Error("Data must be JSON compatible dictionary");
}
if (data.jsonrpc !== "2.0") {
throw new Error(`Got unexpected jsonrpc version: ${JSON.stringify(data)}`);
}
const id = data.id;
if (typeof id !== "number" && typeof id !== "string") {
throw new Error("Invalid id field");
}
if (typeof data.result === "undefined") {
throw new Error("Invalid result field");
}
const result = data.result;
return {
jsonrpc: "2.0",
id: id,
result: result,
};
}
/**
* Returns a JsonRpcErrorResponse if input can be parsed as a JSON-RPC error. Otherwise parses
* input as JsonRpcSuccessResponse. Throws if input is neither a valid error nor success response.
*/
export function parseJsonRpcResponse(data: unknown): JsonRpcResponse {
let response: JsonRpcResponse;
try {
response = parseJsonRpcErrorResponse(data);
} catch (_) {
response = parseJsonRpcSuccessResponse(data);
}
return response;
}

View File

@ -0,0 +1,59 @@
import { JsonCompatibleArray, JsonCompatibleDictionary, JsonCompatibleValue } from "./compatibility";
export type JsonRpcId = number | string;
export interface JsonRpcRequest {
readonly jsonrpc: "2.0";
readonly id: JsonRpcId;
readonly method: string;
readonly params: JsonCompatibleArray | JsonCompatibleDictionary;
}
export interface JsonRpcSuccessResponse {
readonly jsonrpc: "2.0";
readonly id: JsonRpcId;
readonly result: any;
}
export interface JsonRpcError {
readonly code: number;
readonly message: string;
readonly data?: JsonCompatibleValue;
}
/**
* And error object as described in https://www.jsonrpc.org/specification#error_object
*/
export interface JsonRpcErrorResponse {
readonly jsonrpc: "2.0";
readonly id: JsonRpcId | null;
readonly error: JsonRpcError;
}
export type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse;
export function isJsonRpcErrorResponse(response: JsonRpcResponse): response is JsonRpcErrorResponse {
return typeof (response as JsonRpcErrorResponse).error === "object";
}
export function isJsonRpcSuccessResponse(response: JsonRpcResponse): response is JsonRpcSuccessResponse {
return !isJsonRpcErrorResponse(response);
}
/**
* Error codes as specified in JSON-RPC 2.0
*
* @see https://www.jsonrpc.org/specification#error_object
*/
export const jsonRpcCode = {
parseError: -32700,
invalidRequest: -32600,
methodNotFound: -32601,
invalidParams: -32602,
internalError: -32603,
// server error (Reserved for implementation-defined server-errors.):
// -32000 to -32099
serverError: {
default: -32000,
},
};

View File

@ -0,0 +1,67 @@
/// <reference lib="webworker" />
// for testing only
import { isJsonCompatibleDictionary } from "../compatibility";
import { parseJsonRpcId, parseJsonRpcRequest } from "../parse";
import {
jsonRpcCode,
JsonRpcErrorResponse,
JsonRpcRequest,
JsonRpcResponse,
JsonRpcSuccessResponse,
} from "../types";
function handleRequest(event: MessageEvent): JsonRpcResponse {
let request: JsonRpcRequest;
try {
request = parseJsonRpcRequest(event.data);
} catch (error) {
const requestId = parseJsonRpcId(event.data);
const errorResponse: JsonRpcErrorResponse = {
jsonrpc: "2.0",
id: requestId,
error: {
code: jsonRpcCode.invalidRequest,
message: error.toString(),
},
};
return errorResponse;
}
let paramsString: string;
if (isJsonCompatibleDictionary(request.params)) {
paramsString = JSON.stringify(request.params);
} else {
paramsString = request.params
.map((p) => {
if (typeof p === "number") {
return p;
} else if (p === null) {
return `null`;
} else if (typeof p === "string") {
return `"${p}"`;
} else {
return p.toString();
}
})
.join(", ");
}
const response: JsonRpcSuccessResponse = {
jsonrpc: "2.0",
id: request.id,
result: `Called ${request.method}(${paramsString})`,
};
return response;
}
onmessage = (event) => {
// filter out empty {"isTrusted":true} events
if (!event.data) {
return;
}
const response = handleRequest(event);
setTimeout(() => postMessage(response), 50);
};

View File

@ -0,0 +1,15 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"outDir": "build",
"declarationDir": "build/types",
"rootDir": "src"
},
"include": [
"src/**/*"
],
"exclude": [
"src/workers/**/*"
]
}

View File

@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"outDir": "build",
"declarationDir": "build/types",
"rootDir": "src"
},
"include": [
"src/workers/**/*"
]
}

View File

@ -0,0 +1,14 @@
const packageJson = require("./package.json");
module.exports = {
src: ["./src"],
out: "docs",
exclude: "**/*.spec.ts",
target: "es6",
name: `${packageJson.name} Documentation`,
readme: "README.md",
mode: "file",
excludeExternals: true,
excludeNotExported: true,
excludePrivate: true,
};

View File

@ -0,0 +1,23 @@
/**
* A single JSON value. This is the missing return type of JSON.parse().
*/
export declare type JsonCompatibleValue =
| JsonCompatibleDictionary
| JsonCompatibleArray
| string
| number
| boolean
| null;
/**
* An array of JsonCompatibleValue
*/
export interface JsonCompatibleArray extends ReadonlyArray<JsonCompatibleValue> {}
/**
* A string to json value dictionary.
*/
export interface JsonCompatibleDictionary {
readonly [key: string]: JsonCompatibleValue | readonly JsonCompatibleValue[];
}
export declare function isJsonCompatibleValue(value: unknown): value is JsonCompatibleValue;
export declare function isJsonCompatibleArray(value: unknown): value is JsonCompatibleArray;
export declare function isJsonCompatibleDictionary(data: unknown): data is JsonCompatibleDictionary;

8
packages/json-rpc/types/id.d.ts vendored Normal file
View File

@ -0,0 +1,8 @@
/**
* Creates a new ID to be used for creating a JSON-RPC request.
*
* Multiple calls of this produce unique values.
*
* The output may be any value compatible to JSON-RPC request IDs with an undefined output format and generation logic.
*/
export declare function makeJsonRpcId(): number;

20
packages/json-rpc/types/index.d.ts vendored Normal file
View File

@ -0,0 +1,20 @@
export { makeJsonRpcId } from "./id";
export { JsonRpcClient, SimpleMessagingConnection } from "./jsonrpcclient";
export {
parseJsonRpcId,
parseJsonRpcRequest,
parseJsonRpcResponse,
parseJsonRpcErrorResponse,
parseJsonRpcSuccessResponse,
} from "./parse";
export {
isJsonRpcErrorResponse,
isJsonRpcSuccessResponse,
JsonRpcError,
JsonRpcErrorResponse,
JsonRpcId,
JsonRpcRequest,
JsonRpcResponse,
JsonRpcSuccessResponse,
jsonRpcCode,
} from "./types";

View File

@ -0,0 +1,17 @@
import { Stream } from "xstream";
import { JsonRpcRequest, JsonRpcResponse, JsonRpcSuccessResponse } from "./types";
export interface SimpleMessagingConnection<Request, Response> {
readonly responseStream: Stream<Response>;
readonly sendRequest: (request: Request) => void;
}
/**
* A thin wrapper that is used to bring together requests and responses by ID.
*
* Using this class is only advised for continous communication channels like
* WebSockets or WebWorker messaging.
*/
export declare class JsonRpcClient {
private readonly connection;
constructor(connection: SimpleMessagingConnection<JsonRpcRequest, JsonRpcResponse>);
run(request: JsonRpcRequest): Promise<JsonRpcSuccessResponse>;
}

23
packages/json-rpc/types/parse.d.ts vendored Normal file
View File

@ -0,0 +1,23 @@
import {
JsonRpcErrorResponse,
JsonRpcId,
JsonRpcRequest,
JsonRpcResponse,
JsonRpcSuccessResponse,
} from "./types";
/**
* Extracts ID field from request or response object.
*
* Returns `null` when no valid ID was found.
*/
export declare function parseJsonRpcId(data: unknown): JsonRpcId | null;
export declare function parseJsonRpcRequest(data: unknown): JsonRpcRequest;
/** Throws if data is not a JsonRpcErrorResponse */
export declare function parseJsonRpcErrorResponse(data: unknown): JsonRpcErrorResponse;
/** Throws if data is not a JsonRpcSuccessResponse */
export declare function parseJsonRpcSuccessResponse(data: unknown): JsonRpcSuccessResponse;
/**
* Returns a JsonRpcErrorResponse if input can be parsed as a JSON-RPC error. Otherwise parses
* input as JsonRpcSuccessResponse. Throws if input is neither a valid error nor success response.
*/
export declare function parseJsonRpcResponse(data: unknown): JsonRpcResponse;

46
packages/json-rpc/types/types.d.ts vendored Normal file
View File

@ -0,0 +1,46 @@
import { JsonCompatibleArray, JsonCompatibleDictionary, JsonCompatibleValue } from "./compatibility";
export declare type JsonRpcId = number | string;
export interface JsonRpcRequest {
readonly jsonrpc: "2.0";
readonly id: JsonRpcId;
readonly method: string;
readonly params: JsonCompatibleArray | JsonCompatibleDictionary;
}
export interface JsonRpcSuccessResponse {
readonly jsonrpc: "2.0";
readonly id: JsonRpcId;
readonly result: any;
}
export interface JsonRpcError {
readonly code: number;
readonly message: string;
readonly data?: JsonCompatibleValue;
}
/**
* And error object as described in https://www.jsonrpc.org/specification#error_object
*/
export interface JsonRpcErrorResponse {
readonly jsonrpc: "2.0";
readonly id: JsonRpcId | null;
readonly error: JsonRpcError;
}
export declare type JsonRpcResponse = JsonRpcSuccessResponse | JsonRpcErrorResponse;
export declare function isJsonRpcErrorResponse(response: JsonRpcResponse): response is JsonRpcErrorResponse;
export declare function isJsonRpcSuccessResponse(
response: JsonRpcResponse,
): response is JsonRpcSuccessResponse;
/**
* Error codes as specified in JSON-RPC 2.0
*
* @see https://www.jsonrpc.org/specification#error_object
*/
export declare const jsonRpcCode: {
parseError: number;
invalidRequest: number;
methodNotFound: number;
invalidParams: number;
internalError: number;
serverError: {
default: number;
};
};

View File

@ -0,0 +1,2 @@
/// <reference lib="webworker" />
export {};

View File

@ -0,0 +1,28 @@
const glob = require("glob");
const path = require("path");
const webpack = require("webpack");
const target = "web";
const distdir = path.join(__dirname, "dist", "web");
module.exports = [
{
// bundle for WebWorker tests
target: target,
entry: "./build/workers/dummyservice.worker.js",
output: {
path: distdir,
filename: "dummyservice.worker.js",
},
},
{
// bundle used for Karma tests
target: target,
entry: glob.sync("./build/**/*.spec.js"),
output: {
path: distdir,
filename: "tests.js",
},
plugins: [new webpack.EnvironmentPlugin([])],
},
];

View File

@ -45,8 +45,8 @@
"dependencies": {
"@cosmjs/crypto": "^0.20.0",
"@cosmjs/encoding": "^0.20.0",
"@cosmjs/json-rpc": "^0.20.0",
"@cosmjs/math": "^0.20.0",
"@iov/jsonrpc": "^2.3.2",
"@iov/socket": "^2.3.2",
"axios": "^0.19.0",
"readonly-date": "^1.0.0",

View File

@ -1,4 +1,4 @@
import { JsonRpcRequest, JsonRpcSuccessResponse } from "@iov/jsonrpc";
import { JsonRpcRequest, JsonRpcSuccessResponse } from "@cosmjs/json-rpc";
import * as requests from "./requests";
import * as responses from "./responses";

View File

@ -1,4 +1,4 @@
import { JsonRpcRequest } from "@iov/jsonrpc";
import { JsonRpcRequest } from "@cosmjs/json-rpc";
const numbers = "0123456789";

View File

@ -3,7 +3,7 @@ import {
JsonRpcRequest,
JsonRpcSuccessResponse,
parseJsonRpcResponse,
} from "@iov/jsonrpc";
} from "@cosmjs/json-rpc";
import axios from "axios";
import { hasProtocol, RpcClient } from "./rpcclient";

View File

@ -1,4 +1,4 @@
import { JsonRpcRequest, JsonRpcSuccessResponse } from "@iov/jsonrpc";
import { JsonRpcRequest, JsonRpcSuccessResponse } from "@cosmjs/json-rpc";
import { Stream } from "xstream";
/**

View File

@ -5,7 +5,7 @@ import {
JsonRpcResponse,
JsonRpcSuccessResponse,
parseJsonRpcResponse,
} from "@iov/jsonrpc";
} from "@cosmjs/json-rpc";
import { ConnectionStatus, ReconnectingSocket, SocketWrapperMessageEvent } from "@iov/socket";
import { firstEvent } from "@iov/stream";
import { Listener, Producer, Stream, Subscription } from "xstream";

View File

@ -1,4 +1,4 @@
// Types in this file are exported outside of the @iov/tendermint-rpc package,
// Types in this file are exported outside of the @cosmjs/tendermint-rpc package,
// e.g. as part of a request or response
import { As } from "type-tagger";

View File

@ -1,5 +1,5 @@
import { toHex } from "@cosmjs/encoding";
import { JsonRpcRequest } from "@iov/jsonrpc";
import { JsonRpcRequest } from "@cosmjs/json-rpc";
import { assertNotEmpty, Base64, Base64String, HexString, Integer, IntegerString, may } from "../encodings";
import { createJsonRpcRequest } from "../jsonrpc";

View File

@ -1,5 +1,5 @@
import { fromHex } from "@cosmjs/encoding";
import { JsonRpcSuccessResponse } from "@iov/jsonrpc";
import { JsonRpcSuccessResponse } from "@cosmjs/json-rpc";
import {
assertArray,

View File

@ -1,4 +1,4 @@
import { JsonRpcRequest, JsonRpcSuccessResponse } from "@iov/jsonrpc";
import { JsonRpcRequest, JsonRpcSuccessResponse } from "@cosmjs/json-rpc";
import * as requests from "./requests";
import * as responses from "./responses";
import { SubscriptionEvent } from "./rpcclients";

View File

@ -1,3 +1,3 @@
import { JsonRpcRequest } from "@iov/jsonrpc";
import { JsonRpcRequest } from "@cosmjs/json-rpc";
/** Creates a JSON-RPC request with random ID */
export declare function createJsonRpcRequest(method: string, params?: {}): JsonRpcRequest;

View File

@ -1,4 +1,4 @@
import { JsonRpcRequest, JsonRpcSuccessResponse } from "@iov/jsonrpc";
import { JsonRpcRequest, JsonRpcSuccessResponse } from "@cosmjs/json-rpc";
import { RpcClient } from "./rpcclient";
export declare class HttpClient implements RpcClient {
protected readonly url: string;

View File

@ -1,4 +1,4 @@
import { JsonRpcRequest, JsonRpcSuccessResponse } from "@iov/jsonrpc";
import { JsonRpcRequest, JsonRpcSuccessResponse } from "@cosmjs/json-rpc";
import { Stream } from "xstream";
/**
* An event emitted from Tendermint after subscribing via RPC.

View File

@ -1,4 +1,4 @@
import { JsonRpcId, JsonRpcRequest, JsonRpcResponse, JsonRpcSuccessResponse } from "@iov/jsonrpc";
import { JsonRpcId, JsonRpcRequest, JsonRpcResponse, JsonRpcSuccessResponse } from "@cosmjs/json-rpc";
import { Stream } from "xstream";
import { RpcStreamingClient, SubscriptionEvent } from "./rpcclient";
export declare class WebsocketClient implements RpcStreamingClient {

View File

@ -1,4 +1,4 @@
import { JsonRpcRequest } from "@iov/jsonrpc";
import { JsonRpcRequest } from "@cosmjs/json-rpc";
import * as requests from "../requests";
export declare class Params {
static encodeAbciInfo(req: requests.AbciInfoRequest): JsonRpcRequest;

View File

@ -1,4 +1,4 @@
import { JsonRpcSuccessResponse } from "@iov/jsonrpc";
import { JsonRpcSuccessResponse } from "@cosmjs/json-rpc";
import * as responses from "../responses";
import { SubscriptionEvent } from "../rpcclients";
export declare class Responses {

View File

@ -92,25 +92,6 @@
unique-filename "^1.1.1"
which "^1.3.1"
"@iov/encoding@^2.3.2":
version "2.3.2"
resolved "https://registry.yarnpkg.com/@iov/encoding/-/encoding-2.3.2.tgz#4b37966af0345a6bc904bb58189dc1ea9d14ad9b"
integrity sha512-viioqo1flTkG4Oxb0PvoBXGozHq9fObAgAL4dRHJe9zmChE77EBX2Y5u0nabd2JwAhEbir56AtsrUe4dOrtd5w==
dependencies:
base64-js "^1.3.0"
bech32 "^1.1.4"
bn.js "^4.11.8"
readonly-date "^1.0.0"
"@iov/jsonrpc@^2.3.2":
version "2.3.2"
resolved "https://registry.yarnpkg.com/@iov/jsonrpc/-/jsonrpc-2.3.2.tgz#5cdfa56333741073cc00f17d54efb9f526b9705a"
integrity sha512-fPryTYZ4na1F/K0AF4eRjf1mwg97s8N/AgvELHyqpm7Bq9zvV0/ZBPvzjV1mqmPMfONx91qLIkpDguwmwEb8NA==
dependencies:
"@iov/encoding" "^2.3.2"
"@iov/stream" "^2.3.2"
xstream "^11.10.0"
"@iov/socket@^2.3.2":
version "2.3.2"
resolved "https://registry.yarnpkg.com/@iov/socket/-/socket-2.3.2.tgz#adc8ef389bafc5380e1c7415fb21f9a890d79195"