@k13engineering/yajrpc 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/index.d.ts +3 -47
- package/dist/lib/index.js +4 -252
- package/dist/lib/jrpc.d.ts +20 -0
- package/dist/lib/jrpc.js +248 -0
- package/dist/lib/types.d.ts +28 -1
- package/dist/lib/types.js +28 -0
- package/dist/lib/websocket.d.ts +35 -0
- package/dist/lib/websocket.js +202 -0
- package/package.json +2 -1
package/dist/lib/index.d.ts
CHANGED
|
@@ -1,47 +1,3 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
error: undefined;
|
|
5
|
-
} | {
|
|
6
|
-
result: undefined;
|
|
7
|
-
error: TJsonRpcError;
|
|
8
|
-
} | {
|
|
9
|
-
result: undefined;
|
|
10
|
-
error: undefined;
|
|
11
|
-
};
|
|
12
|
-
type TRequestHandler = (args: {
|
|
13
|
-
method: string;
|
|
14
|
-
params: TJsonRpcParameters | undefined;
|
|
15
|
-
}) => Promise<TRequestHandlerResponse>;
|
|
16
|
-
type TNotificationHandler = (args: {
|
|
17
|
-
method: string;
|
|
18
|
-
params: TJsonRpcParameters | undefined;
|
|
19
|
-
}) => void;
|
|
20
|
-
type TNotifyMethod = (args: {
|
|
21
|
-
method: string;
|
|
22
|
-
params: TJsonRpcParameters;
|
|
23
|
-
}) => void;
|
|
24
|
-
type TRequestMethod = (args: {
|
|
25
|
-
method: string;
|
|
26
|
-
params: TJsonRpcParameters;
|
|
27
|
-
timeoutMs?: number;
|
|
28
|
-
}) => Promise<TRequestResult>;
|
|
29
|
-
declare const createJrpc: ({ sendMessage, handleRequest, handleNotification }: {
|
|
30
|
-
sendMessage: (args: {
|
|
31
|
-
message: TJsonRpcMessage;
|
|
32
|
-
}) => void;
|
|
33
|
-
handleRequest: TRequestHandler;
|
|
34
|
-
handleNotification: TNotificationHandler;
|
|
35
|
-
}) => {
|
|
36
|
-
receivedMessage: ({ message }: {
|
|
37
|
-
message: unknown;
|
|
38
|
-
}) => {
|
|
39
|
-
error: Error | undefined;
|
|
40
|
-
};
|
|
41
|
-
request: TRequestMethod;
|
|
42
|
-
notify: TNotifyMethod;
|
|
43
|
-
close: () => void;
|
|
44
|
-
};
|
|
45
|
-
type TJrpc = ReturnType<typeof createJrpc>;
|
|
46
|
-
export { createJrpc };
|
|
47
|
-
export type { TJsonRpcMessage, TRequestResponse, TRequestResult, TJrpc, TRequestHandler, TNotificationHandler, TRequestMethod, TNotifyMethod };
|
|
1
|
+
import { createJrpc } from "./jrpc.js";
|
|
2
|
+
import { createWebSocketJrpc } from "./websocket.js";
|
|
3
|
+
export { createJrpc, createWebSocketJrpc };
|
package/dist/lib/index.js
CHANGED
|
@@ -1,255 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
/*type TPendingRequestHandle = {
|
|
5
|
-
resolve: (args: TRequestResult) => void;
|
|
6
|
-
};*/
|
|
7
|
-
|
|
8
|
-
/*type TRequestHandlerResponse = {
|
|
9
|
-
result: TRequestResponseValue;
|
|
10
|
-
error: undefined;
|
|
11
|
-
} | {
|
|
12
|
-
result: undefined;
|
|
13
|
-
error: TJsonRpcError;
|
|
14
|
-
} | {
|
|
15
|
-
result: undefined;
|
|
16
|
-
error: undefined;
|
|
17
|
-
};*/
|
|
18
|
-
|
|
19
|
-
/*type TRequestHandler = (args: { method: string, params: TJsonRpcParameters | undefined }) => Promise<TRequestHandlerResponse>;*/
|
|
20
|
-
/*type TNotificationHandler = (args: { method: string, params: TJsonRpcParameters | undefined }) => void;*/
|
|
21
|
-
|
|
22
|
-
/*type TNotifyMethod = (args: { method: string, params: TJsonRpcParameters }) => void;*/
|
|
23
|
-
/*type TRequestMethod = (args: { method: string, params: TJsonRpcParameters, timeoutMs?: number }) => Promise<TRequestResult>;*/
|
|
24
|
-
|
|
25
|
-
const createJrpc = ({
|
|
26
|
-
sendMessage,
|
|
27
|
-
handleRequest,
|
|
28
|
-
handleNotification
|
|
29
|
-
}/*: {
|
|
30
|
-
sendMessage: (args: { message: TJsonRpcMessage }) => void;
|
|
31
|
-
handleRequest: TRequestHandler;
|
|
32
|
-
handleNotification: TNotificationHandler;
|
|
33
|
-
}*/) => {
|
|
34
|
-
|
|
35
|
-
let closed = false;
|
|
36
|
-
|
|
37
|
-
const receivedRequest = ({
|
|
38
|
-
id,
|
|
39
|
-
method,
|
|
40
|
-
params
|
|
41
|
-
}/*: {
|
|
42
|
-
id: TJsonRpcMandatoryId,
|
|
43
|
-
method: string,
|
|
44
|
-
params: TJsonRpcParameters | undefined
|
|
45
|
-
}*/)/*: { error: Error | undefined }*/ => {
|
|
46
|
-
|
|
47
|
-
handleRequest({ method, params }).then((response) => {
|
|
48
|
-
|
|
49
|
-
if (response.error !== undefined) {
|
|
50
|
-
sendMessage({
|
|
51
|
-
message: {
|
|
52
|
-
jsonrpc: "2.0",
|
|
53
|
-
id,
|
|
54
|
-
error: response.error
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (response.result !== undefined) {
|
|
61
|
-
sendMessage({
|
|
62
|
-
message: {
|
|
63
|
-
jsonrpc: "2.0",
|
|
64
|
-
result: response.result,
|
|
65
|
-
id
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// send nothing if we neither have a result nor an error
|
|
72
|
-
// this is a valid case
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
return {
|
|
76
|
-
error: undefined
|
|
77
|
-
};
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
let pendingRequests/*: Record<string, TPendingRequestHandle>*/ = {};
|
|
81
|
-
|
|
82
|
-
const receivedResponse = ({ id, response }/*: { id: TJsonRpcMandatoryId, response: TRequestResponse }*/)/*: { error: Error | undefined }*/ => {
|
|
83
|
-
const pendingRequest = pendingRequests[id];
|
|
84
|
-
if (pendingRequest === undefined) {
|
|
85
|
-
return {
|
|
86
|
-
error: Error("received response for non-pending id")
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
pendingRequest.resolve({
|
|
91
|
-
response,
|
|
92
|
-
error: undefined
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
return {
|
|
96
|
-
error: undefined
|
|
97
|
-
};
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// eslint-disable-next-line complexity
|
|
101
|
-
const receivedMessage = ({ message }/*: { message: unknown }*/)/*: { error: Error | undefined }*/ => {
|
|
102
|
-
if (closed) {
|
|
103
|
-
throw Error("connection closed");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const { error: coerceError, jrpcMessage } = coerceJrpcMessage({ message });
|
|
107
|
-
if (coerceError !== undefined) {
|
|
108
|
-
return {
|
|
109
|
-
error: coerceError
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (jrpcMessage.id === null) {
|
|
114
|
-
return {
|
|
115
|
-
error: Error("this library does not support null ids")
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (jrpcMessage.id === undefined) {
|
|
120
|
-
handleNotification({
|
|
121
|
-
method: jrpcMessage.method,
|
|
122
|
-
params: jrpcMessage.params
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
return { error: undefined };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (jrpcMessage.result !== undefined) {
|
|
129
|
-
return receivedResponse({
|
|
130
|
-
id: jrpcMessage.id,
|
|
131
|
-
response: {
|
|
132
|
-
result: jrpcMessage.result,
|
|
133
|
-
error: undefined
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (jrpcMessage.error !== undefined) {
|
|
139
|
-
return receivedResponse({
|
|
140
|
-
id: jrpcMessage.id,
|
|
141
|
-
response: {
|
|
142
|
-
result: undefined,
|
|
143
|
-
error: jrpcMessage.error
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return receivedRequest({
|
|
149
|
-
id: jrpcMessage.id,
|
|
150
|
-
method: jrpcMessage.method,
|
|
151
|
-
params: jrpcMessage.params
|
|
152
|
-
});
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const close = () => {
|
|
156
|
-
if (closed) {
|
|
157
|
-
throw Error("connection already closed");
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
Object.keys(pendingRequests).forEach((id) => {
|
|
161
|
-
const pendingRequest = pendingRequests[id];
|
|
162
|
-
pendingRequest.resolve({
|
|
163
|
-
error: Error("connection closed"),
|
|
164
|
-
response: undefined
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
closed = true;
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
let requestIdCounter = 0;
|
|
172
|
-
|
|
173
|
-
const request/*: TRequestMethod*/ = ({
|
|
174
|
-
method,
|
|
175
|
-
params,
|
|
176
|
-
timeoutMs
|
|
177
|
-
})/*: Promise<TRequestResult>*/ => {
|
|
178
|
-
|
|
179
|
-
const requestId = requestIdCounter;
|
|
180
|
-
requestIdCounter += 1;
|
|
181
|
-
|
|
182
|
-
let timeoutHandle/*: NodeJS.Timeout | undefined*/ = undefined;
|
|
183
|
-
if (timeoutMs !== undefined) {
|
|
184
|
-
timeoutHandle = setTimeout(() => {
|
|
185
|
-
const pendingRequest = pendingRequests[requestId];
|
|
186
|
-
pendingRequest.resolve({
|
|
187
|
-
error: Error("timeout"),
|
|
188
|
-
response: undefined
|
|
189
|
-
});
|
|
190
|
-
}, timeoutMs);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
sendMessage({
|
|
194
|
-
message: {
|
|
195
|
-
jsonrpc: "2.0",
|
|
196
|
-
method,
|
|
197
|
-
params,
|
|
198
|
-
id: requestId
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
return new Promise((resolve) => {
|
|
203
|
-
pendingRequests = {
|
|
204
|
-
...pendingRequests,
|
|
205
|
-
[requestId]: {
|
|
206
|
-
resolve: (result) => {
|
|
207
|
-
// remove the request from the pending requests
|
|
208
|
-
const { [requestId]: requestToDrop, ...otherPendingRequests } = pendingRequests;
|
|
209
|
-
pendingRequests = otherPendingRequests;
|
|
210
|
-
|
|
211
|
-
// clear the timeout
|
|
212
|
-
clearTimeout(timeoutHandle);
|
|
213
|
-
|
|
214
|
-
resolve(result);
|
|
215
|
-
},
|
|
216
|
-
timeoutHandle
|
|
217
|
-
}
|
|
218
|
-
};
|
|
219
|
-
});
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
const notify/*: TNotifyMethod*/ = ({ method, params }) => {
|
|
223
|
-
sendMessage({
|
|
224
|
-
message: {
|
|
225
|
-
jsonrpc: "2.0",
|
|
226
|
-
method,
|
|
227
|
-
params
|
|
228
|
-
}
|
|
229
|
-
});
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
return {
|
|
233
|
-
receivedMessage,
|
|
234
|
-
request,
|
|
235
|
-
notify,
|
|
236
|
-
close
|
|
237
|
-
};
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
/*type TJrpc = ReturnType<typeof createJrpc>;*/
|
|
1
|
+
import { createJrpc } from "./jrpc.js";
|
|
2
|
+
import { createWebSocketJrpc } from "./websocket.js";
|
|
241
3
|
|
|
242
4
|
export {
|
|
243
|
-
createJrpc
|
|
5
|
+
createJrpc,
|
|
6
|
+
createWebSocketJrpc
|
|
244
7
|
};
|
|
245
|
-
|
|
246
|
-
/*export type {
|
|
247
|
-
TJsonRpcMessage,
|
|
248
|
-
TRequestResponse,
|
|
249
|
-
TRequestResult,
|
|
250
|
-
TJrpc,
|
|
251
|
-
TRequestHandler,
|
|
252
|
-
TNotificationHandler,
|
|
253
|
-
TRequestMethod,
|
|
254
|
-
TNotifyMethod
|
|
255
|
-
};*/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TJsonRpcMessage, TNotificationHandler, TNotifyMethod, TRequestHandler, TRequestMethod, TRequestResponse, TRequestResult } from "./types.js";
|
|
2
|
+
declare const createJrpc: ({ sendMessage, handleRequest, handleNotification }: {
|
|
3
|
+
sendMessage: (args: {
|
|
4
|
+
message: TJsonRpcMessage;
|
|
5
|
+
}) => void;
|
|
6
|
+
handleRequest: TRequestHandler;
|
|
7
|
+
handleNotification: TNotificationHandler;
|
|
8
|
+
}) => {
|
|
9
|
+
receivedMessage: ({ message }: {
|
|
10
|
+
message: unknown;
|
|
11
|
+
}) => {
|
|
12
|
+
error: Error | undefined;
|
|
13
|
+
};
|
|
14
|
+
request: TRequestMethod;
|
|
15
|
+
notify: TNotifyMethod;
|
|
16
|
+
close: () => void;
|
|
17
|
+
};
|
|
18
|
+
type TJrpc = ReturnType<typeof createJrpc>;
|
|
19
|
+
export { createJrpc };
|
|
20
|
+
export type { TJsonRpcMessage, TRequestResponse, TRequestResult, TJrpc, TRequestHandler, TNotificationHandler, TRequestMethod, TNotifyMethod };
|
package/dist/lib/jrpc.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { coerceJrpcMessage } from "./coerce.js";
|
|
2
|
+
/*import type {
|
|
3
|
+
TJsonRpcMandatoryId,
|
|
4
|
+
TJsonRpcMessage,
|
|
5
|
+
TJsonRpcParameters,
|
|
6
|
+
TNotificationHandler,
|
|
7
|
+
TNotifyMethod,
|
|
8
|
+
TRequestHandler,
|
|
9
|
+
TRequestMethod,
|
|
10
|
+
TRequestResponse,
|
|
11
|
+
TRequestResult
|
|
12
|
+
} from "./types.ts";*/
|
|
13
|
+
|
|
14
|
+
/*type TPendingRequestHandle = {
|
|
15
|
+
resolve: (args: TRequestResult) => void;
|
|
16
|
+
};*/
|
|
17
|
+
|
|
18
|
+
const createJrpc = ({
|
|
19
|
+
sendMessage,
|
|
20
|
+
handleRequest,
|
|
21
|
+
handleNotification
|
|
22
|
+
}/*: {
|
|
23
|
+
sendMessage: (args: { message: TJsonRpcMessage }) => void;
|
|
24
|
+
handleRequest: TRequestHandler;
|
|
25
|
+
handleNotification: TNotificationHandler;
|
|
26
|
+
}*/) => {
|
|
27
|
+
|
|
28
|
+
let closed = false;
|
|
29
|
+
|
|
30
|
+
const receivedRequest = ({
|
|
31
|
+
id,
|
|
32
|
+
method,
|
|
33
|
+
params
|
|
34
|
+
}/*: {
|
|
35
|
+
id: TJsonRpcMandatoryId,
|
|
36
|
+
method: string,
|
|
37
|
+
params: TJsonRpcParameters | undefined
|
|
38
|
+
}*/)/*: { error: Error | undefined }*/ => {
|
|
39
|
+
|
|
40
|
+
handleRequest({ method, params }).then((response) => {
|
|
41
|
+
|
|
42
|
+
if (response.error !== undefined) {
|
|
43
|
+
sendMessage({
|
|
44
|
+
message: {
|
|
45
|
+
jsonrpc: "2.0",
|
|
46
|
+
id,
|
|
47
|
+
error: response.error
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (response.result !== undefined) {
|
|
54
|
+
sendMessage({
|
|
55
|
+
message: {
|
|
56
|
+
jsonrpc: "2.0",
|
|
57
|
+
result: response.result,
|
|
58
|
+
id
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// send nothing if we neither have a result nor an error
|
|
65
|
+
// this is a valid case
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
error: undefined
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
let pendingRequests/*: Record<string, TPendingRequestHandle>*/ = {};
|
|
74
|
+
|
|
75
|
+
const receivedResponse = ({ id, response }/*: { id: TJsonRpcMandatoryId, response: TRequestResponse }*/)/*: { error: Error | undefined }*/ => {
|
|
76
|
+
const pendingRequest = pendingRequests[id];
|
|
77
|
+
if (pendingRequest === undefined) {
|
|
78
|
+
return {
|
|
79
|
+
error: Error("received response for non-pending id")
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
pendingRequest.resolve({
|
|
84
|
+
response,
|
|
85
|
+
error: undefined
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
error: undefined
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
// eslint-disable-next-line complexity
|
|
94
|
+
const receivedMessage = ({ message }/*: { message: unknown }*/)/*: { error: Error | undefined }*/ => {
|
|
95
|
+
if (closed) {
|
|
96
|
+
throw Error("connection closed");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const { error: coerceError, jrpcMessage } = coerceJrpcMessage({ message });
|
|
100
|
+
if (coerceError !== undefined) {
|
|
101
|
+
return {
|
|
102
|
+
error: coerceError
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (jrpcMessage.id === null) {
|
|
107
|
+
return {
|
|
108
|
+
error: Error("this library does not support null ids")
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (jrpcMessage.id === undefined) {
|
|
113
|
+
handleNotification({
|
|
114
|
+
method: jrpcMessage.method,
|
|
115
|
+
params: jrpcMessage.params
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
return { error: undefined };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (jrpcMessage.result !== undefined) {
|
|
122
|
+
return receivedResponse({
|
|
123
|
+
id: jrpcMessage.id,
|
|
124
|
+
response: {
|
|
125
|
+
result: jrpcMessage.result,
|
|
126
|
+
error: undefined
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (jrpcMessage.error !== undefined) {
|
|
132
|
+
return receivedResponse({
|
|
133
|
+
id: jrpcMessage.id,
|
|
134
|
+
response: {
|
|
135
|
+
result: undefined,
|
|
136
|
+
error: jrpcMessage.error
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return receivedRequest({
|
|
142
|
+
id: jrpcMessage.id,
|
|
143
|
+
method: jrpcMessage.method,
|
|
144
|
+
params: jrpcMessage.params
|
|
145
|
+
});
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const close = () => {
|
|
149
|
+
if (closed) {
|
|
150
|
+
throw Error("connection already closed");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
Object.keys(pendingRequests).forEach((id) => {
|
|
154
|
+
const pendingRequest = pendingRequests[id];
|
|
155
|
+
pendingRequest.resolve({
|
|
156
|
+
error: Error("connection closed"),
|
|
157
|
+
response: undefined
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
closed = true;
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
let requestIdCounter = 0;
|
|
165
|
+
|
|
166
|
+
const request/*: TRequestMethod*/ = ({
|
|
167
|
+
method,
|
|
168
|
+
params,
|
|
169
|
+
timeoutMs
|
|
170
|
+
})/*: Promise<TRequestResult>*/ => {
|
|
171
|
+
|
|
172
|
+
const requestId = requestIdCounter;
|
|
173
|
+
requestIdCounter += 1;
|
|
174
|
+
|
|
175
|
+
let timeoutHandle/*: NodeJS.Timeout | undefined*/ = undefined;
|
|
176
|
+
if (timeoutMs !== undefined) {
|
|
177
|
+
timeoutHandle = setTimeout(() => {
|
|
178
|
+
const pendingRequest = pendingRequests[requestId];
|
|
179
|
+
pendingRequest.resolve({
|
|
180
|
+
error: Error("timeout"),
|
|
181
|
+
response: undefined
|
|
182
|
+
});
|
|
183
|
+
}, timeoutMs);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
sendMessage({
|
|
187
|
+
message: {
|
|
188
|
+
jsonrpc: "2.0",
|
|
189
|
+
method,
|
|
190
|
+
params,
|
|
191
|
+
id: requestId
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
return new Promise((resolve) => {
|
|
196
|
+
pendingRequests = {
|
|
197
|
+
...pendingRequests,
|
|
198
|
+
[requestId]: {
|
|
199
|
+
resolve: (result) => {
|
|
200
|
+
// remove the request from the pending requests
|
|
201
|
+
const { [requestId]: requestToDrop, ...otherPendingRequests } = pendingRequests;
|
|
202
|
+
pendingRequests = otherPendingRequests;
|
|
203
|
+
|
|
204
|
+
// clear the timeout
|
|
205
|
+
clearTimeout(timeoutHandle);
|
|
206
|
+
|
|
207
|
+
resolve(result);
|
|
208
|
+
},
|
|
209
|
+
timeoutHandle
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
const notify/*: TNotifyMethod*/ = ({ method, params }) => {
|
|
216
|
+
sendMessage({
|
|
217
|
+
message: {
|
|
218
|
+
jsonrpc: "2.0",
|
|
219
|
+
method,
|
|
220
|
+
params
|
|
221
|
+
}
|
|
222
|
+
});
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
receivedMessage,
|
|
227
|
+
request,
|
|
228
|
+
notify,
|
|
229
|
+
close
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
/*type TJrpc = ReturnType<typeof createJrpc>;*/
|
|
234
|
+
|
|
235
|
+
export {
|
|
236
|
+
createJrpc
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
/*export type {
|
|
240
|
+
TJsonRpcMessage,
|
|
241
|
+
TRequestResponse,
|
|
242
|
+
TRequestResult,
|
|
243
|
+
TJrpc,
|
|
244
|
+
TRequestHandler,
|
|
245
|
+
TNotificationHandler,
|
|
246
|
+
TRequestMethod,
|
|
247
|
+
TNotifyMethod
|
|
248
|
+
};*/
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -65,4 +65,31 @@ type TRequestResult = {
|
|
|
65
65
|
error: undefined;
|
|
66
66
|
response: TRequestResponse;
|
|
67
67
|
};
|
|
68
|
-
|
|
68
|
+
type TRequestHandlerResponse = {
|
|
69
|
+
result: TRequestResponseValue;
|
|
70
|
+
error: undefined;
|
|
71
|
+
} | {
|
|
72
|
+
result: undefined;
|
|
73
|
+
error: TJsonRpcError;
|
|
74
|
+
} | {
|
|
75
|
+
result: undefined;
|
|
76
|
+
error: undefined;
|
|
77
|
+
};
|
|
78
|
+
type TRequestHandler = (args: {
|
|
79
|
+
method: string;
|
|
80
|
+
params: TJsonRpcParameters | undefined;
|
|
81
|
+
}) => Promise<TRequestHandlerResponse>;
|
|
82
|
+
type TNotificationHandler = (args: {
|
|
83
|
+
method: string;
|
|
84
|
+
params: TJsonRpcParameters | undefined;
|
|
85
|
+
}) => void;
|
|
86
|
+
type TNotifyMethod = (args: {
|
|
87
|
+
method: string;
|
|
88
|
+
params: TJsonRpcParameters;
|
|
89
|
+
}) => void;
|
|
90
|
+
type TRequestMethod = (args: {
|
|
91
|
+
method: string;
|
|
92
|
+
params: TJsonRpcParameters;
|
|
93
|
+
timeoutMs?: number;
|
|
94
|
+
}) => Promise<TRequestResult>;
|
|
95
|
+
export type { TJsonRpcRequest, TJsonRpcResponse, TJsonRpcNotification, TJsonRpcSuccessResponse, TJsonRpcErrorResponse, TJsonRpcMessage, TJsonRpcParameters, TJsonRpcOptionalId, TJsonRpcMandatoryId, TRequestResponse, TRequestResult, TRequestErrorResponse, TRequestResponseValue, TJsonRpcError, TRequestHandlerResponse, TRequestHandler, TNotificationHandler, TNotifyMethod, TRequestMethod };
|
package/dist/lib/types.js
CHANGED
|
@@ -79,6 +79,26 @@
|
|
|
79
79
|
response: TRequestResponse;
|
|
80
80
|
};*/
|
|
81
81
|
|
|
82
|
+
|
|
83
|
+
// -----------
|
|
84
|
+
|
|
85
|
+
/*type TRequestHandlerResponse = {
|
|
86
|
+
result: TRequestResponseValue;
|
|
87
|
+
error: undefined;
|
|
88
|
+
} | {
|
|
89
|
+
result: undefined;
|
|
90
|
+
error: TJsonRpcError;
|
|
91
|
+
} | {
|
|
92
|
+
result: undefined;
|
|
93
|
+
error: undefined;
|
|
94
|
+
};*/
|
|
95
|
+
|
|
96
|
+
/*type TRequestHandler = (args: { method: string, params: TJsonRpcParameters | undefined }) => Promise<TRequestHandlerResponse>;*/
|
|
97
|
+
/*type TNotificationHandler = (args: { method: string, params: TJsonRpcParameters | undefined }) => void;*/
|
|
98
|
+
|
|
99
|
+
/*type TNotifyMethod = (args: { method: string, params: TJsonRpcParameters }) => void;*/
|
|
100
|
+
/*type TRequestMethod = (args: { method: string, params: TJsonRpcParameters, timeoutMs?: number }) => Promise<TRequestResult>;*/
|
|
101
|
+
|
|
82
102
|
/*export type {
|
|
83
103
|
TJsonRpcRequest,
|
|
84
104
|
TJsonRpcResponse,
|
|
@@ -94,5 +114,13 @@
|
|
|
94
114
|
TRequestErrorResponse,
|
|
95
115
|
TRequestResponseValue,
|
|
96
116
|
TJsonRpcError,
|
|
117
|
+
|
|
118
|
+
// -------
|
|
119
|
+
|
|
120
|
+
TRequestHandlerResponse,
|
|
121
|
+
TRequestHandler,
|
|
122
|
+
TNotificationHandler,
|
|
123
|
+
TNotifyMethod,
|
|
124
|
+
TRequestMethod
|
|
97
125
|
};*/
|
|
98
126
|
/* c8 ignore end */
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TNotificationHandler, TRequestHandler } from "./jrpc.js";
|
|
2
|
+
type TMessageParseResult = {
|
|
3
|
+
error: Error;
|
|
4
|
+
message: undefined;
|
|
5
|
+
} | {
|
|
6
|
+
error: undefined;
|
|
7
|
+
message: unknown;
|
|
8
|
+
};
|
|
9
|
+
type TWebSocketMessageParser = {
|
|
10
|
+
parse: (args: {
|
|
11
|
+
data: string | Uint8Array;
|
|
12
|
+
}) => TMessageParseResult;
|
|
13
|
+
format: (args: {
|
|
14
|
+
message: unknown;
|
|
15
|
+
}) => string | Uint8Array;
|
|
16
|
+
};
|
|
17
|
+
declare const createWebSocketJrpc: ({ socket, parser, handleRequest, handleNotification, onConnectionError, onRemoteClose }: {
|
|
18
|
+
socket: WebSocket;
|
|
19
|
+
parser: TWebSocketMessageParser;
|
|
20
|
+
handleRequest: TRequestHandler;
|
|
21
|
+
handleNotification: TNotificationHandler;
|
|
22
|
+
onConnectionError: (args: {
|
|
23
|
+
error: Error;
|
|
24
|
+
}) => void;
|
|
25
|
+
onRemoteClose: () => void;
|
|
26
|
+
}) => {
|
|
27
|
+
request: any;
|
|
28
|
+
notify: any;
|
|
29
|
+
bufferedAmount: () => any;
|
|
30
|
+
close: () => void;
|
|
31
|
+
};
|
|
32
|
+
type TWebsocketJrpcHandle = ReturnType<typeof createWebSocketJrpc>;
|
|
33
|
+
declare const createJsonParser: () => TWebSocketMessageParser;
|
|
34
|
+
export { createWebSocketJrpc, createJsonParser };
|
|
35
|
+
export type { TWebsocketJrpcHandle, };
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import { createJrpc } from "./index.js";
|
|
2
|
+
/*import type { TJsonRpcMessage, TNotificationHandler, TRequestHandler } from "./jrpc.ts";*/
|
|
3
|
+
|
|
4
|
+
/*type TMessageParseResult = {
|
|
5
|
+
error: Error;
|
|
6
|
+
message: undefined;
|
|
7
|
+
} | {
|
|
8
|
+
error: undefined;
|
|
9
|
+
message: unknown;
|
|
10
|
+
};*/
|
|
11
|
+
|
|
12
|
+
/*type TWebSocketMessageParser = {
|
|
13
|
+
parse: (args: { data: string | Uint8Array }) => TMessageParseResult;
|
|
14
|
+
format: (args: { message: unknown }) => string | Uint8Array;
|
|
15
|
+
};*/
|
|
16
|
+
|
|
17
|
+
const createWebSocketJrpc = ({
|
|
18
|
+
socket,
|
|
19
|
+
|
|
20
|
+
parser,
|
|
21
|
+
|
|
22
|
+
handleRequest,
|
|
23
|
+
handleNotification,
|
|
24
|
+
|
|
25
|
+
onConnectionError,
|
|
26
|
+
onRemoteClose
|
|
27
|
+
}/*: {
|
|
28
|
+
socket: WebSocket,
|
|
29
|
+
|
|
30
|
+
parser: TWebSocketMessageParser;
|
|
31
|
+
|
|
32
|
+
handleRequest: TRequestHandler;
|
|
33
|
+
handleNotification: TNotificationHandler;
|
|
34
|
+
|
|
35
|
+
onConnectionError: (args: { error: Error }) => void;
|
|
36
|
+
onRemoteClose: () => void;
|
|
37
|
+
}*/) => {
|
|
38
|
+
|
|
39
|
+
let closedByUs = false;
|
|
40
|
+
let connectionError/*: Error | undefined*/ = undefined;
|
|
41
|
+
socket.binaryType = "arraybuffer";
|
|
42
|
+
|
|
43
|
+
let sendQueue/*: (string | Uint8Array)[]*/ = [];
|
|
44
|
+
|
|
45
|
+
const maybeSendNext = () => {
|
|
46
|
+
if (socket.readyState !== WebSocket.OPEN) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
sendQueue.forEach((data) => {
|
|
51
|
+
socket.send(data);
|
|
52
|
+
});
|
|
53
|
+
sendQueue = [];
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const sendOrQueueRawMessage = ({ data }/*: { data: string | Uint8Array }*/) => {
|
|
57
|
+
sendQueue.push(data);
|
|
58
|
+
maybeSendNext();
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const sendMessage = ({ message }/*: { message: TJsonRpcMessage }*/) => {
|
|
62
|
+
const encoded = parser.format({ message });
|
|
63
|
+
sendOrQueueRawMessage({ data: encoded });
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const jrpc = createJrpc({
|
|
67
|
+
sendMessage,
|
|
68
|
+
handleRequest,
|
|
69
|
+
handleNotification
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const dataFromMessageEvent = ({ messageEvent }/*: { messageEvent: MessageEvent }*/)/*: string | Uint8Array*/ => {
|
|
73
|
+
if (typeof messageEvent.data === "string") {
|
|
74
|
+
return messageEvent.data;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (messageEvent.data instanceof ArrayBuffer) {
|
|
78
|
+
return new Uint8Array(messageEvent.data);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
throw Error("unsupported WebSocket data type");
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
socket.addEventListener("message", (event) => {
|
|
85
|
+
|
|
86
|
+
if (closedByUs) {
|
|
87
|
+
throw Error("WebSocket was closed by us, but received a message from remote");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const data = dataFromMessageEvent({ messageEvent: event });
|
|
91
|
+
|
|
92
|
+
const { error: parseError, message } = parser.parse({ data });
|
|
93
|
+
if (parseError !== undefined) {
|
|
94
|
+
closedByUs = true;
|
|
95
|
+
socket.close();
|
|
96
|
+
onConnectionError({
|
|
97
|
+
error: Error("failed to parse WebSocket message", { cause: parseError })
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const { error: jrpcError } = jrpc.receivedMessage({ message });
|
|
103
|
+
if (jrpcError !== undefined) {
|
|
104
|
+
closedByUs = true;
|
|
105
|
+
socket.close();
|
|
106
|
+
onConnectionError({
|
|
107
|
+
error: Error("failed to handle JRPC message", { cause: jrpcError })
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
socket.addEventListener("close", () => {
|
|
114
|
+
sendQueue = [];
|
|
115
|
+
jrpc.close();
|
|
116
|
+
|
|
117
|
+
if (closedByUs) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (connectionError !== undefined) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
onRemoteClose();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
socket.addEventListener("error", () => {
|
|
129
|
+
if (closedByUs) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const error = Error("WebSocket connection error");
|
|
134
|
+
connectionError = error;
|
|
135
|
+
|
|
136
|
+
onConnectionError({ error });
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const close = () => {
|
|
140
|
+
closedByUs = true;
|
|
141
|
+
socket.close();
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const bufferedAmount = () => {
|
|
145
|
+
return socket.bufferedAmount;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
request: jrpc.request,
|
|
150
|
+
notify: jrpc.notify,
|
|
151
|
+
|
|
152
|
+
bufferedAmount,
|
|
153
|
+
|
|
154
|
+
close
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/*type TWebsocketJrpcHandle = ReturnType<typeof createWebSocketJrpc>;*/
|
|
159
|
+
|
|
160
|
+
const createJsonParser = ()/*: TWebSocketMessageParser*/ => {
|
|
161
|
+
|
|
162
|
+
const parse/*: TWebSocketMessageParser["parse"]*/ = ({ data }) => {
|
|
163
|
+
|
|
164
|
+
if (typeof data === "string") {
|
|
165
|
+
try {
|
|
166
|
+
const message = JSON.parse(data);
|
|
167
|
+
return {
|
|
168
|
+
error: undefined,
|
|
169
|
+
message
|
|
170
|
+
};
|
|
171
|
+
} catch (err) {
|
|
172
|
+
return {
|
|
173
|
+
error: err /*as Error*/,
|
|
174
|
+
message: undefined
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
error: Error("unsupported WebSocket data type"),
|
|
181
|
+
message: undefined
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
const format/*: TWebSocketMessageParser["format"]*/ = ({ message }) => {
|
|
186
|
+
return JSON.stringify(message);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
parse,
|
|
191
|
+
format
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export {
|
|
196
|
+
createWebSocketJrpc,
|
|
197
|
+
createJsonParser
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
/*export type {
|
|
201
|
+
TWebsocketJrpcHandle,
|
|
202
|
+
};*/
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.0.
|
|
2
|
+
"version": "0.0.3",
|
|
3
3
|
"name": "@k13engineering/yajrpc",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Boilerplate project",
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"@types/node": "^25.0.10",
|
|
16
16
|
"c8": "^10.1.3",
|
|
17
17
|
"deno-node": "^0.0.12",
|
|
18
|
+
"isomorphic-ws": "^5.0.0",
|
|
18
19
|
"mocha": "^11.7.5",
|
|
19
20
|
"npm-check-updates": "^19.3.1",
|
|
20
21
|
"typescript": "^5.9.3",
|