Skip to content

Commit dfc9684

Browse files
committed
type fix && add tests
1 parent dd7851d commit dfc9684

File tree

4 files changed

+122
-11
lines changed

4 files changed

+122
-11
lines changed

README.md

+38-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ A strictly typed json-rpc(2.0) implemention, zero dependency, minimal abstractio
99
```ts
1010
const methodSet = {
1111
upper: (str: string) => str.toUpperCase(),
12-
} as const
12+
lower: (str: string) => str.toLowerCase(),
13+
plus: ([a, b]: [number, number]) => a + b,
14+
minus: ([a, b]: [number, number]) => a - b,
15+
}
1316

1417
const server = new JSONRPCServer(methodSet)
1518

@@ -22,8 +25,10 @@ assertEquals(await client.request('upper', 'hello'), 'HELLO')
2225
assertEquals(
2326
await client.batch(
2427
client.createRequest('upper', 'nihao'),
28+
// notifaction does not have response, even when response errors
2529
client.createNotifaction('upper'),
2630
client.createRequest('upper', 'shijie'),
31+
client.createRequest('plus', [1, 1]),
2732
),
2833
[
2934
{
@@ -34,10 +39,16 @@ assertEquals(
3439
status: 'fulfilled',
3540
value: 'SHIJIE',
3641
},
42+
{
43+
status: 'fulfilled',
44+
value: 2,
45+
},
3746
],
3847
)
3948
```
4049

50+
Example to use the client
51+
4152
```ts
4253
const client = new JSONRPCClient((json) =>
4354
fetch('http://localhost:6800/jsonrpc', {
@@ -48,3 +59,29 @@ const client = new JSONRPCClient((json) =>
4859

4960
const aria2cMethods = await client.request('system.listMethods')
5061
```
62+
63+
Example to use the server
64+
65+
```ts
66+
const server = new JSONRPCServer()
67+
68+
server.setMethod('trim', (str: string) => str.trim())
69+
server.setMethod('trimStart', (str: string) => str.trimStart())
70+
server.setMethod('trimEnd', (str: string) => str.trimEnd())
71+
72+
const httpServer = Deno.serve(
73+
async (request) => {
74+
const url = new URL(request.url)
75+
if (url.pathname === '/jsonrpc') {
76+
return new Response(
77+
// server.process() accept string and returns Promise<string>
78+
await server.process(await request.text()),
79+
{
80+
headers: { 'content-type': 'application/json' },
81+
},
82+
)
83+
}
84+
return new Response('404', { status: 404 })
85+
},
86+
)
87+
```

src/client.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,10 @@ export class JSONRPCClient<MethodSet extends JSONRPCMethodSet> {
137137

138138
/**
139139
* You should use the `createRequest()` or `createNotifaction()` method to
140-
* create the requests array.
140+
* create the requests array. Response order is always matched by id.
141141
*
142142
* Throws `JSONRPCClientParseError` if server response cannot be parsed,
143-
* note that it does not throws for any `JSONRPCErrorResponse`, in this
143+
* note that it does not throws for any `JSONRPCErrorResponse`, in this
144144
* case it will be a single object: `{ status: 'rejected', reason: {...} }`
145145
*
146146
* Usually it returns be like (same as the `Promise.allSettled()` method):

src/index.test.ts

+71-2
Original file line numberDiff line numberDiff line change
@@ -30,26 +30,44 @@ Deno.test('JSONRPCClient/JSONRPCServer', async () => {
3030
assertObjectMatch(
3131
await client.request(
3232
'plus',
33-
{ $: 'not an array, so it should throw' } as any,
33+
{ error_test: 'not an array, so it should throw' } as any,
3434
).catch((e) => e),
3535
{
3636
code: -32603,
3737
message: 'Internal error',
3838
},
3939
)
4040

41+
assertObjectMatch(
42+
await client.request(
43+
'no_such_method' as any,
44+
).catch((e) => e),
45+
{
46+
code: -32601,
47+
message: 'Method not found',
48+
},
49+
)
50+
4151
assertEquals(
4252
await client.batch(
4353
client.createRequest('upper', 'nihao'),
44-
client.createNotifaction('upper', 'anything'),
54+
client.createNotifaction('lower', 'anything'),
4555
client.createRequest('upper', 'shijie'),
56+
client.createRequest('plus', [1, 2]),
57+
client.createRequest('minus', [1, 2]),
4658
),
4759
[{
4860
status: 'fulfilled',
4961
value: 'NIHAO',
5062
}, {
5163
status: 'fulfilled',
5264
value: 'SHIJIE',
65+
}, {
66+
status: 'fulfilled',
67+
value: 3,
68+
}, {
69+
status: 'fulfilled',
70+
value: -1,
5371
}],
5472
)
5573
})
@@ -61,6 +79,14 @@ Deno.test({
6179
.state !==
6280
'granted',
6381
fn: async () => {
82+
const ok = await fetch('http://localhost:6800/jsonrpc', {
83+
signal: AbortSignal.timeout(500),
84+
}).then((res) => res.ok).catch(() => false)
85+
if (!ok) {
86+
// skip when no aria2c jsonrpc is running
87+
return
88+
}
89+
6490
const client = new JSONRPCClient((json) =>
6591
fetch('http://localhost:6800/jsonrpc', {
6692
method: 'POST',
@@ -81,3 +107,46 @@ Deno.test({
81107
assertObjectMatch(r2, { status: 'fulfilled' })
82108
},
83109
})
110+
111+
Deno.test({
112+
name: 'JSONRPCServer/Deno.serve()',
113+
ignore: Deno.permissions.querySync({ name: 'net' })
114+
.state !==
115+
'granted',
116+
fn: async () => {
117+
const server = new JSONRPCServer()
118+
119+
server.setMethod('trim', (str: string) => str.trim())
120+
server.setMethod('trimStart', (str: string) => str.trimStart())
121+
server.setMethod('trimEnd', (str: string) => str.trimEnd())
122+
123+
const httpServer = Deno.serve(
124+
{ port: 8888, onListen() {} },
125+
async (request) => {
126+
const url = new URL(request.url)
127+
if (url.pathname === '/jsonrpc') {
128+
return new Response(
129+
await server.process(await request.text()),
130+
{
131+
headers: { 'content-type': 'application/json' },
132+
},
133+
)
134+
}
135+
return new Response('404', { status: 404 })
136+
},
137+
)
138+
139+
const client = new JSONRPCClient((json) =>
140+
fetch('http://localhost:8888/jsonrpc', {
141+
method: 'POST',
142+
body: json,
143+
}).then((res) => res.text())
144+
)
145+
146+
assertEquals(await client.request('trim', ' trim '), 'trim')
147+
assertEquals(await client.request('trimStart', ' trim '), 'trim ')
148+
assertEquals(await client.request('trimEnd', ' trim '), ' trim')
149+
150+
await httpServer.shutdown()
151+
},
152+
})

src/server.ts

+11-6
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,17 @@ import type { JSONRPCMethodSet, JSONRPCValue } from './types.ts'
2525
*
2626
* See `JSONRPCMethodSet` for how method in method set should be.
2727
*/
28-
export class JSONRPCServer<MethodSet extends JSONRPCMethodSet> {
28+
export class JSONRPCServer<
29+
MethodSet extends JSONRPCMethodSet = JSONRPCMethodSet,
30+
> {
2931
private methodSet: MethodSet
3032

33+
constructor()
34+
constructor(methodSet: MethodSet)
35+
constructor(methodSet?: MethodSet) {
36+
this.methodSet = methodSet || ({} as MethodSet)
37+
}
38+
3139
/**
3240
* Override this function, to customize the behavior
3341
* when method is not in methodSet.
@@ -60,12 +68,9 @@ export class JSONRPCServer<MethodSet extends JSONRPCMethodSet> {
6068
public setMethod<T extends keyof MethodSet>(
6169
method: T,
6270
fn: MethodSet[T],
63-
): void {
71+
) {
6472
Reflect.set(this.methodSet, method, fn)
65-
}
66-
67-
constructor(methodSet: MethodSet) {
68-
this.methodSet = methodSet
73+
return this
6974
}
7075

7176
/**

0 commit comments

Comments
 (0)