@k13engineering/yajrpc 0.0.2 → 0.0.4

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.
@@ -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.4",
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",