cojson-transport-ws 0.19.22 → 0.20.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.
@@ -1,4 +1,4 @@
1
1
 
2
- > cojson-transport-ws@0.19.22 build /home/runner/_work/jazz/jazz/packages/cojson-transport-ws
2
+ > cojson-transport-ws@0.20.1 build /home/runner/_work/jazz/jazz/packages/cojson-transport-ws
3
3
  > rm -rf ./dist && tsc --sourceMap --outDir dist
4
4
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # cojson-transport-nodejs-ws
2
2
 
3
+ ## 0.20.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [03195eb]
8
+ - cojson@0.20.1
9
+
10
+ ## 0.20.0
11
+
12
+ ### Patch Changes
13
+
14
+ - Updated dependencies [6b9368a]
15
+ - Updated dependencies [89332d5]
16
+ - Updated dependencies [f562a1f]
17
+ - Updated dependencies [b5ada4d]
18
+ - Updated dependencies [8934d8a]
19
+ - cojson@0.20.0
20
+
3
21
  ## 0.19.22
4
22
 
5
23
  ### Patch Changes
@@ -23,6 +23,8 @@ export declare class BatchedOutgoingMessages implements CojsonInternalTypes.Outg
23
23
  push(msg: SyncMessage | DisconnectedError): void;
24
24
  private processQueue;
25
25
  private processMessage;
26
+ private serializeMessage;
27
+ private appendMessage;
26
28
  private sendMessagesInBulk;
27
29
  drain(): void;
28
30
  setBatching(enabled: boolean): void;
@@ -1 +1 @@
1
- {"version":3,"file":"BatchedOutgoingMessages.d.ts","sourceRoot":"","sources":["../src/BatchedOutgoingMessages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAsB,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EACL,KAAK,mBAAmB,EAIzB,MAAM,QAAQ,CAAC;AAChB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAW/C,qBAAa,uBACX,YAAW,mBAAmB,CAAC,mBAAmB;IAShD,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,QAAQ;IAEhB;;OAEG;IACH,OAAO,CAAC,IAAI,CAAC;IAbf,OAAO,CAAC,OAAO,CAAM;IACrB,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,kBAAkB,CAAC;gBAGjB,SAAS,EAAE,YAAY,EACvB,QAAQ,EAAE,OAAO,EACzB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC;IACtB;;OAEG;IACK,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,YAAA,EAC9C,KAAK,CAAC,EAAE,KAAK;IAsBf,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,iBAAiB;YAiB3B,YAAY;IAqC1B,OAAO,CAAC,cAAc;IAmCtB,OAAO,CAAC,kBAAkB;IAO1B,KAAK;IAIL,WAAW,CAAC,OAAO,EAAE,OAAO;IAI5B,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI;IAI5B,KAAK;CAqBN"}
1
+ {"version":3,"file":"BatchedOutgoingMessages.d.ts","sourceRoot":"","sources":["../src/BatchedOutgoingMessages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAsB,MAAM,oBAAoB,CAAC;AACpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,EACL,KAAK,mBAAmB,EAIzB,MAAM,QAAQ,CAAC;AAChB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAW/C,qBAAa,uBACX,YAAW,mBAAmB,CAAC,mBAAmB;IAShD,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,QAAQ;IAEhB;;OAEG;IACH,OAAO,CAAC,IAAI,CAAC;IAbf,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,KAAK,CAA4B;IACzC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,kBAAkB,CAAC;gBAGjB,SAAS,EAAE,YAAY,EACvB,QAAQ,EAAE,OAAO,EACzB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC;IACtB;;OAEG;IACK,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,YAAA,EAC9C,KAAK,CAAC,EAAE,KAAK;IAsBf,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,iBAAiB;YAyB3B,YAAY;IA+B1B,OAAO,CAAC,cAAc;IA+BtB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,kBAAkB;IAO1B,KAAK;IAIL,WAAW,CAAC,OAAO,EAAE,OAAO;IAI5B,OAAO,CAAC,cAAc,CAAyB;IAC/C,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI;IAI5B,KAAK;CAqBN"}
@@ -11,7 +11,7 @@ export class BatchedOutgoingMessages {
11
11
  this.websocket = websocket;
12
12
  this.batching = batching;
13
13
  this.meta = meta;
14
- this.backlog = "";
14
+ this.backlog = [];
15
15
  this.processing = false;
16
16
  this.closed = false;
17
17
  this.closeListeners = new Set();
@@ -35,6 +35,11 @@ export class BatchedOutgoingMessages {
35
35
  if (this.processing) {
36
36
  return;
37
37
  }
38
+ if (isWebSocketOpen(this.websocket) &&
39
+ !hasWebSocketTooMuchBufferedData(this.websocket)) {
40
+ this.processMessage(msg, true);
41
+ return;
42
+ }
38
43
  this.processQueue().catch((e) => {
39
44
  logger.error("Error while processing sendMessage queue", { err: e });
40
45
  });
@@ -42,9 +47,6 @@ export class BatchedOutgoingMessages {
42
47
  async processQueue() {
43
48
  const { websocket } = this;
44
49
  this.processing = true;
45
- // Delay the initiation of the queue processing to accumulate messages
46
- // before sending them, in order to do prioritization and batching
47
- await new Promise((resolve) => setTimeout(resolve, WEBSOCKET_CONFIG.OUTGOING_MESSAGES_CHUNK_DELAY));
48
50
  let msg = this.queue.pull();
49
51
  while (msg) {
50
52
  if (this.closed) {
@@ -64,12 +66,12 @@ export class BatchedOutgoingMessages {
64
66
  this.sendMessagesInBulk();
65
67
  this.processing = false;
66
68
  }
67
- processMessage(msg) {
69
+ processMessage(msg, skipBatching = false) {
68
70
  if (msg.action === "content") {
69
71
  this.egressBytesCounter.add(getContentMessageSize(msg), this.meta);
70
72
  }
71
- const stringifiedMsg = JSON.stringify(msg);
72
- if (!this.batching) {
73
+ const stringifiedMsg = this.serializeMessage(msg);
74
+ if (!this.batching || skipBatching) {
73
75
  this.websocket.send(stringifiedMsg);
74
76
  return;
75
77
  }
@@ -80,21 +82,22 @@ export class BatchedOutgoingMessages {
80
82
  newBacklogSize > WEBSOCKET_CONFIG.MAX_OUTGOING_MESSAGES_CHUNK_BYTES) {
81
83
  this.sendMessagesInBulk();
82
84
  }
83
- if (this.backlog.length > 0) {
84
- this.backlog += `\n${stringifiedMsg}`;
85
- }
86
- else {
87
- this.backlog = stringifiedMsg;
88
- }
85
+ this.appendMessage(stringifiedMsg);
89
86
  // If message itself exceeds the chunk size, send it immediately
90
87
  if (msgSize >= WEBSOCKET_CONFIG.MAX_OUTGOING_MESSAGES_CHUNK_BYTES) {
91
88
  this.sendMessagesInBulk();
92
89
  }
93
90
  }
91
+ serializeMessage(msg) {
92
+ return JSON.stringify(msg);
93
+ }
94
+ appendMessage(msg) {
95
+ this.backlog.push(msg);
96
+ }
94
97
  sendMessagesInBulk() {
95
98
  if (this.backlog.length > 0 && isWebSocketOpen(this.websocket)) {
96
- this.websocket.send(this.backlog);
97
- this.backlog = "";
99
+ this.websocket.send(this.backlog.join("\n"));
100
+ this.backlog.length = 0;
98
101
  }
99
102
  }
100
103
  drain() {
@@ -1 +1 @@
1
- {"version":3,"file":"BatchedOutgoingMessages.js","sourceRoot":"","sources":["../src/BatchedOutgoingMessages.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BatchedOutgoingMessages.js","sourceRoot":"","sources":["../src/BatchedOutgoingMessages.ts"],"names":[],"mappings":"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"}
@@ -3,36 +3,65 @@ import { afterEach, describe, expect, test, vi } from "vitest";
3
3
  import { createWebSocketPeer, } from "../createWebSocketPeer.js";
4
4
  import { BUFFER_LIMIT, BUFFER_LIMIT_POLLING_INTERVAL } from "../utils.js";
5
5
  import { createTestMetricReader, tearDownTestMetricReader } from "./utils.js";
6
- const { CO_VALUE_PRIORITY, WEBSOCKET_CONFIG, setOutgoingMessagesChunkDelay } = cojsonInternals;
6
+ const { CO_VALUE_PRIORITY, WEBSOCKET_CONFIG } = cojsonInternals;
7
7
  const { MAX_OUTGOING_MESSAGES_CHUNK_BYTES } = WEBSOCKET_CONFIG;
8
8
  function setup(opts = {}) {
9
+ const { initialReadyState = 1, ...peerOpts } = opts;
9
10
  const listeners = new Map();
10
11
  const mockWebSocket = {
11
- readyState: 1,
12
- addEventListener: vi.fn().mockImplementation((type, listener) => {
13
- listeners.set(type, listener);
12
+ readyState: initialReadyState,
13
+ bufferedAmount: 0,
14
+ addEventListener: vi
15
+ .fn()
16
+ .mockImplementation((type, callback, options) => {
17
+ if (!listeners.has(type)) {
18
+ listeners.set(type, new Set());
19
+ }
20
+ listeners.get(type).add({ callback, once: options?.once });
14
21
  }),
15
- removeEventListener: vi.fn().mockImplementation((type) => {
16
- listeners.delete(type);
22
+ removeEventListener: vi
23
+ .fn()
24
+ .mockImplementation((type, callback) => {
25
+ const set = listeners.get(type);
26
+ if (set) {
27
+ for (const entry of set) {
28
+ if (entry.callback === callback) {
29
+ set.delete(entry);
30
+ break;
31
+ }
32
+ }
33
+ }
17
34
  }),
18
35
  close: vi.fn(),
19
36
  send: vi.fn(),
20
37
  };
38
+ const triggerEvent = (type, event) => {
39
+ const set = listeners.get(type);
40
+ if (set) {
41
+ const toRemove = [];
42
+ for (const entry of set) {
43
+ entry.callback(event ?? new MessageEvent(type));
44
+ if (entry.once) {
45
+ toRemove.push(entry);
46
+ }
47
+ }
48
+ for (const entry of toRemove) {
49
+ set.delete(entry);
50
+ }
51
+ }
52
+ };
21
53
  const peer = createWebSocketPeer({
22
54
  id: "test-peer",
23
55
  websocket: mockWebSocket,
24
56
  role: "client",
25
57
  batchingByDefault: true,
26
- ...opts,
58
+ ...peerOpts,
27
59
  });
28
- return { mockWebSocket, peer, listeners };
60
+ return { mockWebSocket, peer, listeners, triggerEvent };
29
61
  }
30
62
  function serializeMessages(messages) {
31
63
  return messages.map((msg) => JSON.stringify(msg)).join("\n");
32
64
  }
33
- afterEach(() => {
34
- setOutgoingMessagesChunkDelay(5);
35
- });
36
65
  describe("createWebSocketPeer", () => {
37
66
  test("should create a peer with correct properties", () => {
38
67
  const { peer } = setup();
@@ -42,20 +71,18 @@ describe("createWebSocketPeer", () => {
42
71
  expect(peer).toHaveProperty("role", "client");
43
72
  });
44
73
  test("should handle disconnection", async () => {
45
- const { listeners, peer } = setup();
74
+ const { triggerEvent, peer } = setup();
46
75
  const onMessageSpy = vi.fn();
47
76
  peer.incoming.onMessage(onMessageSpy);
48
- const closeHandler = listeners.get("close");
49
- closeHandler?.(new MessageEvent("close"));
77
+ triggerEvent("close");
50
78
  expect(onMessageSpy).toHaveBeenCalledWith("Disconnected");
51
79
  });
52
80
  test("should handle ping timeout", async () => {
53
81
  vi.useFakeTimers();
54
- const { listeners, peer } = setup();
82
+ const { triggerEvent, peer } = setup();
55
83
  const onMessageSpy = vi.fn();
56
84
  peer.incoming.onMessage(onMessageSpy);
57
- const messageHandler = listeners.get("message");
58
- messageHandler?.(new MessageEvent("message", { data: "{}" }));
85
+ triggerEvent("message", new MessageEvent("message", { data: "{}" }));
59
86
  await vi.advanceTimersByTimeAsync(10000);
60
87
  expect(onMessageSpy).toHaveBeenCalledWith("Disconnected");
61
88
  vi.useRealTimers();
@@ -107,8 +134,7 @@ describe("createWebSocketPeer", () => {
107
134
  });
108
135
  test("should call onSuccess handler after receiving first message", () => {
109
136
  const onSuccess = vi.fn();
110
- const { listeners } = setup({ onSuccess });
111
- const messageHandler = listeners.get("message");
137
+ const { triggerEvent } = setup({ onSuccess });
112
138
  const message = {
113
139
  action: "known",
114
140
  id: "co_ztest",
@@ -116,17 +142,16 @@ describe("createWebSocketPeer", () => {
116
142
  sessions: {},
117
143
  };
118
144
  // First message should trigger onSuccess
119
- messageHandler?.(new MessageEvent("message", { data: JSON.stringify(message) }));
145
+ triggerEvent("message", new MessageEvent("message", { data: JSON.stringify(message) }));
120
146
  expect(onSuccess).toHaveBeenCalledTimes(1);
121
147
  // Subsequent messages should not trigger onSuccess again
122
- messageHandler?.(new MessageEvent("message", { data: JSON.stringify(message) }));
148
+ triggerEvent("message", new MessageEvent("message", { data: JSON.stringify(message) }));
123
149
  expect(onSuccess).toHaveBeenCalledTimes(1);
124
150
  });
125
151
  describe("batchingByDefault = true", () => {
126
- test("should batch outgoing messages", async () => {
127
- const { peer, mockWebSocket } = setup();
128
- mockWebSocket.send.mockImplementation(() => {
129
- mockWebSocket.readyState = 0;
152
+ test("should batch outgoing messages when socket is not ready", async () => {
153
+ const { peer, mockWebSocket, triggerEvent } = setup({
154
+ initialReadyState: 0,
130
155
  });
131
156
  const message1 = {
132
157
  action: "known",
@@ -142,40 +167,75 @@ describe("createWebSocketPeer", () => {
142
167
  };
143
168
  void peer.outgoing.push(message1);
144
169
  void peer.outgoing.push(message2);
170
+ // Simulate socket becoming ready
171
+ mockWebSocket.readyState = 1;
172
+ triggerEvent("open");
145
173
  await waitFor(() => {
146
174
  expect(mockWebSocket.send).toHaveBeenCalled();
147
175
  });
148
176
  expect(mockWebSocket.send).toHaveBeenCalledWith([message1, message2].map((msg) => JSON.stringify(msg)).join("\n"));
149
177
  });
150
- test("should sort outgoing messages by priority", async () => {
178
+ test("should send messages immediately when socket is ready", async () => {
151
179
  const { peer, mockWebSocket } = setup();
152
- mockWebSocket.send.mockImplementation(() => {
153
- mockWebSocket.readyState = 0;
154
- });
155
180
  const message1 = {
181
+ action: "known",
182
+ id: "co_ztest",
183
+ header: false,
184
+ sessions: {},
185
+ };
186
+ const message2 = {
187
+ action: "content",
188
+ id: "co_zlow",
189
+ new: {},
190
+ priority: 6,
191
+ };
192
+ void peer.outgoing.push(message1);
193
+ await waitFor(() => {
194
+ expect(mockWebSocket.send).toHaveBeenCalledTimes(1);
195
+ });
196
+ expect(mockWebSocket.send).toHaveBeenCalledWith(JSON.stringify(message1));
197
+ void peer.outgoing.push(message2);
198
+ await waitFor(() => {
199
+ expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
200
+ });
201
+ expect(mockWebSocket.send).toHaveBeenNthCalledWith(2, JSON.stringify(message2));
202
+ });
203
+ test("should sort remaining queued messages by priority after first message", async () => {
204
+ const { peer, mockWebSocket, triggerEvent } = setup({
205
+ initialReadyState: 0,
206
+ });
207
+ const lowPriority = {
156
208
  action: "content",
157
209
  id: "co_zlow",
158
210
  new: {},
159
211
  priority: CO_VALUE_PRIORITY.LOW,
160
212
  };
161
- const message2 = {
213
+ const highPriority = {
162
214
  action: "content",
163
215
  id: "co_zhigh",
164
216
  new: {},
165
217
  priority: CO_VALUE_PRIORITY.HIGH,
166
218
  };
167
- void peer.outgoing.push(message1);
168
- void peer.outgoing.push(message2);
169
- void peer.outgoing.push(message2);
219
+ // First message is pulled immediately before socket check,
220
+ // so it will be first regardless of priority
221
+ void peer.outgoing.push(lowPriority);
222
+ // Subsequent messages are queued and sorted by priority
223
+ void peer.outgoing.push(lowPriority);
224
+ void peer.outgoing.push(highPriority);
225
+ // Simulate socket becoming ready
226
+ mockWebSocket.readyState = 1;
227
+ triggerEvent("open");
170
228
  await waitFor(() => {
171
229
  expect(mockWebSocket.send).toHaveBeenCalled();
172
230
  });
173
- expect(mockWebSocket.send).toHaveBeenCalledWith([message2, message2, message1]
231
+ // First message (lowPriority) comes first as it was pulled before waiting,
232
+ // then remaining messages are sorted: highPriority before lowPriority
233
+ expect(mockWebSocket.send).toHaveBeenCalledWith([lowPriority, highPriority, lowPriority]
174
234
  .map((msg) => JSON.stringify(msg))
175
235
  .join("\n"));
176
236
  });
177
- test("should send all the pending messages when the websocket is closed", async () => {
178
- const { peer, mockWebSocket } = setup();
237
+ test("should send remaining queued messages when close is called", async () => {
238
+ const { peer, mockWebSocket } = setup({ initialReadyState: 0 });
179
239
  const message1 = {
180
240
  action: "known",
181
241
  id: "co_ztest",
@@ -188,13 +248,28 @@ describe("createWebSocketPeer", () => {
188
248
  new: {},
189
249
  priority: 6,
190
250
  };
251
+ const message3 = {
252
+ action: "content",
253
+ id: "co_zmedium",
254
+ new: {},
255
+ priority: 3,
256
+ };
191
257
  void peer.outgoing.push(message1);
192
258
  void peer.outgoing.push(message2);
259
+ void peer.outgoing.push(message3);
260
+ // Set socket to open before close to allow sending
261
+ mockWebSocket.readyState = 1;
193
262
  peer.outgoing.close();
194
- expect(mockWebSocket.send).toHaveBeenCalledWith([message1, message2].map((msg) => JSON.stringify(msg)).join("\n"));
263
+ // First message was already pulled by processQueue (waiting for socket),
264
+ // close() processes and sends remaining messages from queue sorted by priority
265
+ expect(mockWebSocket.send).toHaveBeenCalledWith([message3, message2].map((msg) => JSON.stringify(msg)).join("\n"));
195
266
  });
196
267
  test("should limit the chunk size to MAX_OUTGOING_MESSAGES_CHUNK_SIZE", async () => {
268
+ // This test verifies chunking works when socket is already ready
197
269
  const { peer, mockWebSocket } = setup();
270
+ mockWebSocket.send.mockImplementation((value) => {
271
+ mockWebSocket.bufferedAmount += value.length;
272
+ });
198
273
  const message1 = {
199
274
  action: "known",
200
275
  id: "co_ztest",
@@ -207,21 +282,22 @@ describe("createWebSocketPeer", () => {
207
282
  new: {},
208
283
  priority: 6,
209
284
  };
210
- const stream = [];
211
- while (serializeMessages(stream.concat(message1)).length <
212
- MAX_OUTGOING_MESSAGES_CHUNK_BYTES) {
213
- stream.push(message1);
214
- void peer.outgoing.push(message1);
285
+ // Fill up the buffer
286
+ while (mockWebSocket.bufferedAmount < BUFFER_LIMIT) {
287
+ peer.outgoing.push(message1);
215
288
  }
289
+ mockWebSocket.send.mockClear();
216
290
  void peer.outgoing.push(message2);
291
+ expect(mockWebSocket.send).not.toHaveBeenCalled();
292
+ // Reset the buffer, make it look like we have sent the messages
293
+ mockWebSocket.bufferedAmount = 0;
217
294
  await waitFor(() => {
218
- expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
295
+ expect(mockWebSocket.send).toHaveBeenCalled();
219
296
  });
220
- expect(mockWebSocket.send).toHaveBeenCalledWith(serializeMessages(stream));
221
- expect(mockWebSocket.send).toHaveBeenNthCalledWith(2, JSON.stringify(message2));
222
297
  });
223
298
  test("should send accumulated messages before a large message", async () => {
224
299
  const { peer, mockWebSocket } = setup();
300
+ mockWebSocket.bufferedAmount = BUFFER_LIMIT + 1;
225
301
  const smallMessage = {
226
302
  action: "known",
227
303
  id: "co_z_small",
@@ -239,6 +315,7 @@ describe("createWebSocketPeer", () => {
239
315
  };
240
316
  void peer.outgoing.push(smallMessage);
241
317
  void peer.outgoing.push(largeMessage);
318
+ mockWebSocket.bufferedAmount = 0;
242
319
  await waitFor(() => {
243
320
  expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
244
321
  });
@@ -249,9 +326,6 @@ describe("createWebSocketPeer", () => {
249
326
  test("should wait for the buffer to be under BUFFER_LIMIT before sending more messages", async () => {
250
327
  vi.useFakeTimers();
251
328
  const { peer, mockWebSocket } = setup();
252
- mockWebSocket.send.mockImplementation(() => {
253
- mockWebSocket.bufferedAmount = BUFFER_LIMIT + 1;
254
- });
255
329
  const message1 = {
256
330
  action: "known",
257
331
  id: "co_ztest",
@@ -264,18 +338,18 @@ describe("createWebSocketPeer", () => {
264
338
  new: {},
265
339
  priority: 6,
266
340
  };
267
- const stream = [];
268
- while (serializeMessages(stream.concat(message1)).length <
269
- MAX_OUTGOING_MESSAGES_CHUNK_BYTES) {
270
- stream.push(message1);
271
- void peer.outgoing.push(message1);
272
- }
341
+ // Start with buffer full so messages go through the queue
342
+ mockWebSocket.bufferedAmount = BUFFER_LIMIT + 1;
343
+ void peer.outgoing.push(message1);
273
344
  void peer.outgoing.push(message2);
274
- await vi.advanceTimersByTimeAsync(100);
275
- expect(mockWebSocket.send).toHaveBeenCalledWith(serializeMessages(stream));
345
+ await vi.advanceTimersByTimeAsync(0);
346
+ // No messages sent yet because buffer is full
347
+ expect(mockWebSocket.send).not.toHaveBeenCalled();
348
+ // Clear the buffer
276
349
  mockWebSocket.bufferedAmount = 0;
277
350
  await vi.advanceTimersByTimeAsync(BUFFER_LIMIT_POLLING_INTERVAL + 1);
278
- expect(mockWebSocket.send).toHaveBeenNthCalledWith(2, JSON.stringify(message2));
351
+ // Both messages are batched together
352
+ expect(mockWebSocket.send).toHaveBeenCalledWith([message1, message2].map((msg) => JSON.stringify(msg)).join("\n"));
279
353
  vi.useRealTimers();
280
354
  });
281
355
  });
@@ -295,16 +369,20 @@ describe("createWebSocketPeer", () => {
295
369
  priority: 6,
296
370
  };
297
371
  void peer.outgoing.push(message1);
372
+ await waitFor(() => {
373
+ expect(mockWebSocket.send).toHaveBeenCalledTimes(1);
374
+ });
298
375
  void peer.outgoing.push(message2);
299
376
  await waitFor(() => {
300
- expect(mockWebSocket.send).toHaveBeenCalled();
377
+ expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
301
378
  });
302
379
  expect(mockWebSocket.send).toHaveBeenNthCalledWith(1, JSON.stringify(message1));
303
380
  expect(mockWebSocket.send).toHaveBeenNthCalledWith(2, JSON.stringify(message2));
304
381
  });
305
- test("should start batching outgoing messages when reiceving a batched message", async () => {
306
- const { peer, mockWebSocket, listeners } = setup({
382
+ test("should start batching outgoing messages when receiving a batched message", async () => {
383
+ const { peer, mockWebSocket, triggerEvent } = setup({
307
384
  batchingByDefault: false,
385
+ initialReadyState: 0,
308
386
  });
309
387
  const message1 = {
310
388
  action: "known",
@@ -312,8 +390,7 @@ describe("createWebSocketPeer", () => {
312
390
  header: false,
313
391
  sessions: {},
314
392
  };
315
- const messageHandler = listeners.get("message");
316
- messageHandler?.(new MessageEvent("message", {
393
+ triggerEvent("message", new MessageEvent("message", {
317
394
  data: Array.from({ length: 5 }, () => message1)
318
395
  .map((msg) => JSON.stringify(msg))
319
396
  .join("\n"),
@@ -326,13 +403,16 @@ describe("createWebSocketPeer", () => {
326
403
  };
327
404
  void peer.outgoing.push(message1);
328
405
  void peer.outgoing.push(message2);
406
+ // Simulate socket becoming ready
407
+ mockWebSocket.readyState = 1;
408
+ triggerEvent("open");
329
409
  await waitFor(() => {
330
410
  expect(mockWebSocket.send).toHaveBeenCalled();
331
411
  });
332
412
  expect(mockWebSocket.send).toHaveBeenCalledWith([message1, message2].map((msg) => JSON.stringify(msg)).join("\n"));
333
413
  });
334
- test("should not start batching outgoing messages when reiceving non-batched message", async () => {
335
- const { peer, mockWebSocket, listeners } = setup({
414
+ test("should not start batching outgoing messages when receiving non-batched message", async () => {
415
+ const { peer, mockWebSocket, triggerEvent } = setup({
336
416
  batchingByDefault: false,
337
417
  });
338
418
  const message1 = {
@@ -341,8 +421,7 @@ describe("createWebSocketPeer", () => {
341
421
  header: false,
342
422
  sessions: {},
343
423
  };
344
- const messageHandler = listeners.get("message");
345
- messageHandler?.(new MessageEvent("message", {
424
+ triggerEvent("message", new MessageEvent("message", {
346
425
  data: JSON.stringify(message1),
347
426
  }));
348
427
  const message2 = {
@@ -352,9 +431,12 @@ describe("createWebSocketPeer", () => {
352
431
  priority: 6,
353
432
  };
354
433
  void peer.outgoing.push(message1);
434
+ await waitFor(() => {
435
+ expect(mockWebSocket.send).toHaveBeenCalledTimes(1);
436
+ });
355
437
  void peer.outgoing.push(message2);
356
438
  await waitFor(() => {
357
- expect(mockWebSocket.send).toHaveBeenCalled();
439
+ expect(mockWebSocket.send).toHaveBeenCalledTimes(2);
358
440
  });
359
441
  expect(mockWebSocket.send).toHaveBeenNthCalledWith(1, JSON.stringify(message1));
360
442
  expect(mockWebSocket.send).toHaveBeenNthCalledWith(2, JSON.stringify(message2));
@@ -376,12 +458,11 @@ describe("createWebSocketPeer", () => {
376
458
  });
377
459
  test("should correctly measure incoming ingress", async () => {
378
460
  const metricReader = createTestMetricReader();
379
- const { listeners } = setup({
461
+ const { triggerEvent } = setup({
380
462
  meta: { label: "value" },
381
463
  });
382
- const messageHandler = listeners.get("message");
383
464
  const encryptedChanges = "Hello, world!";
384
- messageHandler?.(new MessageEvent("message", {
465
+ triggerEvent("message", new MessageEvent("message", {
385
466
  data: JSON.stringify({
386
467
  action: "content",
387
468
  new: {
@@ -403,7 +484,7 @@ describe("createWebSocketPeer", () => {
403
484
  label: "value",
404
485
  })).toBe(encryptedChanges.length);
405
486
  const trustingChanges = "Jazz is great!";
406
- messageHandler?.(new MessageEvent("message", {
487
+ triggerEvent("message", new MessageEvent("message", {
407
488
  data: JSON.stringify({
408
489
  action: "content",
409
490
  new: {
@@ -423,9 +504,8 @@ describe("createWebSocketPeer", () => {
423
504
  })).toBe(encryptedChanges.length + trustingChanges.length);
424
505
  });
425
506
  test("should drain the outgoing queue on websocket close so pulled equals pushed", async () => {
426
- setOutgoingMessagesChunkDelay(500);
427
507
  const metricReader = createTestMetricReader();
428
- const { peer, listeners } = setup();
508
+ const { peer, triggerEvent } = setup({ initialReadyState: 0 });
429
509
  const high = {
430
510
  action: "content",
431
511
  id: "co_zhigh",
@@ -459,10 +539,12 @@ describe("createWebSocketPeer", () => {
459
539
  priority: CO_VALUE_PRIORITY.LOW,
460
540
  peerRole: "client",
461
541
  })).toBe(1);
542
+ // First message is already pulled by processQueue (waiting for socket open),
543
+ // so pulled count for that priority is already 1
462
544
  expect(await metricReader.getMetricValue("jazz.messagequeue.outgoing.pulled", {
463
545
  priority: CO_VALUE_PRIORITY.HIGH,
464
546
  peerRole: "client",
465
- })).toBe(0);
547
+ })).toBe(1);
466
548
  expect(await metricReader.getMetricValue("jazz.messagequeue.outgoing.pulled", {
467
549
  priority: CO_VALUE_PRIORITY.MEDIUM,
468
550
  peerRole: "client",
@@ -471,8 +553,8 @@ describe("createWebSocketPeer", () => {
471
553
  priority: CO_VALUE_PRIORITY.LOW,
472
554
  peerRole: "client",
473
555
  })).toBe(0);
474
- const closeHandler = listeners.get("close");
475
- closeHandler?.(new MessageEvent("close"));
556
+ triggerEvent("close");
557
+ // After close, drain() is called which pulls all remaining messages
476
558
  expect(await metricReader.getMetricValue("jazz.messagequeue.outgoing.pulled", {
477
559
  priority: CO_VALUE_PRIORITY.HIGH,
478
560
  peerRole: "client",
@@ -485,7 +567,6 @@ describe("createWebSocketPeer", () => {
485
567
  priority: CO_VALUE_PRIORITY.LOW,
486
568
  peerRole: "client",
487
569
  })).toBe(1);
488
- vi.useRealTimers();
489
570
  });
490
571
  });
491
572
  });