1
1
import { BaseError } from "make-error" ;
2
- import { TypeOf , Any , TypeC , Errors } from "io-ts" ;
2
+ import { TypeOf , Any , TypeC } from "io-ts" ;
3
3
import { PathReporter } from "io-ts/lib/PathReporter" ;
4
4
import { either } from "fp-ts" ;
5
5
@@ -90,23 +90,18 @@ const METHOD_NOT_FOUND = { code: -32601, message: "Method not found" };
90
90
const PARSE_ERROR = { code : - 32700 , message : "Parse error" } ;
91
91
92
92
/**
93
- * Create a custom RPC error to report issues .
93
+ * Parse raw input into JSON .
94
94
*/
95
- export class RpcError < T = void > extends BaseError {
96
- constructor ( public message : string , public code = - 32603 , public data : T ) {
97
- super ( message ) ;
98
- }
95
+ export function parse ( input : string ) : either . Either < JsonRpcError , unknown > {
96
+ return either . parseJSON ( input , ( ) => PARSE_ERROR ) ;
99
97
}
100
98
101
99
/**
102
- * Parse raw input into JSON .
100
+ * Create a custom RPC error to report issues .
103
101
*/
104
- export function parse ( input : string ) : either . Either < JsonRpcError , unknown > {
105
- try {
106
- const value = JSON . parse ( input ) ;
107
- return either . right ( value ) ;
108
- } catch ( err ) {
109
- return either . left ( PARSE_ERROR ) ;
102
+ export class RpcError < T = void > extends BaseError {
103
+ constructor ( public message : string , public code = - 32603 , public data : T ) {
104
+ super ( message ) ;
110
105
}
111
106
}
112
107
@@ -139,21 +134,15 @@ function failure<T = never>(
139
134
return { jsonrpc : "2.0" , error, id } ;
140
135
}
141
136
142
- /**
143
- * Ensure `key` is a property of `obj` in type-safe compatible way.
144
- */
145
- function has < T extends string > ( obj : Record < T , any > , key : string ) : key is T {
146
- return Object . prototype . hasOwnProperty . call ( obj , key ) ;
147
- }
148
-
149
137
/**
150
138
* Validate RPC message is correctly formatted and type-safe.
151
139
*/
152
140
async function processRequest < T extends Methods , C = void > (
153
141
methods : T ,
154
142
resolvers : Resolvers < T , C > ,
155
143
message : unknown ,
156
- context : C
144
+ context : C ,
145
+ options : ServerOptions
157
146
) : Promise < JsonRpcSuccess < any > | JsonRpcFailure < any > | undefined > {
158
147
if ( message === null || typeof message !== "object" ) {
159
148
return failure ( INVALID_REQUEST , null ) ;
@@ -170,7 +159,7 @@ async function processRequest<T extends Methods, C = void>(
170
159
return failure ( INVALID_REQUEST , id ?? null ) ;
171
160
}
172
161
173
- if ( ! has ( methods , method ) ) {
162
+ if ( ! ( method in methods ) ) {
174
163
return failure ( METHOD_NOT_FOUND , id ) ;
175
164
}
176
165
@@ -188,7 +177,8 @@ async function processRequest<T extends Methods, C = void>(
188
177
return failure ( INVALID_REQUEST , id ) ;
189
178
}
190
179
191
- const input = request . decode ( data ) ;
180
+ const input =
181
+ options . decode === false ? either . right ( data ) : request . decode ( data ) ;
192
182
193
183
if ( either . isLeft ( input ) ) {
194
184
return failure (
@@ -206,7 +196,7 @@ async function processRequest<T extends Methods, C = void>(
206
196
try {
207
197
const data = await resolvers [ method ] ( input . right , context , metadata ) ;
208
198
if ( isNotification ) return ; // Do not encode response for notifications.
209
- return success ( response . encode ( data ) , id ) ;
199
+ return success ( options . encode === false ? data : response . encode ( data ) , id ) ;
210
200
} catch ( err ) {
211
201
return failure (
212
202
{
@@ -219,12 +209,23 @@ async function processRequest<T extends Methods, C = void>(
219
209
}
220
210
}
221
211
212
+ /**
213
+ * Configure the server options.
214
+ */
215
+ export interface ServerOptions {
216
+ // Decodes the request before processing by resolvers.
217
+ decode ?: boolean ;
218
+ // Encodes the response before returning to client.
219
+ encode ?: boolean ;
220
+ }
221
+
222
222
/**
223
223
* Create a JSON RPC request handler.
224
224
*/
225
225
export function createServer < T extends Methods , C = void > (
226
226
methods : T ,
227
- resolvers : Resolvers < T , C >
227
+ resolvers : Resolvers < T , C > ,
228
+ options : ServerOptions = { }
228
229
) {
229
230
return async function rpcServer ( payload : unknown , context : C ) {
230
231
if ( Array . isArray ( payload ) ) {
@@ -233,7 +234,9 @@ export function createServer<T extends Methods, C = void>(
233
234
}
234
235
235
236
const results = await Promise . all (
236
- payload . map ( x => processRequest ( methods , resolvers , x , context ) )
237
+ payload . map ( x =>
238
+ processRequest ( methods , resolvers , x , context , options )
239
+ )
237
240
) ;
238
241
239
242
return results . filter ( ( x ) : x is
@@ -243,7 +246,7 @@ export function createServer<T extends Methods, C = void>(
243
246
} ) ;
244
247
}
245
248
246
- return processRequest ( methods , resolvers , payload , context ) ;
249
+ return processRequest ( methods , resolvers , payload , context , options ) ;
247
250
} ;
248
251
}
249
252
@@ -258,14 +261,23 @@ type ClientMethods<T extends Methods> = {
258
261
} ;
259
262
} [ keyof T & string ] ;
260
263
264
+ /**
265
+ * Configure client options.
266
+ */
267
+ export interface ClientOptions {
268
+ encode ?: boolean ;
269
+ decode ?: boolean ;
270
+ }
271
+
261
272
/**
262
273
* Create a JSON RPC request client.
263
274
*/
264
275
export function createClient < T extends Methods > (
265
276
methods : T ,
266
277
send : (
267
278
rpc : JsonRpcRequest < string , unknown > | JsonRpcRequest < string , unknown > [ ]
268
- ) => Promise < unknown >
279
+ ) => Promise < unknown > ,
280
+ options : ClientOptions = { }
269
281
) {
270
282
let counter = 0 ;
271
283
const jsonrpc = "2.0" ;
@@ -276,26 +288,29 @@ export function createClient<T extends Methods>(
276
288
277
289
return {
278
290
method,
279
- params : request . encode ( params ) ,
291
+ params : options . encode === false ? params : request . encode ( params ) ,
280
292
id : async ? undefined : counter ++ ,
281
293
process : ( body : unknown ) : unknown => {
282
294
if ( body === undefined ) {
283
295
if ( async ) return undefined ;
284
296
285
- throw new RpcError ( "Invalid response" , - 1 , undefined ) ;
297
+ return new RpcError ( "Invalid response" , - 1 , undefined ) ;
286
298
}
287
299
288
300
if ( body === null || typeof body !== "object" || Array . isArray ( body ) ) {
289
- throw new RpcError ( "Invalid response" , - 1 , undefined ) ;
301
+ return new RpcError ( "Invalid response" , - 1 , undefined ) ;
290
302
}
291
303
292
- const { result, error } = body as Record < string , unknown > ;
304
+ const { result, error } = body as Record < string , any > ;
293
305
294
306
if ( result ) {
295
- const output = response . decode ( result ) ;
307
+ const output =
308
+ options . decode === false
309
+ ? either . right ( result )
310
+ : response . decode ( result ) ;
296
311
297
312
if ( either . isLeft ( output ) ) {
298
- throw new RpcError (
313
+ return new RpcError (
299
314
PathReporter . report ( output ) . join ( "; " ) ,
300
315
- 2 ,
301
316
output . left
@@ -310,11 +325,14 @@ export function createClient<T extends Methods>(
310
325
typeof error !== "object" ||
311
326
Array . isArray ( error )
312
327
) {
313
- throw new RpcError ( "Invalid response" , - 1 , undefined ) ;
328
+ return new RpcError ( "Invalid response" , - 1 , undefined ) ;
314
329
}
315
330
316
- const { message, code, data } = error as Record < string , unknown > ;
317
- throw new RpcError ( String ( message || "Error" ) , Number ( code ) || 0 , data ) ;
331
+ return new RpcError (
332
+ String ( error . message || "Error" ) ,
333
+ Number ( error . code ) || 0 ,
334
+ error . data
335
+ ) ;
318
336
}
319
337
} ;
320
338
}
@@ -326,17 +344,19 @@ export function createClient<T extends Methods>(
326
344
> {
327
345
const { params, id, method, process } = prepare ( payload ) ;
328
346
const data = await send ( { jsonrpc, method, params, id } ) ;
329
- return process ( data ) as any ;
347
+ const response = process ( data ) as any ;
348
+ if ( response instanceof RpcError ) throw response ; // Throw RPC errors.
349
+ return response ;
330
350
}
331
351
332
- rpcClient . batch = async < U extends ClientMethods < T > [ ] > (
333
- ... payload : U
352
+ rpcClient . many = async < U extends ClientMethods < T > [ ] > (
353
+ payload : U
334
354
) : Promise <
335
355
{
336
356
[ K in keyof U ] : U [ K ] extends ClientMethods < T >
337
357
? U [ K ] [ "async" ] extends true
338
358
? undefined
339
- : TypeOf < T [ U [ K ] [ "method" ] ] [ "response" ] >
359
+ : TypeOf < T [ U [ K ] [ "method" ] ] [ "response" ] > | RpcError
340
360
: U [ K ] ;
341
361
}
342
362
> => {
@@ -362,5 +382,9 @@ export function createClient<T extends Methods>(
362
382
return items . map ( item => item . process ( lookup . get ( item . id ) ) ) as any ;
363
383
} ;
364
384
385
+ rpcClient . batch = async < U extends ClientMethods < T > [ ] > ( ...payload : U ) => {
386
+ return rpcClient . many ( payload ) ;
387
+ } ;
388
+
365
389
return rpcClient ;
366
390
}
0 commit comments