Skip to content
This repository has been archived by the owner on Nov 9, 2023. It is now read-only.

BREAKING: Use @metamask/utils #105

Merged
merged 4 commits into from
May 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
},
"dependencies": {
"@metamask/safe-event-emitter": "^2.0.0",
"@metamask/utils": "^2.0.0",
"eth-rpc-errors": "^4.0.2"
},
"devDependencies": {
Expand Down
106 changes: 30 additions & 76 deletions src/JsonRpcEngine.ts
Original file line number Diff line number Diff line change
@@ -1,69 +1,19 @@
import SafeEventEmitter from '@metamask/safe-event-emitter';
import {
hasProperty,
JsonRpcError,
JsonRpcRequest,
JsonRpcResponse,
} from '@metamask/utils';
import { errorCodes, EthereumRpcError, serializeError } from 'eth-rpc-errors';

type Maybe<T> = Partial<T> | null | undefined;

export type Json =
| boolean
| number
| string
| null
| { [property: string]: Json }
| Json[];

/**
* A String specifying the version of the JSON-RPC protocol.
* MUST be exactly "2.0".
*/
export type JsonRpcVersion = '2.0';

/**
* An identifier established by the Client that MUST contain a String, Number,
* or NULL value if included. If it is not included it is assumed to be a
* notification. The value SHOULD normally not be Null and Numbers SHOULD
* NOT contain fractional parts.
*/
export type JsonRpcId = number | string | null;

export interface JsonRpcError {
code: number;
message: string;
data?: unknown;
stack?: string;
}

export interface JsonRpcRequest<T> {
jsonrpc: JsonRpcVersion;
method: string;
id: JsonRpcId;
params?: T;
}

export interface JsonRpcNotification<T> {
jsonrpc: JsonRpcVersion;
method: string;
params?: T;
}

interface JsonRpcResponseBase {
jsonrpc: JsonRpcVersion;
id: JsonRpcId;
}

export interface JsonRpcSuccess<T> extends JsonRpcResponseBase {
result: Maybe<T>;
}

export interface JsonRpcFailure extends JsonRpcResponseBase {
error: JsonRpcError;
}

export type JsonRpcResponse<T> = JsonRpcSuccess<T> | JsonRpcFailure;

export interface PendingJsonRpcResponse<T> extends JsonRpcResponseBase {
result?: T;
error?: Error | JsonRpcError;
}
export type PendingJsonRpcResponse<Result> = Omit<
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not suggesting any changes, but I wonder if this is type is right, considering that it seems that such a response can theoretically have both result and error.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type is meant to be used inside middleware functions and internally in JsonRpcEngine during request processing. It permits both, either, or neither property being present. Or did you mean something else?

JsonRpcResponse<Result>,
'error' | 'result'
> & {
result?: Result;
error?: JsonRpcError;
};

export type JsonRpcEngineCallbackError = Error | JsonRpcError | null;

Expand All @@ -79,9 +29,9 @@ export type JsonRpcEngineEndCallback = (
error?: JsonRpcEngineCallbackError,
) => void;

export type JsonRpcMiddleware<T, U> = (
req: JsonRpcRequest<T>,
res: PendingJsonRpcResponse<U>,
export type JsonRpcMiddleware<Params, Result> = (
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like these type parameter names a lot better than T and U 👍🏻

req: JsonRpcRequest<Params>,
res: PendingJsonRpcResponse<Result>,
next: JsonRpcEngineNextCallback,
end: JsonRpcEngineEndCallback,
) => void;
Expand All @@ -103,7 +53,7 @@ export class JsonRpcEngine extends SafeEventEmitter {
*
* @param middleware - The middleware function to add.
*/
push<T, U>(middleware: JsonRpcMiddleware<T, U>): void {
push<Params, Result>(middleware: JsonRpcMiddleware<Params, Result>): void {
this._middleware.push(middleware as JsonRpcMiddleware<unknown, unknown>);
}

Expand All @@ -113,9 +63,9 @@ export class JsonRpcEngine extends SafeEventEmitter {
* @param request - The request to handle.
* @param callback - An error-first callback that will receive the response.
*/
handle<T, U>(
request: JsonRpcRequest<T>,
callback: (error: unknown, response: JsonRpcResponse<U>) => void,
handle<Params, Result>(
request: JsonRpcRequest<Params>,
callback: (error: unknown, response: JsonRpcResponse<Result>) => void,
): void;

/**
Expand All @@ -125,9 +75,9 @@ export class JsonRpcEngine extends SafeEventEmitter {
* @param callback - An error-first callback that will receive the array of
* responses.
*/
handle<T, U>(
requests: JsonRpcRequest<T>[],
callback: (error: unknown, responses: JsonRpcResponse<U>[]) => void,
handle<Params, Result>(
requests: JsonRpcRequest<Params>[],
callback: (error: unknown, responses: JsonRpcResponse<Result>[]) => void,
): void;

/**
Expand All @@ -136,15 +86,19 @@ export class JsonRpcEngine extends SafeEventEmitter {
* @param request - The JSON-RPC request to handle.
* @returns The JSON-RPC response.
*/
handle<T, U>(request: JsonRpcRequest<T>): Promise<JsonRpcResponse<U>>;
handle<Params, Result>(
request: JsonRpcRequest<Params>,
): Promise<JsonRpcResponse<Result>>;

/**
* Handle an array of JSON-RPC requests, and return an array of responses.
*
* @param request - The JSON-RPC requests to handle.
* @returns An array of JSON-RPC responses.
*/
handle<T, U>(requests: JsonRpcRequest<T>[]): Promise<JsonRpcResponse<U>[]>;
handle<Params, Result>(
requests: JsonRpcRequest<Params>[],
): Promise<JsonRpcResponse<Result>[]>;

handle(req: unknown, callback?: any) {
if (callback && typeof callback !== 'function') {
Expand Down Expand Up @@ -487,7 +441,7 @@ export class JsonRpcEngine extends SafeEventEmitter {
res: PendingJsonRpcResponse<unknown>,
isComplete: boolean,
): void {
if (!('result' in res) && !('error' in res)) {
if (!hasProperty(res, 'result') && !hasProperty(res, 'error')) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The replaced in checks.

throw new EthereumRpcError(
errorCodes.rpc.internal,
`JsonRpcEngine: Response has no error or result for request:\n${jsonify(
Expand Down
4 changes: 2 additions & 2 deletions src/asMiddleware.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {
assertIsJsonRpcSuccess,
isJsonRpcSuccess,
JsonRpcEngine,
JsonRpcRequest,
} from '.';
} from '@metamask/utils';
import { JsonRpcEngine } from '.';

const jsonrpc = '2.0' as const;

Expand Down
7 changes: 2 additions & 5 deletions src/createAsyncMiddleware.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import {
JsonRpcEngine,
createAsyncMiddleware,
assertIsJsonRpcSuccess,
} from '.';
import { assertIsJsonRpcSuccess } from '@metamask/utils';
import { JsonRpcEngine, createAsyncMiddleware } from '.';

const jsonrpc = '2.0' as const;

Expand Down
19 changes: 8 additions & 11 deletions src/createAsyncMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import {
JsonRpcMiddleware,
JsonRpcRequest,
PendingJsonRpcResponse,
} from './JsonRpcEngine';
import { JsonRpcRequest } from '@metamask/utils';
import { JsonRpcMiddleware, PendingJsonRpcResponse } from './JsonRpcEngine';

export type AsyncJsonRpcEngineNextCallback = () => Promise<void>;

export type AsyncJsonrpcMiddleware<T, U> = (
req: JsonRpcRequest<T>,
res: PendingJsonRpcResponse<U>,
export type AsyncJsonrpcMiddleware<Params, Result> = (
req: JsonRpcRequest<Params>,
res: PendingJsonRpcResponse<Result>,
next: AsyncJsonRpcEngineNextCallback,
) => Promise<void>;

Expand All @@ -35,9 +32,9 @@ type ReturnHandlerCallback = (error: null | Error) => void;
* @returns The wrapped asynchronous middleware function, ready to be consumed
* by JsonRpcEngine.
*/
export function createAsyncMiddleware<T, U>(
asyncMiddleware: AsyncJsonrpcMiddleware<T, U>,
): JsonRpcMiddleware<T, U> {
export function createAsyncMiddleware<Params, Result>(
asyncMiddleware: AsyncJsonrpcMiddleware<Params, Result>,
): JsonRpcMiddleware<Params, Result> {
return async (req, res, next, end) => {
// nextPromise is the key to the implementation
// it is resolved by the return handler passed to the
Expand Down
9 changes: 4 additions & 5 deletions src/createScaffoldMiddleware.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import {
JsonRpcEngine,
createScaffoldMiddleware,
JsonRpcMiddleware,
assertIsJsonRpcSuccess,
assertIsJsonRpcFailure,
} from '.';
} from '@metamask/utils';
import { ethErrors } from 'eth-rpc-errors';
import { JsonRpcEngine, createScaffoldMiddleware, JsonRpcMiddleware } from '.';

describe('createScaffoldMiddleware', () => {
it('basic middleware test', async () => {
Expand All @@ -20,7 +19,7 @@ describe('createScaffoldMiddleware', () => {
end();
},
method3: (_req, res, _next, end) => {
res.error = new Error('method3');
res.error = ethErrors.rpc.internal({ message: 'method3' });
end();
},
};
Expand Down
7 changes: 5 additions & 2 deletions src/createScaffoldMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { Json, JsonRpcMiddleware, JsonRpcSuccess } from './JsonRpcEngine';
import { Json, JsonRpcSuccess } from '@metamask/utils';
import { JsonRpcMiddleware } from './JsonRpcEngine';

type ScaffoldMiddlewareHandler<T, U> = JsonRpcMiddleware<T, U> | Json;
type ScaffoldMiddlewareHandler<Params, Result> =
| JsonRpcMiddleware<Params, Result>
| Json;

/**
* Creates a middleware function from an object of RPC method handler functions,
Expand Down
16 changes: 9 additions & 7 deletions src/engine.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { isJsonRpcFailure, isJsonRpcSuccess } from './utils';
import {
JsonRpcEngine,
assertIsJsonRpcSuccess,
assertIsJsonRpcFailure,
} from '.';
isJsonRpcFailure,
isJsonRpcSuccess,
} from '@metamask/utils';
import { ethErrors } from 'eth-rpc-errors';
import { JsonRpcEngine } from '.';

const jsonrpc = '2.0' as const;

Expand Down Expand Up @@ -187,7 +189,7 @@ describe('JsonRpcEngine', () => {
const engine = new JsonRpcEngine();

engine.push(function (_req, res, next, _end) {
res.error = new Error('no bueno');
res.error = ethErrors.rpc.internal({ message: 'foobar' });
next();
});

Expand All @@ -208,7 +210,7 @@ describe('JsonRpcEngine', () => {
const engine = new JsonRpcEngine();

engine.push(function (_req, res, _next, end) {
res.error = new Error('no bueno');
res.error = ethErrors.rpc.internal({ message: 'foobar' });
end();
});

Expand Down Expand Up @@ -270,7 +272,7 @@ describe('JsonRpcEngine', () => {
engine.push(function (req, res, _next, end) {
if (req.id === 4) {
delete res.result;
res.error = new Error('foobar');
res.error = ethErrors.rpc.internal({ message: 'foobar' });
return end(res.error);
}
res.result = req.id;
Expand Down Expand Up @@ -305,7 +307,7 @@ describe('JsonRpcEngine', () => {
engine.push(function (req, res, _next, end) {
if (req.id === 4) {
delete res.result;
res.error = new Error('foobar');
res.error = ethErrors.rpc.internal({ message: 'foobar' });
return end(res.error);
}
res.result = req.id;
Expand Down
1 change: 0 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@ export * from './getUniqueId';
export * from './idRemapMiddleware';
export * from './JsonRpcEngine';
export * from './mergeMiddleware';
export * from './utils';
8 changes: 4 additions & 4 deletions src/mergeMiddleware.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import {
assertIsJsonRpcSuccess,
JsonRpcEngine,
hasProperty,
JsonRpcRequest,
mergeMiddleware,
} from '.';
} from '@metamask/utils';
import { JsonRpcEngine, mergeMiddleware } from '.';

const jsonrpc = '2.0' as const;

Expand All @@ -30,7 +30,7 @@ describe('mergeMiddleware', () => {
expect(res).toBeDefined();
expect(originalReq.id).toStrictEqual(res.id);
expect(originalReq.jsonrpc).toStrictEqual(res.jsonrpc);
expect('result' in res).toBe(true);
expect(hasProperty(res, 'result')).toBe(true);
resolve();
});
});
Expand Down
Loading