11import SafeEventEmitter from '@metamask/safe-event-emitter' ;
22import { errorCodes , EthereumRpcError , serializeError } from 'eth-rpc-errors' ;
3+ import { isJsonRpcRequest } from './utils' ;
34
45type Maybe < T > = Partial < T > | null | undefined ;
56
@@ -32,38 +33,37 @@ export interface JsonRpcError {
3233 stack ?: string ;
3334}
3435
35- export interface JsonRpcRequest < T > {
36+ interface JsonRpcRequestBase < T > {
3637 jsonrpc : JsonRpcVersion ;
3738 method : string ;
38- id : JsonRpcId ;
3939 params ?: T ;
4040}
4141
42- export interface JsonRpcNotification < T > {
43- jsonrpc : JsonRpcVersion ;
44- method : string ;
45- params ?: T ;
46- }
42+ export type JsonRpcRequest < T > = JsonRpcRequestBase < T > & {
43+ id : JsonRpcId ;
44+ } ;
45+
46+ export type JsonRpcNotification < T > = JsonRpcRequestBase < T > ;
4747
4848interface JsonRpcResponseBase {
4949 jsonrpc : JsonRpcVersion ;
5050 id : JsonRpcId ;
5151}
5252
53- export interface JsonRpcSuccess < T > extends JsonRpcResponseBase {
53+ export type JsonRpcSuccess < T > = JsonRpcResponseBase & {
5454 result : Maybe < T > ;
55- }
55+ } ;
5656
57- export interface JsonRpcFailure extends JsonRpcResponseBase {
57+ export type JsonRpcFailure = JsonRpcResponseBase & {
5858 error : JsonRpcError ;
59- }
59+ } ;
6060
6161export type JsonRpcResponse < T > = JsonRpcSuccess < T > | JsonRpcFailure ;
6262
63- export interface PendingJsonRpcResponse < T > extends JsonRpcResponseBase {
63+ export type PendingJsonRpcResponse < T > = JsonRpcResponseBase & {
6464 result ?: T ;
6565 error ?: Error | JsonRpcError ;
66- }
66+ } ;
6767
6868export type JsonRpcEngineCallbackError = Error | JsonRpcError | null ;
6969
@@ -86,27 +86,56 @@ export type JsonRpcMiddleware<T, U> = (
8686 end : JsonRpcEngineEndCallback ,
8787) => void ;
8888
89+ export type JsonRpcNotificationMiddleware < T > = (
90+ req : JsonRpcNotification < T > ,
91+ ) => Promise < void > ;
92+
8993/**
9094 * A JSON-RPC request and response processor.
9195 * Give it a stack of middleware, pass it requests, and get back responses.
9296 */
9397export class JsonRpcEngine extends SafeEventEmitter {
9498 private _middleware : JsonRpcMiddleware < unknown , unknown > [ ] ;
9599
100+ private _notificationMiddleware : JsonRpcNotificationMiddleware < unknown > [ ] ;
101+
96102 constructor ( ) {
97103 super ( ) ;
98104 this . _middleware = [ ] ;
105+ this . _notificationMiddleware = [ ] ;
99106 }
100107
101108 /**
102- * Add a middleware function to the engine's middleware stack.
109+ * Add a middleware function to the engine's request middleware stack.
103110 *
104111 * @param middleware - The middleware function to add.
105112 */
106- push < T , U > ( middleware : JsonRpcMiddleware < T , U > ) : void {
113+ pushRequestMiddleware < T , U > ( middleware : JsonRpcMiddleware < T , U > ) : void {
107114 this . _middleware . push ( middleware as JsonRpcMiddleware < unknown , unknown > ) ;
108115 }
109116
117+ /**
118+ * Alias for `pushRequestMiddleware`, for backwards compatibility.
119+ *
120+ * @param middleware - The request middleware function to add.
121+ */
122+ push < T , U > ( middleware : JsonRpcMiddleware < T , U > ) : void {
123+ this . pushRequestMiddleware (
124+ middleware as JsonRpcMiddleware < unknown , unknown > ,
125+ ) ;
126+ }
127+
128+ /**
129+ * Add a middleware function to the engine's notification middleware stack.
130+ *
131+ * @param middleware - The notification middleware function to add.
132+ */
133+ pushNotificationMiddleware < T > (
134+ middleware : JsonRpcNotificationMiddleware < T > ,
135+ ) : void {
136+ this . _middleware . push ( middleware as JsonRpcNotificationMiddleware < unknown > ) ;
137+ }
138+
110139 /**
111140 * Handle a JSON-RPC request, and return a response.
112141 *
@@ -119,14 +148,26 @@ export class JsonRpcEngine extends SafeEventEmitter {
119148 ) : void ;
120149
121150 /**
122- * Handle an array of JSON-RPC requests, and return an array of responses.
151+ * Handle a JSON-RPC notification.
152+ *
153+ * @param notification - The notification to handle.
154+ * @param callback - An error-first callback that will receive a `void` response.
155+ */
156+ handle < T > (
157+ request : JsonRpcNotification < T > ,
158+ callback : ( error : unknown , response : void ) => void ,
159+ ) : void ;
160+
161+ /**
162+ * Handle an array of JSON-RPC requests and/or notifications, and return an
163+ * array of responses to any included requests.
123164 *
124165 * @param request - The requests to handle.
125166 * @param callback - An error-first callback that will receive the array of
126167 * responses.
127168 */
128169 handle < T , U > (
129- requests : JsonRpcRequest < T > [ ] ,
170+ requests : ( JsonRpcRequest < T > | JsonRpcNotification < T > ) [ ] ,
130171 callback : ( error : unknown , responses : JsonRpcResponse < U > [ ] ) => void ,
131172 ) : void ;
132173
@@ -139,12 +180,22 @@ export class JsonRpcEngine extends SafeEventEmitter {
139180 handle < T , U > ( request : JsonRpcRequest < T > ) : Promise < JsonRpcResponse < U > > ;
140181
141182 /**
142- * Handle an array of JSON-RPC requests, and return an array of responses.
183+ * Handle a JSON-RPC notification.
184+ *
185+ * @param notification - The notification to handle.
186+ */
187+ handle < T > ( notification : JsonRpcNotification < T > ) : Promise < void > ;
188+
189+ /**
190+ * Handle an array of JSON-RPC requests and/or notifications, and return an
191+ * array of responses to any included requests.
143192 *
144193 * @param request - The JSON-RPC requests to handle.
145194 * @returns An array of JSON-RPC responses.
146195 */
147- handle < T , U > ( requests : JsonRpcRequest < T > [ ] ) : Promise < JsonRpcResponse < U > [ ] > ;
196+ handle < T , U > (
197+ requests : ( JsonRpcRequest < T > | JsonRpcNotification < T > ) [ ] ,
198+ ) : Promise < JsonRpcResponse < U > [ ] > ;
148199
149200 handle ( req : unknown , callback ?: any ) {
150201 if ( callback && typeof callback !== 'function' ) {
@@ -199,14 +250,14 @@ export class JsonRpcEngine extends SafeEventEmitter {
199250 * Like _handle, but for batch requests.
200251 */
201252 private _handleBatch (
202- reqs : JsonRpcRequest < unknown > [ ] ,
253+ reqs : ( JsonRpcRequest < unknown > | JsonRpcNotification < unknown > ) [ ] ,
203254 ) : Promise < JsonRpcResponse < unknown > [ ] > ;
204255
205256 /**
206257 * Like _handle, but for batch requests.
207258 */
208259 private _handleBatch (
209- reqs : JsonRpcRequest < unknown > [ ] ,
260+ reqs : ( JsonRpcRequest < unknown > | JsonRpcNotification < unknown > ) [ ] ,
210261 callback : ( error : unknown , responses ?: JsonRpcResponse < unknown > [ ] ) => void ,
211262 ) : Promise < void > ;
212263
@@ -219,23 +270,23 @@ export class JsonRpcEngine extends SafeEventEmitter {
219270 * @returns The array of responses, or nothing if a callback was specified.
220271 */
221272 private async _handleBatch (
222- reqs : JsonRpcRequest < unknown > [ ] ,
273+ reqs : ( JsonRpcRequest < unknown > | JsonRpcNotification < unknown > ) [ ] ,
223274 callback ?: ( error : unknown , responses ?: JsonRpcResponse < unknown > [ ] ) => void ,
224275 ) : Promise < JsonRpcResponse < unknown > [ ] | void > {
225276 // The order here is important
226277 try {
227278 // 2. Wait for all requests to finish, or throw on some kind of fatal
228279 // error
229- const responses = await Promise . all (
230- // 1. Begin executing each request in the order received
231- reqs . map ( this . _promiseHandle . bind ( this ) ) ,
232- ) ;
280+ const responses = (
281+ await Promise . all (
282+ // 1. Begin executing each request in the order received
283+ reqs . map ( this . _promiseHandle . bind ( this ) ) ,
284+ // Filter out falsy responses from notifications
285+ )
286+ ) . filter ( ( response ) => Boolean ( response ) ) as JsonRpcResponse < unknown > [ ] ;
233287
234288 // 3. Return batch response
235- if ( callback ) {
236- return callback ( null , responses ) ;
237- }
238- return responses ;
289+ return callback ? callback ( null , responses ) : responses ;
239290 } catch ( error ) {
240291 if ( callback ) {
241292 return callback ( error ) ;
@@ -252,8 +303,8 @@ export class JsonRpcEngine extends SafeEventEmitter {
252303 * @returns The JSON-RPC response.
253304 */
254305 private _promiseHandle (
255- req : JsonRpcRequest < unknown > ,
256- ) : Promise < JsonRpcResponse < unknown > > {
306+ req : JsonRpcRequest < unknown > | JsonRpcNotification < unknown > ,
307+ ) : Promise < JsonRpcResponse < unknown > | void > {
257308 return new Promise ( ( resolve ) => {
258309 this . _handle ( req , ( _err , res ) => {
259310 // There will always be a response, and it will always have any error
@@ -264,8 +315,8 @@ export class JsonRpcEngine extends SafeEventEmitter {
264315 }
265316
266317 /**
267- * Ensures that the request object is valid, processes it, and passes any
268- * error and the response object to the given callback.
318+ * Ensures that the request / notification object is valid, processes it, and
319+ * passes any error and response object to the given callback.
269320 *
270321 * Does not reject.
271322 *
@@ -274,8 +325,8 @@ export class JsonRpcEngine extends SafeEventEmitter {
274325 * @returns Nothing.
275326 */
276327 private async _handle (
277- callerReq : JsonRpcRequest < unknown > ,
278- callback : ( error : unknown , response : JsonRpcResponse < unknown > ) => void ,
328+ callerReq : JsonRpcRequest < unknown > | JsonRpcNotification < unknown > ,
329+ callback : ( error : unknown , response ? : JsonRpcResponse < unknown > ) => void ,
279330 ) : Promise < void > {
280331 if (
281332 ! callerReq ||
@@ -296,18 +347,39 @@ export class JsonRpcEngine extends SafeEventEmitter {
296347 `Must specify a string method. Received: ${ typeof callerReq . method } ` ,
297348 { request : callerReq } ,
298349 ) ;
299- return callback ( error , { id : callerReq . id , jsonrpc : '2.0' , error } ) ;
350+ return callback ( error , {
351+ id : ( callerReq as any ) . id ?? null ,
352+ jsonrpc : '2.0' ,
353+ error,
354+ } ) ;
355+ }
356+
357+ let error : JsonRpcEngineCallbackError = null ;
358+
359+ // Handle notifications.
360+ // We can't use isJsonRpcNotification here because that narrows callerReq to
361+ // "never" after the if clause.
362+ if ( ! isJsonRpcRequest ( callerReq ) ) {
363+ try {
364+ await JsonRpcEngine . _processNotification (
365+ { ...callerReq } ,
366+ this . _notificationMiddleware ,
367+ ) ;
368+ } catch ( _error ) {
369+ error = _error ;
370+ }
371+ return callback ( error ) ;
300372 }
301373
374+ // Handle requests.
302375 const req : JsonRpcRequest < unknown > = { ...callerReq } ;
303376 const res : PendingJsonRpcResponse < unknown > = {
304377 id : req . id ,
305378 jsonrpc : req . jsonrpc ,
306379 } ;
307- let error : JsonRpcEngineCallbackError = null ;
308380
309381 try {
310- await this . _processRequest ( req , res ) ;
382+ await JsonRpcEngine . _processRequest ( req , res , this . _middleware ) ;
311383 } catch ( _error ) {
312384 // A request handler error, a re-thrown middleware error, or something
313385 // unexpected.
@@ -325,20 +397,37 @@ export class JsonRpcEngine extends SafeEventEmitter {
325397 return callback ( error , res as JsonRpcResponse < unknown > ) ;
326398 }
327399
400+ /**
401+ * Runs all notification middleware on the specified notification.
402+ *
403+ * @param notification - The notification object to process.
404+ * @param middlewareStack - The stack of notification middleware functions.
405+ */
406+ private static async _processNotification (
407+ notification : JsonRpcNotification < unknown > ,
408+ middlewareStack : JsonRpcNotificationMiddleware < unknown > [ ] ,
409+ ) : Promise < void > {
410+ for ( const middleware of middlewareStack ) {
411+ await middleware ( notification ) ;
412+ }
413+ }
414+
328415 /**
329416 * For the given request and response, runs all middleware and their return
330417 * handlers, if any, and ensures that internal request processing semantics
331418 * are satisfied.
332419 *
333420 * @param req - The request object.
334421 * @param res - The response object.
422+ * @param middlewareStack - The stack of request middleware functions.
335423 */
336- private async _processRequest (
424+ private static async _processRequest (
337425 req : JsonRpcRequest < unknown > ,
338426 res : PendingJsonRpcResponse < unknown > ,
427+ middlewareStack : JsonRpcMiddleware < unknown , unknown > [ ] ,
339428 ) : Promise < void > {
340429 const [ error , isComplete , returnHandlers ] =
341- await JsonRpcEngine . _runAllMiddleware ( req , res , this . _middleware ) ;
430+ await JsonRpcEngine . _runAllMiddleware ( req , res , middlewareStack ) ;
342431
343432 // Throw if "end" was not called, or if the response has neither a result
344433 // nor an error.
0 commit comments