@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.
@@ -1,47 +1,3 @@
1
- import type { TJsonRpcError, TJsonRpcMessage, TJsonRpcParameters, TRequestResponse, TRequestResponseValue, TRequestResult } from "./types.js";
2
- type TRequestHandlerResponse = {
3
- result: TRequestResponseValue;
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 { 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>;*/
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 };
@@ -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
+ };*/
@@ -65,4 +65,31 @@ type TRequestResult = {
65
65
  error: undefined;
66
66
  response: TRequestResponse;
67
67
  };
68
- export type { TJsonRpcRequest, TJsonRpcResponse, TJsonRpcNotification, TJsonRpcSuccessResponse, TJsonRpcErrorResponse, TJsonRpcMessage, TJsonRpcParameters, TJsonRpcOptionalId, TJsonRpcMandatoryId, TRequestResponse, TRequestResult, TRequestErrorResponse, TRequestResponseValue, TJsonRpcError, };
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",
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",