@k13engineering/yajrpc 0.0.1
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/LICENSE +504 -0
- package/README.md +1 -0
- package/dist/lib/coerce.d.ts +87 -0
- package/dist/lib/coerce.js +524 -0
- package/dist/lib/index.d.ts +47 -0
- package/dist/lib/index.js +255 -0
- package/dist/lib/types.d.ts +68 -0
- package/dist/lib/types.js +98 -0
- package/package.json +47 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { coerceJrpcMessage } from "./coerce.js";
|
|
2
|
+
/*import type { TJsonRpcError, TJsonRpcMandatoryId, TJsonRpcMessage, TJsonRpcParameters, TRequestResponse, TRequestResponseValue, TRequestResult } from "./types.ts";*/
|
|
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>;*/
|
|
241
|
+
|
|
242
|
+
export {
|
|
243
|
+
createJrpc
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
/*export type {
|
|
247
|
+
TJsonRpcMessage,
|
|
248
|
+
TRequestResponse,
|
|
249
|
+
TRequestResult,
|
|
250
|
+
TJrpc,
|
|
251
|
+
TRequestHandler,
|
|
252
|
+
TNotificationHandler,
|
|
253
|
+
TRequestMethod,
|
|
254
|
+
TNotifyMethod
|
|
255
|
+
};*/
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
type TJsonRpcParameterValue = Exclude<unknown, undefined>;
|
|
2
|
+
type TRequestResponseValue = NonNullable<unknown> | null;
|
|
3
|
+
type TJsonRpcParameters = Array<TJsonRpcParameterValue> | Record<string, TJsonRpcParameterValue>;
|
|
4
|
+
type TJsonRpcOptionalId = string | number | null;
|
|
5
|
+
type TJsonRpcMandatoryId = string | number;
|
|
6
|
+
type TJsonRpcError = {
|
|
7
|
+
code: number;
|
|
8
|
+
message: string;
|
|
9
|
+
data?: unknown;
|
|
10
|
+
};
|
|
11
|
+
type TJsonRpcRequest = {
|
|
12
|
+
jsonrpc: "2.0";
|
|
13
|
+
id: TJsonRpcOptionalId;
|
|
14
|
+
method: string;
|
|
15
|
+
params?: TJsonRpcParameters;
|
|
16
|
+
result?: undefined;
|
|
17
|
+
error?: undefined;
|
|
18
|
+
};
|
|
19
|
+
type TJsonRpcNotification = {
|
|
20
|
+
jsonrpc: "2.0";
|
|
21
|
+
id?: undefined;
|
|
22
|
+
method: string;
|
|
23
|
+
params?: TJsonRpcParameters;
|
|
24
|
+
result?: undefined;
|
|
25
|
+
error?: undefined;
|
|
26
|
+
};
|
|
27
|
+
type TJsonRpcSuccessResponse = {
|
|
28
|
+
jsonrpc: "2.0";
|
|
29
|
+
id: TJsonRpcMandatoryId;
|
|
30
|
+
method?: undefined;
|
|
31
|
+
params?: undefined;
|
|
32
|
+
result: TRequestResponseValue;
|
|
33
|
+
error?: undefined;
|
|
34
|
+
};
|
|
35
|
+
type TJsonRpcErrorResponse = {
|
|
36
|
+
jsonrpc: "2.0";
|
|
37
|
+
id: TJsonRpcOptionalId;
|
|
38
|
+
method?: undefined;
|
|
39
|
+
params?: undefined;
|
|
40
|
+
result?: undefined;
|
|
41
|
+
error: TJsonRpcError;
|
|
42
|
+
};
|
|
43
|
+
type TJsonRpcResponse = TJsonRpcSuccessResponse | TJsonRpcErrorResponse;
|
|
44
|
+
type TJsonRpcMessage = TJsonRpcRequest | TJsonRpcNotification | TJsonRpcResponse;
|
|
45
|
+
type TRequestErrorResponse = {
|
|
46
|
+
result?: never;
|
|
47
|
+
error: {
|
|
48
|
+
code: number;
|
|
49
|
+
message: string;
|
|
50
|
+
data?: unknown;
|
|
51
|
+
};
|
|
52
|
+
ignore?: never;
|
|
53
|
+
};
|
|
54
|
+
type TRequestResponse = {
|
|
55
|
+
error: TJsonRpcError;
|
|
56
|
+
result: undefined;
|
|
57
|
+
} | {
|
|
58
|
+
error: undefined;
|
|
59
|
+
result: TRequestResponseValue;
|
|
60
|
+
};
|
|
61
|
+
type TRequestResult = {
|
|
62
|
+
error: Error;
|
|
63
|
+
response: undefined;
|
|
64
|
+
} | {
|
|
65
|
+
error: undefined;
|
|
66
|
+
response: TRequestResponse;
|
|
67
|
+
};
|
|
68
|
+
export type { TJsonRpcRequest, TJsonRpcResponse, TJsonRpcNotification, TJsonRpcSuccessResponse, TJsonRpcErrorResponse, TJsonRpcMessage, TJsonRpcParameters, TJsonRpcOptionalId, TJsonRpcMandatoryId, TRequestResponse, TRequestResult, TRequestErrorResponse, TRequestResponseValue, TJsonRpcError, };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/* c8 ignore start */
|
|
2
|
+
/*type TJsonRpcParameterValue = Exclude<unknown, undefined>;*/
|
|
3
|
+
/*type TRequestResponseValue = NonNullable<unknown> | null;*/
|
|
4
|
+
|
|
5
|
+
/*type TJsonRpcParameters = Array<TJsonRpcParameterValue> | Record<string, TJsonRpcParameterValue>;*/
|
|
6
|
+
|
|
7
|
+
/*type TJsonRpcOptionalId = string | number | null;*/
|
|
8
|
+
/*type TJsonRpcMandatoryId = string | number;*/
|
|
9
|
+
|
|
10
|
+
/*type TJsonRpcError = {
|
|
11
|
+
code: number;
|
|
12
|
+
message: string;
|
|
13
|
+
data?: unknown;
|
|
14
|
+
};*/
|
|
15
|
+
|
|
16
|
+
/*type TJsonRpcRequest = {
|
|
17
|
+
jsonrpc: "2.0";
|
|
18
|
+
id: TJsonRpcOptionalId;
|
|
19
|
+
method: string;
|
|
20
|
+
params?: TJsonRpcParameters;
|
|
21
|
+
result?: undefined;
|
|
22
|
+
error?: undefined;
|
|
23
|
+
};*/
|
|
24
|
+
|
|
25
|
+
/*type TJsonRpcNotification = {
|
|
26
|
+
jsonrpc: "2.0";
|
|
27
|
+
id?: undefined;
|
|
28
|
+
method: string;
|
|
29
|
+
params?: TJsonRpcParameters;
|
|
30
|
+
result?: undefined;
|
|
31
|
+
error?: undefined;
|
|
32
|
+
};*/
|
|
33
|
+
|
|
34
|
+
/*type TJsonRpcSuccessResponse = {
|
|
35
|
+
jsonrpc: "2.0";
|
|
36
|
+
id: TJsonRpcMandatoryId;
|
|
37
|
+
method?: undefined;
|
|
38
|
+
params?: undefined;
|
|
39
|
+
result: TRequestResponseValue;
|
|
40
|
+
error?: undefined;
|
|
41
|
+
};*/
|
|
42
|
+
|
|
43
|
+
/*type TJsonRpcErrorResponse = {
|
|
44
|
+
jsonrpc: "2.0";
|
|
45
|
+
id: TJsonRpcOptionalId;
|
|
46
|
+
method?: undefined;
|
|
47
|
+
params?: undefined;
|
|
48
|
+
result?: undefined;
|
|
49
|
+
error: TJsonRpcError;
|
|
50
|
+
};*/
|
|
51
|
+
|
|
52
|
+
/*type TJsonRpcResponse = TJsonRpcSuccessResponse | TJsonRpcErrorResponse;*/
|
|
53
|
+
|
|
54
|
+
/*type TJsonRpcMessage = TJsonRpcRequest | TJsonRpcNotification | TJsonRpcResponse;*/
|
|
55
|
+
|
|
56
|
+
/*type TRequestErrorResponse = {
|
|
57
|
+
result?: never;
|
|
58
|
+
error: {
|
|
59
|
+
code: number;
|
|
60
|
+
message: string;
|
|
61
|
+
data?: unknown;
|
|
62
|
+
};
|
|
63
|
+
ignore?: never;
|
|
64
|
+
};*/
|
|
65
|
+
|
|
66
|
+
/*type TRequestResponse = {
|
|
67
|
+
error: TJsonRpcError;
|
|
68
|
+
result: undefined;
|
|
69
|
+
} | {
|
|
70
|
+
error: undefined;
|
|
71
|
+
result: TRequestResponseValue;
|
|
72
|
+
};*/
|
|
73
|
+
|
|
74
|
+
/*type TRequestResult = {
|
|
75
|
+
error: Error;
|
|
76
|
+
response: undefined;
|
|
77
|
+
} | {
|
|
78
|
+
error: undefined;
|
|
79
|
+
response: TRequestResponse;
|
|
80
|
+
};*/
|
|
81
|
+
|
|
82
|
+
/*export type {
|
|
83
|
+
TJsonRpcRequest,
|
|
84
|
+
TJsonRpcResponse,
|
|
85
|
+
TJsonRpcNotification,
|
|
86
|
+
TJsonRpcSuccessResponse,
|
|
87
|
+
TJsonRpcErrorResponse,
|
|
88
|
+
TJsonRpcMessage,
|
|
89
|
+
TJsonRpcParameters,
|
|
90
|
+
TJsonRpcOptionalId,
|
|
91
|
+
TJsonRpcMandatoryId,
|
|
92
|
+
TRequestResponse,
|
|
93
|
+
TRequestResult,
|
|
94
|
+
TRequestErrorResponse,
|
|
95
|
+
TRequestResponseValue,
|
|
96
|
+
TJsonRpcError,
|
|
97
|
+
};*/
|
|
98
|
+
/* c8 ignore end */
|
package/package.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.0.1",
|
|
3
|
+
"name": "@k13engineering/yajrpc",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Boilerplate project",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"main": "dist/lib/index.js",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "rm -rf dist/ && deno-node-build --root . --out dist/ --entry lib/index.ts",
|
|
12
|
+
"type-check": "tsc --noEmit",
|
|
13
|
+
"test": "c8 --reporter lcov --reporter html --reporter text --all --src lib/ --exclude lib/snippets/ --exclude test/ mocha test/**/*.ts",
|
|
14
|
+
"lint": "eslint .",
|
|
15
|
+
"update-deps": "npm-check-updates -u"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@eslint/js": "^9.39.2",
|
|
19
|
+
"@k13engineering/releasetool": "^0.0.5",
|
|
20
|
+
"@types/mocha": "^10.0.10",
|
|
21
|
+
"@types/node": "^25.0.10",
|
|
22
|
+
"c8": "^10.1.3",
|
|
23
|
+
"deno-node": "^0.0.12",
|
|
24
|
+
"mocha": "^11.7.5",
|
|
25
|
+
"npm-check-updates": "^19.3.1",
|
|
26
|
+
"typescript": "^5.9.3",
|
|
27
|
+
"typescript-eslint": "^8.53.1"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/k13-engineering/yajrpc.git"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"node",
|
|
39
|
+
"jrpc"
|
|
40
|
+
],
|
|
41
|
+
"author": "Simon Kadisch",
|
|
42
|
+
"license": "LGPL 2.1",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/k13-engineering/yajrpc/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/k13-engineering/yajrpc#readme"
|
|
47
|
+
}
|