@actuallab/rpc 12.1.115 → 12.2.21

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/index.js CHANGED
@@ -22,6 +22,10 @@ var ARG_DELIMITER = "";
22
22
  var FRAME_DELIMITER = "\n";
23
23
 
24
24
  // src/rpc-serialization.ts
25
+ import {
26
+ Encoder,
27
+ Decoder
28
+ } from "@msgpack/msgpack";
25
29
  function serializeMessage(message, args) {
26
30
  const envelope = JSON.stringify(message);
27
31
  if (args === void 0 || args.length === 0) {
@@ -51,13 +55,263 @@ function deserializeMessage(raw) {
51
55
  try {
52
56
  return JSON.parse(s);
53
57
  } catch {
58
+ console.warn(
59
+ `[RpcSerialization] failed to parse arg as JSON: ${s.substring(0, 100)}`
60
+ );
54
61
  return s;
55
62
  }
56
63
  });
57
64
  return { message, args };
58
65
  }
66
+ var textEncoder = new TextEncoder();
67
+ var textDecoder = new TextDecoder();
68
+ var INITIAL_ENCODER_BUFFER_SIZE = 32 * 1024;
69
+ var defaultBinaryEncoder = new Encoder(
70
+ void 0,
71
+ void 0,
72
+ void 0,
73
+ INITIAL_ENCODER_BUFFER_SIZE
74
+ );
75
+ var defaultBinaryDecoder = new Decoder();
76
+ function createBinaryEncoder() {
77
+ return new Encoder(
78
+ void 0,
79
+ void 0,
80
+ void 0,
81
+ INITIAL_ENCODER_BUFFER_SIZE
82
+ );
83
+ }
84
+ function varUintByteLen(value) {
85
+ if (value < 0) value = 0;
86
+ let n = 0;
87
+ do {
88
+ n++;
89
+ value >>>= 7;
90
+ } while (value > 0);
91
+ return n;
92
+ }
93
+ function writeVarUintInto(out, pos, value) {
94
+ if (value < 0) value = 0;
95
+ do {
96
+ let byte = value & 127;
97
+ value >>>= 7;
98
+ if (value > 0) byte |= 128;
99
+ out[pos++] = byte;
100
+ } while (value > 0);
101
+ return pos;
102
+ }
103
+ function readVarUint(data, offset) {
104
+ let value = 0, shift = 0, bytesRead = 0;
105
+ do {
106
+ if (offset + bytesRead >= data.length) break;
107
+ const byte = data[offset + bytesRead];
108
+ value |= (byte & 127) << shift;
109
+ bytesRead++;
110
+ if ((byte & 128) === 0) break;
111
+ shift += 7;
112
+ } while (shift < 35);
113
+ return { value: value >>> 0, bytesRead };
114
+ }
115
+ function concatUint8Arrays(arrays) {
116
+ let totalLength = 0;
117
+ for (const arr of arrays) totalLength += arr.length;
118
+ const result = new Uint8Array(totalLength);
119
+ let offset = 0;
120
+ for (const arr of arrays) {
121
+ result.set(arr, offset);
122
+ offset += arr.length;
123
+ }
124
+ return result;
125
+ }
126
+ function serializeBinaryMessage(message, args, encoder = defaultBinaryEncoder) {
127
+ let argsDataLen = 0;
128
+ let argBufs = null;
129
+ if (args && args.length > 0) {
130
+ argBufs = new Array(args.length);
131
+ for (let i = 0; i < args.length; i++) {
132
+ const buf = encoder.encode(args[i]);
133
+ argBufs[i] = buf;
134
+ argsDataLen += buf.length;
135
+ }
136
+ }
137
+ const callType = message.CallType ?? 0;
138
+ const relatedId = message.RelatedId ?? 0;
139
+ const methodBytes = textEncoder.encode(message.Method ?? "");
140
+ const relatedIdLen = varUintByteLen(relatedId);
141
+ const methodLenVarintLen = varUintByteLen(methodBytes.length);
142
+ const headerSize = 1 + relatedIdLen + methodLenVarintLen + methodBytes.length;
143
+ const totalSize = headerSize + 4 + argsDataLen;
144
+ const out = new Uint8Array(totalSize);
145
+ let pos = 0;
146
+ out[pos++] = callType << 5 & 224;
147
+ pos = writeVarUintInto(out, pos, relatedId);
148
+ pos = writeVarUintInto(out, pos, methodBytes.length);
149
+ out.set(methodBytes, pos);
150
+ pos += methodBytes.length;
151
+ out[pos++] = argsDataLen & 255;
152
+ out[pos++] = argsDataLen >>> 8 & 255;
153
+ out[pos++] = argsDataLen >>> 16 & 255;
154
+ out[pos++] = argsDataLen >>> 24 & 255;
155
+ if (argBufs !== null) {
156
+ for (const buf of argBufs) {
157
+ out.set(buf, pos);
158
+ pos += buf.length;
159
+ }
160
+ }
161
+ return out;
162
+ }
163
+ function deserializeBinaryMessage(data, offset, decoder = defaultBinaryDecoder) {
164
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
165
+ let pos = offset;
166
+ const byte0 = data[pos++];
167
+ const callTypeId = byte0 >> 5 & 7;
168
+ const headerCount = byte0 & 31;
169
+ const relId = readVarUint(data, pos);
170
+ pos += relId.bytesRead;
171
+ const methodLen = readVarUint(data, pos);
172
+ pos += methodLen.bytesRead;
173
+ const methodBytes = data.subarray(pos, pos + methodLen.value);
174
+ const method = textDecoder.decode(methodBytes);
175
+ pos += methodLen.value;
176
+ const argDataLen = view.getInt32(pos, true);
177
+ pos += 4;
178
+ if (headerCount > 0) {
179
+ for (let h = 0; h < headerCount; h++) {
180
+ const keyLen = data[pos++];
181
+ pos += keyLen;
182
+ const valLen = readVarUint(data, pos);
183
+ pos += valLen.bytesRead + valLen.value;
184
+ }
185
+ }
186
+ const args = [];
187
+ const argEnd = pos + argDataLen;
188
+ if (argDataLen > 0) {
189
+ const argSlice = data.subarray(pos, argEnd);
190
+ for (const decoded of decoder.decodeMulti(argSlice)) {
191
+ args.push(decoded);
192
+ }
193
+ pos = argEnd;
194
+ }
195
+ const message = {
196
+ Method: method,
197
+ RelatedId: relId.value,
198
+ CallType: callTypeId
199
+ };
200
+ return { message, args, bytesRead: pos - offset };
201
+ }
202
+ function splitBinaryFrame(frame, decoder = defaultBinaryDecoder) {
203
+ const results = [];
204
+ let offset = 0;
205
+ while (offset < frame.length) {
206
+ const { message, args, bytesRead } = deserializeBinaryMessage(
207
+ frame,
208
+ offset,
209
+ decoder
210
+ );
211
+ if (bytesRead <= 0) break;
212
+ results.push({ message, args });
213
+ offset += bytesRead;
214
+ }
215
+ return results;
216
+ }
217
+ function serializeBinaryFrame(messages) {
218
+ return concatUint8Arrays(messages);
219
+ }
220
+ function serializeCompactBinaryMessage(message, args, registry, encoder = defaultBinaryEncoder) {
221
+ let argsDataLen = 0;
222
+ let argBufs = null;
223
+ if (args && args.length > 0) {
224
+ argBufs = new Array(args.length);
225
+ for (let i = 0; i < args.length; i++) {
226
+ const buf = encoder.encode(args[i]);
227
+ argBufs[i] = buf;
228
+ argsDataLen += buf.length;
229
+ }
230
+ }
231
+ const methodName = message.Method ?? "";
232
+ const methodHash = registry?.requireHash(methodName) ?? 0;
233
+ const callType = message.CallType ?? 0;
234
+ const relatedId = message.RelatedId ?? 0;
235
+ const relatedIdLen = varUintByteLen(relatedId);
236
+ const headerSize = 1 + relatedIdLen + 4;
237
+ const totalSize = headerSize + 4 + argsDataLen;
238
+ const out = new Uint8Array(totalSize);
239
+ let pos = 0;
240
+ out[pos++] = callType << 5 & 224;
241
+ pos = writeVarUintInto(out, pos, relatedId);
242
+ const hashU32 = methodHash >>> 0;
243
+ out[pos++] = hashU32 & 255;
244
+ out[pos++] = hashU32 >>> 8 & 255;
245
+ out[pos++] = hashU32 >>> 16 & 255;
246
+ out[pos++] = hashU32 >>> 24 & 255;
247
+ out[pos++] = argsDataLen & 255;
248
+ out[pos++] = argsDataLen >>> 8 & 255;
249
+ out[pos++] = argsDataLen >>> 16 & 255;
250
+ out[pos++] = argsDataLen >>> 24 & 255;
251
+ if (argBufs !== null) {
252
+ for (const buf of argBufs) {
253
+ out.set(buf, pos);
254
+ pos += buf.length;
255
+ }
256
+ }
257
+ return out;
258
+ }
259
+ function deserializeCompactBinaryMessage(data, offset, registry, decoder = defaultBinaryDecoder) {
260
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
261
+ let pos = offset;
262
+ const byte0 = data[pos++];
263
+ const callTypeId = byte0 >> 5 & 7;
264
+ const headerCount = byte0 & 31;
265
+ const relId = readVarUint(data, pos);
266
+ pos += relId.bytesRead;
267
+ const methodHash = view.getUint32(pos, true);
268
+ pos += 4;
269
+ const method = registry?.getName(methodHash | 0) ?? `<hash:0x${methodHash.toString(16).padStart(8, "0")}>`;
270
+ const argDataLen = view.getInt32(pos, true);
271
+ pos += 4;
272
+ if (headerCount > 0) {
273
+ for (let h = 0; h < headerCount; h++) {
274
+ const keyLen = data[pos++];
275
+ pos += keyLen;
276
+ const valLen = readVarUint(data, pos);
277
+ pos += valLen.bytesRead + valLen.value;
278
+ }
279
+ }
280
+ const args = [];
281
+ const argEnd = pos + argDataLen;
282
+ if (argDataLen > 0) {
283
+ const argSlice = data.subarray(pos, argEnd);
284
+ for (const decoded of decoder.decodeMulti(argSlice)) {
285
+ args.push(decoded);
286
+ }
287
+ pos = argEnd;
288
+ }
289
+ const message = {
290
+ Method: method,
291
+ RelatedId: relId.value,
292
+ CallType: callTypeId
293
+ };
294
+ return { message, args, bytesRead: pos - offset };
295
+ }
296
+ function splitCompactBinaryFrame(frame, registry, decoder = defaultBinaryDecoder) {
297
+ const results = [];
298
+ let offset = 0;
299
+ while (offset < frame.length) {
300
+ const { message, args, bytesRead } = deserializeCompactBinaryMessage(
301
+ frame,
302
+ offset,
303
+ registry,
304
+ decoder
305
+ );
306
+ if (bytesRead <= 0) break;
307
+ results.push({ message, args });
308
+ offset += bytesRead;
309
+ }
310
+ return results;
311
+ }
59
312
 
60
313
  // src/rpc-connection.ts
314
+ import { Decoder as Decoder2 } from "@msgpack/msgpack";
61
315
  import { PromiseSource, EventHandlerSet } from "@actuallab/core";
62
316
  var WebSocketState = {
63
317
  CONNECTING: 0,
@@ -69,11 +323,36 @@ var RpcWebSocketConnection = class {
69
323
  _ws;
70
324
  _sendBuffer = [];
71
325
  _connected = new PromiseSource();
326
+ /** Per-connection msgpack encoder — reused across outbound messages to
327
+ * amortise construction cost and to keep the internal write buffer at
328
+ * its largest observed size (avoids repeated `resizeBuffer` growth on
329
+ * large video frames). See serializeBinaryMessage() for details. */
330
+ _encoder = createBinaryEncoder();
331
+ /** Per-connection msgpack decoder — reused across inbound messages
332
+ * rather than reconstructing one for every `decodeMulti` call. */
333
+ _decoder = new Decoder2();
334
+ /** Exposed for callers who need to encode outbound binary messages
335
+ * through this connection (e.g. `serializeBinaryMessage`). Safe to
336
+ * pass to synchronous encode calls on the same event-loop turn. */
337
+ get encoder() {
338
+ return this._encoder;
339
+ }
340
+ get decoder() {
341
+ return this._decoder;
342
+ }
343
+ _format;
344
+ _methodRegistry;
345
+ binaryMode;
72
346
  messageReceived = new EventHandlerSet();
73
347
  closed = new EventHandlerSet();
74
348
  error = new EventHandlerSet();
75
- constructor(ws) {
349
+ constructor(ws, binaryMode = false, format, methodRegistry) {
76
350
  this._ws = ws;
351
+ this.binaryMode = binaryMode;
352
+ this._format = format;
353
+ this._methodRegistry = methodRegistry;
354
+ if (binaryMode && ws.binaryType !== void 0)
355
+ ws.binaryType = "arraybuffer";
77
356
  if (ws.readyState === WebSocketState.OPEN) {
78
357
  this._connected.resolve();
79
358
  this._flush();
@@ -83,10 +362,46 @@ var RpcWebSocketConnection = class {
83
362
  this._flush();
84
363
  };
85
364
  ws.onmessage = (ev) => {
86
- const data = typeof ev.data === "string" ? ev.data : String(ev.data);
87
- const messages = splitFrame(data);
88
- for (const msg of messages) {
89
- if (msg.length > 0) this.messageReceived.trigger(msg);
365
+ if (ev.data instanceof Uint8Array || ev.data instanceof ArrayBuffer) {
366
+ const frame = ev.data instanceof Uint8Array ? ev.data : new Uint8Array(ev.data);
367
+ try {
368
+ const messages = this._splitBinary(frame);
369
+ for (const { message, args } of messages) {
370
+ this.messageReceived.trigger({
371
+ kind: "binary",
372
+ message,
373
+ args
374
+ });
375
+ }
376
+ } catch (e) {
377
+ this.error.trigger(e);
378
+ }
379
+ } else if (typeof Blob !== "undefined" && ev.data instanceof Blob && this.binaryMode) {
380
+ void ev.data.arrayBuffer().then((ab) => {
381
+ const frame = new Uint8Array(ab);
382
+ try {
383
+ const messages = this._splitBinary(frame);
384
+ for (const { message, args } of messages) {
385
+ this.messageReceived.trigger({
386
+ kind: "binary",
387
+ message,
388
+ args
389
+ });
390
+ }
391
+ } catch (e) {
392
+ this.error.trigger(e);
393
+ }
394
+ });
395
+ } else {
396
+ const data = typeof ev.data === "string" ? ev.data : String(ev.data);
397
+ const messages = splitFrame(data);
398
+ for (const msg of messages) {
399
+ if (msg.length > 0)
400
+ this.messageReceived.trigger({
401
+ kind: "text",
402
+ raw: msg
403
+ });
404
+ }
90
405
  }
91
406
  };
92
407
  ws.onclose = (ev) => {
@@ -103,27 +418,46 @@ var RpcWebSocketConnection = class {
103
418
  return this._connected.promise;
104
419
  }
105
420
  send(serializedMessage) {
106
- try {
107
- if (this._ws.readyState === WebSocketState.OPEN)
108
- this._ws.send(serializedMessage);
109
- else if (this._ws.readyState === WebSocketState.CONNECTING)
110
- this._sendBuffer.push(serializedMessage);
111
- } catch {
112
- }
421
+ this._sendRaw(serializedMessage);
422
+ }
423
+ sendBinary(data) {
424
+ this._sendRaw(data);
425
+ }
426
+ sendTextBatch(messages) {
427
+ this._sendRaw(serializeFrame(messages));
113
428
  }
114
- sendBatch(messages) {
115
- const frame = serializeFrame(messages);
116
- this.send(frame);
429
+ sendBinaryBatch(messages) {
430
+ this._sendRaw(serializeBinaryFrame(messages));
431
+ }
432
+ /** Split a binary frame using the format (compact-aware) or default splitter. */
433
+ _splitBinary(frame) {
434
+ if (this._format) {
435
+ return this._format.splitBinaryFrame(
436
+ frame,
437
+ this._decoder,
438
+ this._methodRegistry
439
+ );
440
+ }
441
+ return splitBinaryFrame(frame, this._decoder);
117
442
  }
118
443
  close(code, reason) {
119
444
  this._ws.close(code, reason);
120
445
  }
446
+ _sendRaw(data) {
447
+ try {
448
+ if (this._ws.readyState === WebSocketState.OPEN) {
449
+ this._ws.send(data);
450
+ } else if (this._ws.readyState === WebSocketState.CONNECTING)
451
+ this._sendBuffer.push(data);
452
+ } catch {
453
+ }
454
+ }
121
455
  _flush() {
122
456
  if (this._sendBuffer.length === 0) return;
123
- const frame = serializeFrame(this._sendBuffer);
457
+ const buffer = this._sendBuffer;
124
458
  this._sendBuffer = [];
125
459
  try {
126
- this._ws.send(frame);
460
+ for (const item of buffer) this._ws.send(item);
127
461
  } catch {
128
462
  }
129
463
  }
@@ -134,6 +468,7 @@ import { EventHandlerSet as EventHandlerSet2 } from "@actuallab/core";
134
468
  var RpcMessageChannelConnection = class {
135
469
  _port;
136
470
  _open = true;
471
+ binaryMode = false;
137
472
  messageReceived = new EventHandlerSet2();
138
473
  closed = new EventHandlerSet2();
139
474
  whenConnected = Promise.resolve();
@@ -143,7 +478,8 @@ var RpcMessageChannelConnection = class {
143
478
  port.onmessage = (ev) => {
144
479
  const data = typeof ev.data === "string" ? ev.data : String(ev.data);
145
480
  for (const msg of splitFrame(data))
146
- if (msg.length > 0) this.messageReceived.trigger(msg);
481
+ if (msg.length > 0)
482
+ this.messageReceived.trigger({ kind: "text", raw: msg });
147
483
  };
148
484
  }
149
485
  get isOpen() {
@@ -156,6 +492,13 @@ var RpcMessageChannelConnection = class {
156
492
  } catch {
157
493
  }
158
494
  }
495
+ sendBinary(data) {
496
+ if (!this._open) return;
497
+ try {
498
+ this._port.postMessage(data, [data.buffer]);
499
+ } catch {
500
+ }
501
+ }
159
502
  close(code, reason) {
160
503
  if (!this._open) return;
161
504
  this._open = false;
@@ -210,8 +553,8 @@ var RpcOutboundCall = class {
210
553
  /** Whether to remove this call from the tracker on $sys.Ok. Default: true.
211
554
  * Subclasses (e.g. compute calls) override to false to stay in tracker for invalidation. */
212
555
  removeOnOk = true;
213
- /** The serialized wire message — stored for re-sending on reconnect. */
214
- serializedMessage = "";
556
+ /** The serialized wire data — stored for re-sending on reconnect. */
557
+ serializedWireData = "";
215
558
  constructor(callId, method) {
216
559
  this.callId = callId;
217
560
  this.method = method;
@@ -301,6 +644,7 @@ var RpcInboundCallTracker = class {
301
644
  };
302
645
 
303
646
  // src/rpc-stream.ts
647
+ import Denque from "denque";
304
648
  import { PromiseSource as PromiseSource3 } from "@actuallab/core";
305
649
 
306
650
  // src/rpc-object.ts
@@ -308,25 +652,49 @@ var RpcObjectKind = { Local: 0, Remote: 1 };
308
652
 
309
653
  // src/rpc-stream.ts
310
654
  function parseStreamRef(value) {
311
- if (typeof value !== "string") return null;
312
- const parts = value.split(",");
313
- if (parts.length < 4 || parts.length > 5) return null;
314
- const hostId = parts[0];
315
- const localId = parseInt(parts[1], 10);
316
- const ackPeriod = parseInt(parts[2], 10);
317
- const ackAdvance = parseInt(parts[3], 10);
318
- if (isNaN(localId) || isNaN(ackPeriod) || isNaN(ackAdvance)) return null;
319
- const allowReconnect = parts.length < 5 || parts[4] !== "0";
320
- return { hostId, localId, ackPeriod, ackAdvance, allowReconnect };
655
+ if (typeof value === "string") {
656
+ const parts = value.split(",");
657
+ if (parts.length < 4 || parts.length > 6) return null;
658
+ const hostId = parts[0];
659
+ const localId = parseInt(parts[1], 10);
660
+ const ackPeriod = parseInt(parts[2], 10);
661
+ const ackAdvance = parseInt(parts[3], 10);
662
+ if (isNaN(localId) || isNaN(ackPeriod) || isNaN(ackAdvance))
663
+ return null;
664
+ const allowReconnect = parts.length < 5 || parts[4] !== "0";
665
+ const isRealTime = parts.length >= 6 && parts[5] === "1";
666
+ return { hostId, localId, ackPeriod, ackAdvance, allowReconnect, isRealTime };
667
+ }
668
+ if (typeof value === "object" && value !== null) {
669
+ const obj = value;
670
+ const serializedId = obj.SerializedId;
671
+ if (!Array.isArray(serializedId) || serializedId.length < 2)
672
+ return null;
673
+ const rawHostId = serializedId[0];
674
+ const hostId = String(rawHostId);
675
+ const localId = Number(serializedId[1]);
676
+ const ackPeriod = Number(obj.AckPeriod ?? 256);
677
+ const ackAdvance = Number(obj.AckAdvance ?? 128);
678
+ const allowReconnect = obj.AllowReconnect !== false;
679
+ const isRealTime = obj.IsRealTime === true;
680
+ return { hostId, localId, ackPeriod, ackAdvance, allowReconnect, isRealTime };
681
+ }
682
+ return null;
321
683
  }
322
- var RpcStream = class {
684
+ var RpcStream = class _RpcStream {
323
685
  id;
324
686
  kind = RpcObjectKind.Remote;
325
687
  allowReconnect;
326
688
  peer;
327
689
  ackPeriod;
328
690
  ackAdvance;
329
- _buffer = [];
691
+ // Double-ended queue so consumption via `shift()` is O(1) and the
692
+ // backing store frees memory as items are drained. The previous
693
+ // `T[]` buffer kept every item for the life of the stream because
694
+ // the iterator tracked a read index instead of shifting — fine for
695
+ // short runs but a slow leak in the browser where a video call can
696
+ // last an hour.
697
+ _buffer = new Denque();
330
698
  _nextExpectedIndex = 0;
331
699
  _consumerWaiting = null;
332
700
  _completed = false;
@@ -346,6 +714,17 @@ var RpcStream = class {
346
714
  onItem(index, item) {
347
715
  if (this._disposed || this._completed) return;
348
716
  if (index > this._nextExpectedIndex) {
717
+ console.warn(
718
+ `[RpcStream] item index gap: localId=${this.id.localId}, expected=${this._nextExpectedIndex}, received=${index}`
719
+ );
720
+ if (!this.allowReconnect) {
721
+ this._completed = true;
722
+ this._completionError = new Error(
723
+ `Stream gap at index ${index} (expected ${this._nextExpectedIndex}); reconnect not allowed`
724
+ );
725
+ this._notifyConsumer();
726
+ return;
727
+ }
349
728
  this._sendAck(this._nextExpectedIndex, true);
350
729
  return;
351
730
  }
@@ -362,6 +741,17 @@ var RpcStream = class {
362
741
  onBatch(index, items) {
363
742
  if (this._disposed || this._completed) return;
364
743
  if (index > this._nextExpectedIndex) {
744
+ console.warn(
745
+ `[RpcStream] batch index gap: localId=${this.id.localId}, expected=${this._nextExpectedIndex}, received=${index}`
746
+ );
747
+ if (!this.allowReconnect) {
748
+ this._completed = true;
749
+ this._completionError = new Error(
750
+ `Stream gap at index ${index} (expected ${this._nextExpectedIndex}); reconnect not allowed`
751
+ );
752
+ this._notifyConsumer();
753
+ return;
754
+ }
365
755
  this._sendAck(this._nextExpectedIndex, true);
366
756
  return;
367
757
  }
@@ -397,10 +787,9 @@ var RpcStream = class {
397
787
  }
398
788
  // -- AsyncIterable --
399
789
  [Symbol.asyncIterator]() {
400
- if (this._iterating) throw new Error("RpcStream can only be iterated once.");
790
+ if (this._iterating)
791
+ throw new Error("RpcStream can only be iterated once.");
401
792
  this._iterating = true;
402
- let bufferIndex = 0;
403
- let consumedIndex = 0;
404
793
  const self = this;
405
794
  return {
406
795
  async next() {
@@ -409,10 +798,8 @@ var RpcStream = class {
409
798
  self._sendAck(0, true);
410
799
  }
411
800
  while (true) {
412
- if (bufferIndex < self._buffer.length) {
413
- const value = self._buffer[bufferIndex];
414
- bufferIndex++;
415
- consumedIndex++;
801
+ if (!self._buffer.isEmpty()) {
802
+ const value = self._buffer.shift();
416
803
  return { value, done: false };
417
804
  }
418
805
  if (self._completed) {
@@ -451,17 +838,30 @@ var RpcStream = class {
451
838
  this._sendAck(nextIndex, false);
452
839
  }
453
840
  }
454
- _sendAck(nextIndex, _mustReset) {
841
+ static _emptyGuid = "00000000-0000-0000-0000-000000000000";
842
+ _sendAck(nextIndex, mustReset) {
455
843
  this._ackSentUpTo = nextIndex;
456
844
  const conn = this.peer.connection;
457
845
  if (conn) {
458
- this.peer.hub.systemCallSender.ack(conn, this.id.localId, nextIndex, this.id.hostId);
846
+ const hostId = mustReset ? this.id.hostId : _RpcStream._emptyGuid;
847
+ this.peer.hub.systemCallSender.ack(
848
+ conn,
849
+ this.peer.format,
850
+ this.id.localId,
851
+ nextIndex,
852
+ hostId
853
+ );
459
854
  }
460
855
  }
461
856
  _sendAckEnd() {
462
857
  const conn = this.peer.connection;
463
858
  if (conn) {
464
- this.peer.hub.systemCallSender.ackEnd(conn, this.id.localId, this.id.hostId);
859
+ this.peer.hub.systemCallSender.ackEnd(
860
+ conn,
861
+ this.peer.format,
862
+ this.id.localId,
863
+ this.id.hostId
864
+ );
465
865
  }
466
866
  }
467
867
  };
@@ -483,6 +883,8 @@ function resolveStreamRefs(value, peer) {
483
883
  return value;
484
884
  }
485
885
  if (typeof value === "object") {
886
+ if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer)
887
+ return value;
486
888
  for (const key of Object.keys(value)) {
487
889
  value[key] = resolveStreamRefs(
488
890
  value[key],
@@ -528,21 +930,39 @@ var RpcSystemCallHandler = class {
528
930
  }
529
931
  case RpcSystemCalls.item: {
530
932
  const stream = peer.remoteObjects.get(relatedId);
531
- if (stream) stream.onItem(args[0], resolveStreamRefs(args[1], peer));
933
+ if (!stream) {
934
+ console.warn(
935
+ `[RpcSysHandler] $sys.I: no stream for relatedId=${relatedId}`
936
+ );
937
+ } else {
938
+ stream.onItem(
939
+ args[0],
940
+ resolveStreamRefs(args[1], peer)
941
+ );
942
+ }
532
943
  break;
533
944
  }
534
945
  case RpcSystemCalls.batch: {
535
946
  const stream = peer.remoteObjects.get(relatedId);
536
- if (stream) {
947
+ if (!stream) {
948
+ console.warn(
949
+ `[RpcSysHandler] $sys.B: no stream for relatedId=${relatedId}`
950
+ );
951
+ } else if (Array.isArray(args[1])) {
537
952
  const items = args[1];
538
- for (let i = 0; i < items.length; i++) items[i] = resolveStreamRefs(items[i], peer);
953
+ for (let i = 0; i < items.length; i++)
954
+ items[i] = resolveStreamRefs(items[i], peer);
539
955
  stream.onBatch(args[0], items);
540
956
  }
541
957
  break;
542
958
  }
543
959
  case RpcSystemCalls.end: {
544
960
  const stream = peer.remoteObjects.get(relatedId);
545
- if (stream) {
961
+ if (!stream) {
962
+ console.warn(
963
+ `[RpcSysHandler] $sys.End: no stream for relatedId=${relatedId}`
964
+ );
965
+ } else {
546
966
  const errorInfo = args[1];
547
967
  const msg = errorInfo?.Message ?? errorInfo?.message;
548
968
  const error = msg ? new Error(msg) : null;
@@ -560,76 +980,142 @@ var RpcSystemCallHandler = class {
560
980
  sender?.onAckEnd(args[0]);
561
981
  break;
562
982
  }
983
+ default: {
984
+ if (method === "$sys.Disconnect" || method?.startsWith("$sys.Disconnect")) {
985
+ const ids = args[0];
986
+ if (Array.isArray(ids)) {
987
+ for (const id of ids) {
988
+ const remoteObj = peer.remoteObjects.get(id);
989
+ if (remoteObj && typeof remoteObj.disconnect === "function") {
990
+ remoteObj.disconnect();
991
+ }
992
+ const sharedObj = peer.sharedObjects.get(id);
993
+ if (sharedObj && typeof sharedObj.disconnect === "function") {
994
+ sharedObj.disconnect();
995
+ }
996
+ }
997
+ }
998
+ }
999
+ break;
1000
+ }
563
1001
  }
564
1002
  }
565
1003
  };
566
1004
 
567
1005
  // src/rpc-system-call-sender.ts
568
- var RpcSystemCallSender = class {
569
- handshake(conn, peerId, hubId, index) {
570
- const msg = serializeMessage({ Method: RpcSystemCalls.handshake }, [{
1006
+ var RpcSystemCallSender = class _RpcSystemCallSender {
1007
+ // Reusable envelopes — one per hot-path method.
1008
+ _itemEnv = {
1009
+ Method: RpcSystemCalls.item,
1010
+ RelatedId: 0
1011
+ };
1012
+ _batchEnv = {
1013
+ Method: RpcSystemCalls.batch,
1014
+ RelatedId: 0
1015
+ };
1016
+ _endEnv = {
1017
+ Method: RpcSystemCalls.end,
1018
+ RelatedId: 0
1019
+ };
1020
+ _ackEnv = {
1021
+ Method: RpcSystemCalls.ack,
1022
+ RelatedId: 0
1023
+ };
1024
+ _ackEndEnv = {
1025
+ Method: RpcSystemCalls.ackEnd,
1026
+ RelatedId: 0
1027
+ };
1028
+ // Reusable arg arrays.
1029
+ _itemArgs = [0, null];
1030
+ _batchArgs = [0, null];
1031
+ _endArgs = [0, { TypeRef: "", Message: "" }];
1032
+ _ackArgs = [0, ""];
1033
+ _ackEndArgs = [""];
1034
+ static NO_ERROR = Object.freeze({ TypeRef: "", Message: "" });
1035
+ registry;
1036
+ _send(conn, format, envelope, args) {
1037
+ const wireData = format.serializeMessage(
1038
+ envelope,
1039
+ args,
1040
+ conn.encoder,
1041
+ this.registry
1042
+ );
1043
+ if (typeof wireData === "string") {
1044
+ conn.send(wireData);
1045
+ } else {
1046
+ conn.sendBinary(wireData);
1047
+ }
1048
+ }
1049
+ handshake(conn, format, peerId, hubId, index) {
1050
+ const handshakeArg = format.isBinary ? [peerId, null, hubId, 2, index] : {
571
1051
  RemotePeerId: peerId,
572
1052
  RemoteApiVersionSet: null,
573
1053
  RemoteHubId: hubId,
574
1054
  ProtocolVersion: 2,
575
1055
  Index: index
576
- }]);
577
- conn.send(msg);
578
- }
579
- ok(conn, relatedId, result) {
580
- const msg = serializeMessage({ Method: RpcSystemCalls.ok, RelatedId: relatedId }, [result]);
581
- conn.send(msg);
1056
+ };
1057
+ this._send(conn, format, { Method: RpcSystemCalls.handshake }, [
1058
+ handshakeArg
1059
+ ]);
1060
+ }
1061
+ ok(conn, format, relatedId, result) {
1062
+ this._send(
1063
+ conn,
1064
+ format,
1065
+ { Method: RpcSystemCalls.ok, RelatedId: relatedId },
1066
+ [result]
1067
+ );
582
1068
  }
583
- error(conn, relatedId, error) {
1069
+ error(conn, format, relatedId, error) {
584
1070
  const message = error instanceof Error ? error.message : String(error);
585
- const msg = serializeMessage(
1071
+ this._send(
1072
+ conn,
1073
+ format,
586
1074
  { Method: RpcSystemCalls.error, RelatedId: relatedId },
587
1075
  [{ Message: message }]
588
1076
  );
589
- conn.send(msg);
590
- }
591
- cancel(conn, relatedId) {
592
- const msg = serializeMessage({ Method: RpcSystemCalls.cancel, RelatedId: relatedId });
593
- conn.send(msg);
594
- }
595
- keepAlive(conn, activeCallIds) {
596
- const msg = serializeMessage({ Method: RpcSystemCalls.keepAlive }, [activeCallIds]);
597
- conn.send(msg);
598
- }
599
- item(conn, localId, index, item) {
600
- const msg = serializeMessage(
601
- { Method: RpcSystemCalls.item, RelatedId: localId },
602
- [index, item]
603
- );
604
- conn.send(msg);
605
1077
  }
606
- batch(conn, localId, index, items) {
607
- const msg = serializeMessage(
608
- { Method: RpcSystemCalls.batch, RelatedId: localId },
609
- [index, items]
610
- );
611
- conn.send(msg);
612
- }
613
- end(conn, localId, index, error = null) {
614
- const msg = serializeMessage(
615
- { Method: RpcSystemCalls.end, RelatedId: localId },
616
- [index, error]
617
- );
618
- conn.send(msg);
619
- }
620
- ack(conn, localId, nextIndex, hostId) {
621
- const msg = serializeMessage(
622
- { Method: RpcSystemCalls.ack, RelatedId: localId },
623
- [nextIndex, hostId]
624
- );
625
- conn.send(msg);
1078
+ cancel(conn, format, relatedId) {
1079
+ this._send(conn, format, {
1080
+ Method: RpcSystemCalls.cancel,
1081
+ RelatedId: relatedId
1082
+ });
626
1083
  }
627
- ackEnd(conn, localId, hostId) {
628
- const msg = serializeMessage(
629
- { Method: RpcSystemCalls.ackEnd, RelatedId: localId },
630
- [hostId]
631
- );
632
- conn.send(msg);
1084
+ keepAlive(conn, format, activeCallIds) {
1085
+ this._send(conn, format, { Method: RpcSystemCalls.keepAlive }, [
1086
+ activeCallIds
1087
+ ]);
1088
+ }
1089
+ item(conn, format, localId, index, item) {
1090
+ this._itemEnv.RelatedId = localId;
1091
+ this._itemArgs[0] = index;
1092
+ this._itemArgs[1] = item;
1093
+ this._send(conn, format, this._itemEnv, this._itemArgs);
1094
+ this._itemArgs[1] = null;
1095
+ }
1096
+ batch(conn, format, localId, index, items) {
1097
+ this._batchEnv.RelatedId = localId;
1098
+ this._batchArgs[0] = index;
1099
+ this._batchArgs[1] = items;
1100
+ this._send(conn, format, this._batchEnv, this._batchArgs);
1101
+ this._batchArgs[1] = null;
1102
+ }
1103
+ end(conn, format, localId, index, error = _RpcSystemCallSender.NO_ERROR) {
1104
+ this._endEnv.RelatedId = localId;
1105
+ this._endArgs[0] = index;
1106
+ this._endArgs[1] = error;
1107
+ this._send(conn, format, this._endEnv, this._endArgs);
1108
+ }
1109
+ ack(conn, format, localId, nextIndex, hostId) {
1110
+ this._ackEnv.RelatedId = localId;
1111
+ this._ackArgs[0] = nextIndex;
1112
+ this._ackArgs[1] = hostId;
1113
+ this._send(conn, format, this._ackEnv, this._ackArgs);
1114
+ }
1115
+ ackEnd(conn, format, localId, hostId) {
1116
+ this._ackEndEnv.RelatedId = localId;
1117
+ this._ackEndArgs[0] = hostId;
1118
+ this._send(conn, format, this._ackEndEnv, this._ackEndArgs);
633
1119
  }
634
1120
  };
635
1121
 
@@ -641,29 +1127,42 @@ var RpcStreamSender = class {
641
1127
  id;
642
1128
  kind = RpcObjectKind.Local;
643
1129
  allowReconnect;
1130
+ isRealTime;
1131
+ canSkipTo;
644
1132
  peer;
645
1133
  ackPeriod;
646
1134
  ackAdvance;
647
1135
  _nextIndex = 0;
648
1136
  _ended = false;
649
1137
  _started = new PromiseSource4();
650
- constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE, allowReconnect = true) {
1138
+ _lastAckedIndex = 0;
1139
+ _ackWaiting = null;
1140
+ constructor(peer, ackPeriod = DEFAULT_ACK_PERIOD, ackAdvance = DEFAULT_ACK_ADVANCE, allowReconnect = true, isRealTime = false, canSkipTo = () => true) {
651
1141
  const localId = peer.sharedObjects.nextId();
652
1142
  this.id = { hostId: peer.hub.hubId, localId };
653
1143
  this.allowReconnect = allowReconnect;
1144
+ this.isRealTime = isRealTime;
1145
+ this.canSkipTo = canSkipTo;
654
1146
  this.peer = peer;
655
1147
  this.ackPeriod = ackPeriod;
656
1148
  this.ackAdvance = ackAdvance;
657
1149
  }
658
1150
  /** Returns the stream reference string for the $sys.Ok response. */
659
1151
  toRef() {
660
- return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? "1" : "0"}`;
1152
+ return `${this.id.hostId},${this.id.localId},${this.ackPeriod},${this.ackAdvance},${this.allowReconnect ? "1" : "0"},${this.isRealTime ? "1" : "0"}`;
661
1153
  }
662
1154
  /** Called by system call handler when $sys.Ack is received from the client. */
663
- onAck(_nextIndex, _hostId) {
1155
+ onAck(nextIndex, _hostId) {
664
1156
  if (!this._started.isCompleted) {
665
1157
  this._started.resolve();
666
1158
  }
1159
+ if (nextIndex > this._lastAckedIndex) {
1160
+ this._lastAckedIndex = nextIndex;
1161
+ }
1162
+ if (this._ackWaiting) {
1163
+ this._ackWaiting.resolve();
1164
+ this._ackWaiting = null;
1165
+ }
667
1166
  }
668
1167
  /** Called by system call handler when $sys.AckEnd is received from the client. */
669
1168
  onAckEnd(_hostId) {
@@ -675,7 +1174,13 @@ var RpcStreamSender = class {
675
1174
  if (this._ended) return;
676
1175
  const conn = this.peer.connection;
677
1176
  if (!conn) return;
678
- this.peer.hub.systemCallSender.item(conn, this.id.localId, this._nextIndex, item);
1177
+ this.peer.hub.systemCallSender.item(
1178
+ conn,
1179
+ this.peer.format,
1180
+ this.id.localId,
1181
+ this._nextIndex,
1182
+ item
1183
+ );
679
1184
  this._nextIndex++;
680
1185
  }
681
1186
  /** Send a batch of items to the client. */
@@ -683,7 +1188,13 @@ var RpcStreamSender = class {
683
1188
  if (this._ended || items.length === 0) return;
684
1189
  const conn = this.peer.connection;
685
1190
  if (!conn) return;
686
- this.peer.hub.systemCallSender.batch(conn, this.id.localId, this._nextIndex, items);
1191
+ this.peer.hub.systemCallSender.batch(
1192
+ conn,
1193
+ this.peer.format,
1194
+ this.id.localId,
1195
+ this._nextIndex,
1196
+ items
1197
+ );
687
1198
  this._nextIndex += items.length;
688
1199
  }
689
1200
  /** Signal stream completion to the client. */
@@ -692,19 +1203,63 @@ var RpcStreamSender = class {
692
1203
  this._ended = true;
693
1204
  const conn = this.peer.connection;
694
1205
  if (!conn) return;
695
- const errorInfo = error ? { Message: error.message } : null;
696
- this.peer.hub.systemCallSender.end(conn, this.id.localId, this._nextIndex, errorInfo);
1206
+ const errorInfo = error ? { TypeRef: "System.Exception", Message: error.message } : { TypeRef: "", Message: "" };
1207
+ this.peer.hub.systemCallSender.end(
1208
+ conn,
1209
+ this.peer.format,
1210
+ this.id.localId,
1211
+ this._nextIndex,
1212
+ errorInfo
1213
+ );
697
1214
  }
698
1215
  /**
699
1216
  * Consume an AsyncIterable and send all items to the client.
700
1217
  * Waits for the client's initial Ack before starting to pump items.
1218
+ *
1219
+ * When `isRealTime` is true, applies flow control based on `ackAdvance`:
1220
+ * if the sender is `ackAdvance` items ahead of the last ACK, it enters
1221
+ * skip mode — draining the source and discarding items until it finds
1222
+ * one where `canSkipTo` returns true.
701
1223
  */
702
1224
  async writeFrom(source) {
703
1225
  await this._started.promise;
1226
+ const iterator = source[Symbol.asyncIterator]();
704
1227
  try {
705
- for await (const item of source) {
1228
+ while (true) {
1229
+ const next = await iterator.next();
1230
+ if (next.done || this._ended) break;
1231
+ const item = next.value;
1232
+ if (this._nextIndex < this._lastAckedIndex + this.ackAdvance) {
1233
+ this.sendItem(item);
1234
+ continue;
1235
+ }
1236
+ if (!this.isRealTime) {
1237
+ await this._waitForAckBudget();
1238
+ if (this._ended) return;
1239
+ this.sendItem(item);
1240
+ continue;
1241
+ }
1242
+ let latestSkipTarget = this.canSkipTo(item) ? item : void 0;
1243
+ let sourceExhausted = false;
1244
+ while (this._nextIndex >= this._lastAckedIndex + this.ackAdvance) {
1245
+ if (this._ended) return;
1246
+ const n = await iterator.next();
1247
+ if (n.done) {
1248
+ sourceExhausted = true;
1249
+ break;
1250
+ }
1251
+ if (this.canSkipTo(n.value)) {
1252
+ latestSkipTarget = n.value;
1253
+ }
1254
+ }
706
1255
  if (this._ended) return;
707
- this.sendItem(item);
1256
+ if (latestSkipTarget !== void 0) {
1257
+ this.sendItem(latestSkipTarget);
1258
+ }
1259
+ if (sourceExhausted) {
1260
+ if (!this._ended) this.sendEnd();
1261
+ return;
1262
+ }
708
1263
  }
709
1264
  if (!this._ended) {
710
1265
  this.sendEnd();
@@ -715,6 +1270,14 @@ var RpcStreamSender = class {
715
1270
  }
716
1271
  }
717
1272
  }
1273
+ /** Wait until _nextIndex < _lastAckedIndex + ackAdvance. */
1274
+ async _waitForAckBudget() {
1275
+ while (this._nextIndex >= this._lastAckedIndex + this.ackAdvance) {
1276
+ if (this._ended) return;
1277
+ this._ackWaiting = new PromiseSource4();
1278
+ await this._ackWaiting.promise;
1279
+ }
1280
+ }
718
1281
  // -- IRpcObject --
719
1282
  reconnect() {
720
1283
  }
@@ -736,6 +1299,9 @@ var RpcRemoteObjectTracker = class {
736
1299
  get(localId) {
737
1300
  return this._objects.get(localId);
738
1301
  }
1302
+ keys() {
1303
+ return this._objects.keys();
1304
+ }
739
1305
  unregister(obj) {
740
1306
  this._objects.delete(obj.id.localId);
741
1307
  }
@@ -747,10 +1313,8 @@ var RpcRemoteObjectTracker = class {
747
1313
  }
748
1314
  reconnectAll() {
749
1315
  for (const obj of this._objects.values()) {
750
- if (obj.allowReconnect)
751
- obj.reconnect();
752
- else
753
- obj.disconnect();
1316
+ if (obj.allowReconnect) obj.reconnect();
1317
+ else obj.disconnect();
754
1318
  }
755
1319
  }
756
1320
  };
@@ -768,6 +1332,9 @@ var RpcSharedObjectTracker = class {
768
1332
  get(localId) {
769
1333
  return this._objects.get(localId);
770
1334
  }
1335
+ keys() {
1336
+ return this._objects.keys();
1337
+ }
771
1338
  unregister(obj) {
772
1339
  this._objects.delete(obj.id.localId);
773
1340
  }
@@ -791,6 +1358,129 @@ var RpcClientPeerReconnectDelayer = class extends RetryDelayer {
791
1358
  }
792
1359
  };
793
1360
 
1361
+ // src/rpc-serialization-format.ts
1362
+ import { Decoder as Decoder3 } from "@msgpack/msgpack";
1363
+ var RpcSerializationFormat = class _RpcSerializationFormat {
1364
+ /** Create a reusable binary encoder for this format. Returns undefined for text formats. */
1365
+ createEncoder() {
1366
+ return void 0;
1367
+ }
1368
+ /** Create a reusable binary decoder for this format. Returns undefined for text formats. */
1369
+ createDecoder() {
1370
+ return void 0;
1371
+ }
1372
+ // --- Static registry ---
1373
+ static _all = /* @__PURE__ */ new Map();
1374
+ static register(format) {
1375
+ _RpcSerializationFormat._all.set(format.key, format);
1376
+ }
1377
+ static get(key) {
1378
+ const format = _RpcSerializationFormat._all.get(key);
1379
+ if (!format)
1380
+ throw new Error(`Unknown RPC serialization format: "${key}"`);
1381
+ return format;
1382
+ }
1383
+ static tryGet(key) {
1384
+ return _RpcSerializationFormat._all.get(key);
1385
+ }
1386
+ };
1387
+ var RpcJsonSerializationFormat = class extends RpcSerializationFormat {
1388
+ key;
1389
+ isBinary = false;
1390
+ isCompact = false;
1391
+ constructor(key) {
1392
+ super();
1393
+ this.key = key;
1394
+ }
1395
+ serializeMessage(message, args) {
1396
+ return serializeMessage(message, args);
1397
+ }
1398
+ deserializeTextMessage(raw) {
1399
+ return deserializeMessage(raw);
1400
+ }
1401
+ splitBinaryFrame() {
1402
+ throw new Error("Text format does not support binary frames");
1403
+ }
1404
+ serializeBinaryFrame() {
1405
+ throw new Error("Text format does not support binary frames");
1406
+ }
1407
+ };
1408
+ var RpcMessagePackSerializationFormat = class extends RpcSerializationFormat {
1409
+ key;
1410
+ isBinary = true;
1411
+ isCompact = false;
1412
+ constructor(key) {
1413
+ super();
1414
+ this.key = key;
1415
+ }
1416
+ serializeMessage(message, args, encoder) {
1417
+ return serializeBinaryMessage(message, args, encoder);
1418
+ }
1419
+ deserializeTextMessage(raw) {
1420
+ return deserializeMessage(raw);
1421
+ }
1422
+ splitBinaryFrame(frame, decoder) {
1423
+ return splitBinaryFrame(frame, decoder);
1424
+ }
1425
+ serializeBinaryFrame(messages) {
1426
+ return serializeBinaryFrame(messages);
1427
+ }
1428
+ createEncoder() {
1429
+ return createBinaryEncoder();
1430
+ }
1431
+ createDecoder() {
1432
+ return new Decoder3();
1433
+ }
1434
+ };
1435
+ var RpcMessagePackCompactSerializationFormat = class extends RpcSerializationFormat {
1436
+ key;
1437
+ isBinary = true;
1438
+ isCompact = true;
1439
+ constructor(key) {
1440
+ super();
1441
+ this.key = key;
1442
+ }
1443
+ serializeMessage(message, args, encoder, registry) {
1444
+ return serializeCompactBinaryMessage(
1445
+ message,
1446
+ args,
1447
+ registry,
1448
+ encoder
1449
+ );
1450
+ }
1451
+ deserializeTextMessage(raw) {
1452
+ return deserializeMessage(raw);
1453
+ }
1454
+ splitBinaryFrame(frame, decoder, registry) {
1455
+ return splitCompactBinaryFrame(frame, registry, decoder);
1456
+ }
1457
+ serializeBinaryFrame(messages) {
1458
+ return serializeBinaryFrame(messages);
1459
+ }
1460
+ createEncoder() {
1461
+ return createBinaryEncoder();
1462
+ }
1463
+ createDecoder() {
1464
+ return new Decoder3();
1465
+ }
1466
+ };
1467
+ RpcSerializationFormat.register(new RpcJsonSerializationFormat("json5"));
1468
+ RpcSerializationFormat.register(new RpcJsonSerializationFormat("json5np"));
1469
+ RpcSerializationFormat.register(new RpcJsonSerializationFormat("njson5"));
1470
+ RpcSerializationFormat.register(new RpcJsonSerializationFormat("njson5np"));
1471
+ RpcSerializationFormat.register(
1472
+ new RpcMessagePackSerializationFormat("msgpack6")
1473
+ );
1474
+ RpcSerializationFormat.register(
1475
+ new RpcMessagePackSerializationFormat("mempack6")
1476
+ );
1477
+ RpcSerializationFormat.register(
1478
+ new RpcMessagePackCompactSerializationFormat("msgpack6c")
1479
+ );
1480
+ RpcSerializationFormat.register(
1481
+ new RpcMessagePackCompactSerializationFormat("mempack6c")
1482
+ );
1483
+
794
1484
  // src/rpc-peer.ts
795
1485
  var DEFAULT_SERIALIZATION_FORMAT = "json5np";
796
1486
  var RPC_CLOSE_CODE_UNSUPPORTED_FORMAT = 4001;
@@ -839,17 +1529,27 @@ var RpcPeer = class {
839
1529
  this._startKeepAlive();
840
1530
  });
841
1531
  }
1532
+ /** The serialization format used by this peer. Override in subclasses. */
1533
+ get format() {
1534
+ return RpcSerializationFormat.get("json5np");
1535
+ }
842
1536
  call(method, args, options) {
843
1537
  const callId = this.outbound.nextId();
844
1538
  const outboundCall = options?.outboundCallFactory ? options.outboundCallFactory(callId, method) : new RpcOutboundCall(callId, method);
845
- const msg = serializeMessage(
846
- { Method: method, RelatedId: callId, CallType: options?.callTypeId ?? 0 },
847
- args
1539
+ const envelope = {
1540
+ Method: method,
1541
+ RelatedId: callId,
1542
+ CallType: options?.callTypeId ?? 0
1543
+ };
1544
+ outboundCall.serializedWireData = this.format.serializeMessage(
1545
+ envelope,
1546
+ args,
1547
+ this._connection?.encoder,
1548
+ this._hub.registry
848
1549
  );
849
- outboundCall.serializedMessage = msg;
850
1550
  if (this._connection !== void 0) {
851
1551
  this.outbound.register(outboundCall);
852
- this._connection.send(msg);
1552
+ this._sendWireData(outboundCall.serializedWireData);
853
1553
  } else {
854
1554
  this._pendingSends.push(outboundCall);
855
1555
  }
@@ -863,12 +1563,16 @@ var RpcPeer = class {
863
1563
  outboundCall.result.reject(new Error("Call cancelled."));
864
1564
  outboundCall.onDisconnect();
865
1565
  if (peer._connection !== void 0)
866
- hub.systemCallSender.cancel(peer._connection, callId);
1566
+ hub.systemCallSender.cancel(peer._connection, peer.format, callId);
867
1567
  } else {
868
- const idx = peer._pendingSends.findIndex((c) => c.callId === callId);
1568
+ const idx = peer._pendingSends.findIndex(
1569
+ (c) => c.callId === callId
1570
+ );
869
1571
  if (idx !== -1) {
870
1572
  peer._pendingSends.splice(idx, 1);
871
- outboundCall.result.reject(new Error("Call cancelled."));
1573
+ outboundCall.result.reject(
1574
+ new Error("Call cancelled.")
1575
+ );
872
1576
  outboundCall.onDisconnect();
873
1577
  }
874
1578
  }
@@ -880,8 +1584,14 @@ var RpcPeer = class {
880
1584
  }
881
1585
  callNoWait(method, args) {
882
1586
  if (this._connection === void 0) return;
883
- const msg = serializeMessage({ Method: method, RelatedId: 0 }, args);
884
- this._connection.send(msg);
1587
+ const envelope = { Method: method, RelatedId: 0 };
1588
+ const wireData = this.format.serializeMessage(
1589
+ envelope,
1590
+ args,
1591
+ this._connection.encoder,
1592
+ this._hub.registry
1593
+ );
1594
+ this._sendWireData(wireData);
885
1595
  }
886
1596
  close() {
887
1597
  this._stopKeepAlive();
@@ -900,23 +1610,41 @@ var RpcPeer = class {
900
1610
  }
901
1611
  /** Send any messages buffered while disconnected. Call after connection + handshake are ready. */
902
1612
  _flushPendingSends() {
903
- if (this._pendingSends.length === 0 || this._connection === void 0) return;
1613
+ if (this._pendingSends.length === 0 || this._connection === void 0)
1614
+ return;
904
1615
  for (const call of this._pendingSends) {
905
1616
  this.outbound.register(call);
906
- this._connection.send(call.serializedMessage);
1617
+ this._sendWireData(call.serializedWireData);
907
1618
  }
908
1619
  this._pendingSends.length = 0;
909
1620
  }
1621
+ /** Send pre-serialized wire data through the current connection. */
1622
+ _sendWireData(data) {
1623
+ if (this._connection === void 0) return;
1624
+ if (typeof data === "string") {
1625
+ this._connection.send(data);
1626
+ } else {
1627
+ this._connection.sendBinary(data);
1628
+ }
1629
+ }
910
1630
  /** Override in subclasses to handle the remote peer's handshake response. */
911
1631
  _onHandshakeReceived(_handshake) {
912
1632
  }
913
- _handleMessage(raw) {
914
- const { message, args } = deserializeMessage(raw);
1633
+ _handleMessage(received) {
1634
+ let message;
1635
+ let args;
1636
+ if (received.kind === "binary") {
1637
+ message = received.message;
1638
+ args = received.args;
1639
+ } else {
1640
+ const parsed = deserializeMessage(received.raw);
1641
+ message = parsed.message;
1642
+ args = parsed.args;
1643
+ }
915
1644
  const method = message.Method ?? "";
916
1645
  if (method === RpcSystemCalls.handshake) {
917
1646
  const handshake = args[0];
918
- if (handshake !== void 0)
919
- this._onHandshakeReceived(handshake);
1647
+ if (handshake !== void 0) this._onHandshakeReceived(handshake);
920
1648
  return;
921
1649
  }
922
1650
  if (method.startsWith("$sys")) {
@@ -938,19 +1666,42 @@ var RpcPeer = class {
938
1666
  if (serviceHost !== void 0) {
939
1667
  void (async () => {
940
1668
  try {
941
- const context = this._connection !== void 0 ? { __rpcDispatch: true, callId: relatedId, connection: this._connection } : void 0;
942
- const result = await serviceHost.dispatch(method, args, context);
1669
+ const context = this._connection !== void 0 ? {
1670
+ __rpcDispatch: true,
1671
+ callId: relatedId,
1672
+ connection: this._connection
1673
+ } : void 0;
1674
+ const result = await serviceHost.dispatch(
1675
+ method,
1676
+ args,
1677
+ context
1678
+ );
943
1679
  if (methodDef?.stream === true && this._connection !== void 0) {
944
1680
  const sender = new RpcStreamSender(this);
945
1681
  this.sharedObjects.register(sender);
946
- this._hub.systemCallSender.ok(this._connection, relatedId, sender.toRef());
1682
+ this._hub.systemCallSender.ok(
1683
+ this._connection,
1684
+ this.format,
1685
+ relatedId,
1686
+ sender.toRef()
1687
+ );
947
1688
  void sender.writeFrom(result);
948
1689
  } else if (!isNoWait && this._connection !== void 0) {
949
- this._hub.systemCallSender.ok(this._connection, relatedId, result);
1690
+ this._hub.systemCallSender.ok(
1691
+ this._connection,
1692
+ this.format,
1693
+ relatedId,
1694
+ result
1695
+ );
950
1696
  }
951
1697
  } catch (e) {
952
1698
  if (!isNoWait && this._connection !== void 0) {
953
- this._hub.systemCallSender.error(this._connection, relatedId, e);
1699
+ this._hub.systemCallSender.error(
1700
+ this._connection,
1701
+ this.format,
1702
+ relatedId,
1703
+ e
1704
+ );
954
1705
  }
955
1706
  } finally {
956
1707
  if (!isNoWait) {
@@ -963,7 +1714,9 @@ var RpcPeer = class {
963
1714
  _startKeepAlive() {
964
1715
  this._keepAliveTimer = setInterval(() => {
965
1716
  if (this._connection !== void 0) {
966
- this._hub.systemCallSender.keepAlive(this._connection, this.outbound.activeCallIds());
1717
+ this._hub.systemCallSender.keepAlive(this._connection, this.format, [
1718
+ ...this.remoteObjects.keys()
1719
+ ]);
967
1720
  }
968
1721
  }, 15e3);
969
1722
  }
@@ -1011,10 +1764,15 @@ var RpcClientPeer = class extends RpcPeer {
1011
1764
  this._reconnectsAt = value;
1012
1765
  this.reconnectsAtChanged.trigger();
1013
1766
  }
1767
+ _format;
1014
1768
  constructor(hub, url, serializationFormat) {
1015
1769
  super(url, hub);
1016
1770
  this.clientId = guidToBase64Url(this.id);
1017
1771
  this.serializationFormat = serializationFormat ?? DEFAULT_SERIALIZATION_FORMAT;
1772
+ this._format = RpcSerializationFormat.get(this.serializationFormat);
1773
+ }
1774
+ get format() {
1775
+ return this._format;
1018
1776
  }
1019
1777
  get connectionKind() {
1020
1778
  return this._connectionKind;
@@ -1032,7 +1790,12 @@ var RpcClientPeer = class extends RpcPeer {
1032
1790
  let lastCloseReason = "";
1033
1791
  try {
1034
1792
  const ws = wsFactory?.(connUrl) ?? new WebSocket(connUrl);
1035
- const conn = new RpcWebSocketConnection(ws);
1793
+ const conn = new RpcWebSocketConnection(
1794
+ ws,
1795
+ this.format.isBinary,
1796
+ this.format,
1797
+ this._hub.registry
1798
+ );
1036
1799
  this._connectionKind = 1 /* Connecting */;
1037
1800
  conn.closed.add((ev) => {
1038
1801
  lastCloseCode = ev.code;
@@ -1041,11 +1804,21 @@ var RpcClientPeer = class extends RpcPeer {
1041
1804
  this._pendingHandshake = new PromiseSource5();
1042
1805
  this.setupConnection(conn);
1043
1806
  this._connection = void 0;
1044
- const closedRejection = new Promise((_, reject) => conn.closed.add(() => reject(new Error("Connection failed"))));
1807
+ const closedRejection = new Promise(
1808
+ (_, reject) => conn.closed.add(
1809
+ () => reject(new Error("Connection failed"))
1810
+ )
1811
+ );
1045
1812
  closedRejection.catch(() => {
1046
1813
  });
1047
1814
  await Promise.race([conn.whenConnected, closedRejection]);
1048
- this._hub.systemCallSender.handshake(conn, this.id, this._hub.hubId, ++this._handshakeIndex);
1815
+ this._hub.systemCallSender.handshake(
1816
+ conn,
1817
+ this.format,
1818
+ this.id,
1819
+ this._hub.hubId,
1820
+ ++this._handshakeIndex
1821
+ );
1049
1822
  const remoteHandshake = await Promise.race([
1050
1823
  this._pendingHandshake.promise,
1051
1824
  closedRejection
@@ -1098,7 +1871,6 @@ var RpcClientPeer = class extends RpcPeer {
1098
1871
  * Regular in-flight calls are re-sent transparently. */
1099
1872
  _reconnect(_isPeerChanged) {
1100
1873
  if (this._connection === void 0) return;
1101
- const conn = this._connection;
1102
1874
  if (_isPeerChanged) {
1103
1875
  this.remoteObjects.disconnectAll();
1104
1876
  } else {
@@ -1110,7 +1882,7 @@ var RpcClientPeer = class extends RpcPeer {
1110
1882
  call.onDisconnect();
1111
1883
  this.outbound.remove(call.callId);
1112
1884
  } else {
1113
- conn.send(call.serializedMessage);
1885
+ this._sendWireData(call.serializedWireData);
1114
1886
  }
1115
1887
  }
1116
1888
  this._flushPendingSends();
@@ -1133,7 +1905,13 @@ var RpcServerPeer = class extends RpcPeer {
1133
1905
  }
1134
1906
  _onHandshakeReceived(_handshake) {
1135
1907
  if (this._connection !== void 0) {
1136
- this._hub.systemCallSender.handshake(this._connection, this.id, this._hub.hubId, 0);
1908
+ this._hub.systemCallSender.handshake(
1909
+ this._connection,
1910
+ this.format,
1911
+ this.id,
1912
+ this._hub.hubId,
1913
+ 0
1914
+ );
1137
1915
  }
1138
1916
  }
1139
1917
  };
@@ -1161,12 +1939,16 @@ var RpcServiceHost = class {
1161
1939
  for (const methodDef of def.methods.values()) {
1162
1940
  const fn = impl[methodDef.name];
1163
1941
  if (!fn) continue;
1164
- this._methods.set(wireMethodName(methodDef), { def: methodDef, fn });
1942
+ this._methods.set(wireMethodName(methodDef), {
1943
+ def: methodDef,
1944
+ fn
1945
+ });
1165
1946
  }
1166
1947
  }
1167
1948
  async dispatch(wireMethod, args, context) {
1168
1949
  const entry = this._methods.get(wireMethod);
1169
- if (entry === void 0) throw new Error(`Method not found: ${wireMethod}`);
1950
+ if (entry === void 0)
1951
+ throw new Error(`Method not found: ${wireMethod}`);
1170
1952
  if (context !== void 0 && entry.def.callTypeId !== 0) {
1171
1953
  return await entry.fn(...args, context);
1172
1954
  }
@@ -1205,6 +1987,539 @@ function rpcMethod(options) {
1205
1987
  };
1206
1988
  }
1207
1989
 
1990
+ // src/rpc-xxhash3.ts
1991
+ var U64_MASK = 0xffffffffffffffffn;
1992
+ var U32_MASK = 0xffffffffn;
1993
+ var PRIME32_1 = 2654435761;
1994
+ var PRIME32_2 = 2246822519;
1995
+ var PRIME32_3 = 3266489917;
1996
+ var PRIME64_1 = 0x9e3779b185ebca87n;
1997
+ var PRIME64_2 = 0xc2b2ae3d27d4eb4fn;
1998
+ var PRIME64_3 = 0x165667b19e3779f9n;
1999
+ var PRIME64_4 = 0x85ebca77c2b2ae63n;
2000
+ var PRIME64_5 = 0x27d4eb2f165667c5n;
2001
+ var XXH3_SECRET_SIZE_MIN = 136;
2002
+ var XXH_SECRET_DEFAULT_SIZE = 192;
2003
+ var STRIPE_LEN = 64;
2004
+ var XXH_SECRET_CONSUME_RATE = 8;
2005
+ var ACC_NB = STRIPE_LEN / 8;
2006
+ var XXH3_MIDSIZE_MAX = 240;
2007
+ var XXH3_MIDSIZE_STARTOFFSET = 3;
2008
+ var XXH3_MIDSIZE_LASTOFFSET = 17;
2009
+ var XXH_SECRET_LASTACC_START = 7;
2010
+ var XXH_SECRET_MERGEACCS_START = 11;
2011
+ var RRMXMX = 0x9fb21c651e98df25n;
2012
+ var DEFAULT_SECRET = new Uint8Array([
2013
+ 184,
2014
+ 254,
2015
+ 108,
2016
+ 57,
2017
+ 35,
2018
+ 164,
2019
+ 75,
2020
+ 190,
2021
+ 124,
2022
+ 1,
2023
+ 129,
2024
+ 44,
2025
+ 247,
2026
+ 33,
2027
+ 173,
2028
+ 28,
2029
+ 222,
2030
+ 212,
2031
+ 109,
2032
+ 233,
2033
+ 131,
2034
+ 144,
2035
+ 151,
2036
+ 219,
2037
+ 114,
2038
+ 64,
2039
+ 164,
2040
+ 164,
2041
+ 183,
2042
+ 179,
2043
+ 103,
2044
+ 31,
2045
+ 203,
2046
+ 121,
2047
+ 230,
2048
+ 78,
2049
+ 204,
2050
+ 192,
2051
+ 229,
2052
+ 120,
2053
+ 130,
2054
+ 90,
2055
+ 208,
2056
+ 125,
2057
+ 204,
2058
+ 255,
2059
+ 114,
2060
+ 33,
2061
+ 184,
2062
+ 8,
2063
+ 70,
2064
+ 116,
2065
+ 247,
2066
+ 67,
2067
+ 36,
2068
+ 142,
2069
+ 224,
2070
+ 53,
2071
+ 144,
2072
+ 230,
2073
+ 129,
2074
+ 58,
2075
+ 38,
2076
+ 76,
2077
+ 60,
2078
+ 40,
2079
+ 82,
2080
+ 187,
2081
+ 145,
2082
+ 195,
2083
+ 0,
2084
+ 203,
2085
+ 136,
2086
+ 208,
2087
+ 101,
2088
+ 139,
2089
+ 27,
2090
+ 83,
2091
+ 46,
2092
+ 163,
2093
+ 113,
2094
+ 100,
2095
+ 72,
2096
+ 151,
2097
+ 162,
2098
+ 13,
2099
+ 249,
2100
+ 78,
2101
+ 56,
2102
+ 25,
2103
+ 239,
2104
+ 70,
2105
+ 169,
2106
+ 222,
2107
+ 172,
2108
+ 216,
2109
+ 168,
2110
+ 250,
2111
+ 118,
2112
+ 63,
2113
+ 227,
2114
+ 156,
2115
+ 52,
2116
+ 63,
2117
+ 249,
2118
+ 220,
2119
+ 187,
2120
+ 199,
2121
+ 199,
2122
+ 11,
2123
+ 79,
2124
+ 29,
2125
+ 138,
2126
+ 81,
2127
+ 224,
2128
+ 75,
2129
+ 205,
2130
+ 180,
2131
+ 89,
2132
+ 49,
2133
+ 200,
2134
+ 159,
2135
+ 126,
2136
+ 201,
2137
+ 217,
2138
+ 120,
2139
+ 115,
2140
+ 100,
2141
+ 234,
2142
+ 197,
2143
+ 172,
2144
+ 131,
2145
+ 52,
2146
+ 211,
2147
+ 235,
2148
+ 195,
2149
+ 197,
2150
+ 129,
2151
+ 160,
2152
+ 255,
2153
+ 250,
2154
+ 19,
2155
+ 99,
2156
+ 235,
2157
+ 23,
2158
+ 13,
2159
+ 221,
2160
+ 81,
2161
+ 183,
2162
+ 240,
2163
+ 218,
2164
+ 73,
2165
+ 211,
2166
+ 22,
2167
+ 85,
2168
+ 38,
2169
+ 41,
2170
+ 212,
2171
+ 104,
2172
+ 158,
2173
+ 43,
2174
+ 22,
2175
+ 190,
2176
+ 88,
2177
+ 125,
2178
+ 71,
2179
+ 161,
2180
+ 252,
2181
+ 143,
2182
+ 248,
2183
+ 184,
2184
+ 209,
2185
+ 122,
2186
+ 208,
2187
+ 49,
2188
+ 206,
2189
+ 69,
2190
+ 203,
2191
+ 58,
2192
+ 143,
2193
+ 149,
2194
+ 22,
2195
+ 4,
2196
+ 40,
2197
+ 175,
2198
+ 215,
2199
+ 251,
2200
+ 202,
2201
+ 187,
2202
+ 75,
2203
+ 64,
2204
+ 126
2205
+ ]);
2206
+ function u64(x) {
2207
+ return x & U64_MASK;
2208
+ }
2209
+ function read32LE(buf, off) {
2210
+ return (buf[off] | buf[off + 1] << 8 | buf[off + 2] << 16 | buf[off + 3] << 24) >>> 0;
2211
+ }
2212
+ function read64LE(buf, off) {
2213
+ return BigInt(buf[off]) | BigInt(buf[off + 1]) << 8n | BigInt(buf[off + 2]) << 16n | BigInt(buf[off + 3]) << 24n | BigInt(buf[off + 4]) << 32n | BigInt(buf[off + 5]) << 40n | BigInt(buf[off + 6]) << 48n | BigInt(buf[off + 7]) << 56n;
2214
+ }
2215
+ function write64LE(buf, off, value) {
2216
+ let x = u64(value);
2217
+ for (let i = 0; i < 8; i++) {
2218
+ buf[off + i] = Number(x & 0xffn);
2219
+ x >>= 8n;
2220
+ }
2221
+ }
2222
+ function swap32(x) {
2223
+ return (x << 24 & 4278190080 | x << 8 & 16711680 | x >>> 8 & 65280 | x >>> 24 & 255) >>> 0;
2224
+ }
2225
+ function swap64(x) {
2226
+ x = u64(x);
2227
+ return x << 56n & 0xff00000000000000n | x << 40n & 0x00ff000000000000n | x << 24n & 0x0000ff0000000000n | x << 8n & 0x000000ff00000000n | x >> 8n & 0x00000000ff000000n | x >> 24n & 0x0000000000ff0000n | x >> 40n & 0x000000000000ff00n | x >> 56n & 0x00000000000000ffn;
2228
+ }
2229
+ function rotl64(x, amount) {
2230
+ const r = BigInt(amount & 63);
2231
+ return u64(x << r | x >> 64n - r);
2232
+ }
2233
+ function mul128Fold64(lhs, rhs) {
2234
+ const product = u64(lhs) * u64(rhs);
2235
+ return u64(product) ^ u64(product >> 64n);
2236
+ }
2237
+ function xxh64Avalanche(hash) {
2238
+ let h = u64(hash);
2239
+ h ^= h >> 33n;
2240
+ h = u64(h * PRIME64_2);
2241
+ h ^= h >> 29n;
2242
+ h = u64(h * PRIME64_3);
2243
+ h ^= h >> 32n;
2244
+ return u64(h);
2245
+ }
2246
+ function xxh3Avalanche(hash) {
2247
+ let h = u64(hash);
2248
+ h ^= h >> 37n;
2249
+ h = u64(h * 0x165667919e3779f9n);
2250
+ h ^= h >> 32n;
2251
+ return u64(h);
2252
+ }
2253
+ function len0(secret, seed) {
2254
+ const acc = u64(seed ^ read64LE(secret, 56) ^ read64LE(secret, 64));
2255
+ return xxh64Avalanche(acc);
2256
+ }
2257
+ function len1to3(input, length, secret, seed) {
2258
+ const byte1 = input[0];
2259
+ const byte2 = length > 1 ? input[1] : input[0];
2260
+ const byte3 = input[length - 1];
2261
+ const combined = (byte1 << 16 | byte2 << 24 | byte3 | length << 8) >>> 0;
2262
+ const bitflip = u64(
2263
+ BigInt((read32LE(secret, 0) ^ read32LE(secret, 4)) >>> 0) + seed
2264
+ );
2265
+ const value64 = u64(BigInt(combined) ^ bitflip);
2266
+ return xxh64Avalanche(value64);
2267
+ }
2268
+ function len4to8(input, length, secret, seed) {
2269
+ const inputHi = read32LE(input, 0);
2270
+ const inputLo = read32LE(input, length - 4);
2271
+ const input64 = BigInt(inputLo) | BigInt(inputHi) << 32n;
2272
+ let acc = read64LE(secret, 8) ^ read64LE(secret, 16);
2273
+ seed ^= BigInt(swap32(Number(seed & U32_MASK))) << 32n;
2274
+ acc = u64(acc - seed);
2275
+ acc ^= input64;
2276
+ acc ^= rotl64(acc, 49) ^ rotl64(acc, 24);
2277
+ acc = u64(acc * RRMXMX);
2278
+ acc ^= (acc >> 35n) + BigInt(length);
2279
+ acc = u64(acc * RRMXMX);
2280
+ acc ^= acc >> 28n;
2281
+ return u64(acc);
2282
+ }
2283
+ function len9to16(input, length, secret, seed) {
2284
+ let inputLo = read64LE(secret, 24) ^ read64LE(secret, 32);
2285
+ let inputHi = read64LE(secret, 40) ^ read64LE(secret, 48);
2286
+ let acc = BigInt(length);
2287
+ inputLo = u64(inputLo + seed);
2288
+ inputHi = u64(inputHi - seed);
2289
+ inputLo ^= read64LE(input, 0);
2290
+ inputHi ^= read64LE(input, length - 8);
2291
+ acc = u64(acc + swap64(inputLo));
2292
+ acc = u64(acc + inputHi);
2293
+ acc = u64(acc + mul128Fold64(inputLo, inputHi));
2294
+ return xxh3Avalanche(acc);
2295
+ }
2296
+ function mix16B(input, inputOff, secret, secretOff, seed) {
2297
+ let lhs = seed;
2298
+ let rhs = u64(0n - seed);
2299
+ lhs = u64(lhs + read64LE(secret, secretOff));
2300
+ rhs = u64(rhs + read64LE(secret, secretOff + 8));
2301
+ lhs ^= read64LE(input, inputOff);
2302
+ rhs ^= read64LE(input, inputOff + 8);
2303
+ return mul128Fold64(lhs, rhs);
2304
+ }
2305
+ function len17to128(input, length, secret, seed) {
2306
+ let i = Math.floor((length - 1) / 32);
2307
+ let acc = u64(BigInt(length) * PRIME64_1);
2308
+ for (; i >= 0; i--) {
2309
+ acc = u64(acc + mix16B(input, 16 * i, secret, 32 * i, seed));
2310
+ acc = u64(
2311
+ acc + mix16B(
2312
+ input,
2313
+ length - 16 * (i + 1),
2314
+ secret,
2315
+ 32 * i + 16,
2316
+ seed
2317
+ )
2318
+ );
2319
+ }
2320
+ return xxh3Avalanche(acc);
2321
+ }
2322
+ function len129to240(input, length, secret, seed) {
2323
+ let acc = u64(BigInt(length) * PRIME64_1);
2324
+ const nbRounds = Math.floor(length / 16);
2325
+ for (let i = 0; i < 8; i++) {
2326
+ acc = u64(acc + mix16B(input, 16 * i, secret, 16 * i, seed));
2327
+ }
2328
+ acc = xxh3Avalanche(acc);
2329
+ for (let i = 8; i < nbRounds; i++) {
2330
+ acc = u64(
2331
+ acc + mix16B(
2332
+ input,
2333
+ 16 * i,
2334
+ secret,
2335
+ 16 * (i - 8) + XXH3_MIDSIZE_STARTOFFSET,
2336
+ seed
2337
+ )
2338
+ );
2339
+ }
2340
+ acc = u64(
2341
+ acc + mix16B(
2342
+ input,
2343
+ length - 16,
2344
+ secret,
2345
+ XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET,
2346
+ seed
2347
+ )
2348
+ );
2349
+ return xxh3Avalanche(acc);
2350
+ }
2351
+ function hashShort(input, length, secret, seed) {
2352
+ if (length <= 16) {
2353
+ if (length > 8) return len9to16(input, length, secret, seed);
2354
+ if (length >= 4) return len4to8(input, length, secret, seed);
2355
+ if (length !== 0) return len1to3(input, length, secret, seed);
2356
+ return len0(secret, seed);
2357
+ }
2358
+ if (length <= 128) return len17to128(input, length, secret, seed);
2359
+ return len129to240(input, length, secret, seed);
2360
+ }
2361
+ function accumulate512(acc, input, inputOff, secret, secretOff) {
2362
+ for (let i = 0; i < ACC_NB; i++) {
2363
+ const dataVal = read64LE(input, inputOff + 8 * i);
2364
+ const dataKey = dataVal ^ read64LE(secret, secretOff + 8 * i);
2365
+ acc[i ^ 1] = u64(acc[i ^ 1] + dataVal);
2366
+ const low32 = dataKey & U32_MASK;
2367
+ const high32 = dataKey >> 32n;
2368
+ acc[i] = u64(acc[i] + low32 * high32);
2369
+ }
2370
+ }
2371
+ function scrambleAcc(acc, secret, secretOff) {
2372
+ for (let i = 0; i < ACC_NB; i++) {
2373
+ let x = acc[i];
2374
+ x ^= x >> 47n;
2375
+ x ^= read64LE(secret, secretOff + 8 * i);
2376
+ x = u64(x * BigInt(PRIME32_1));
2377
+ acc[i] = x;
2378
+ }
2379
+ }
2380
+ function accumulateLoop(acc, input, inputOff, secret, secretOff, nbStripes) {
2381
+ for (let n = 0; n < nbStripes; n++) {
2382
+ accumulate512(
2383
+ acc,
2384
+ input,
2385
+ inputOff + n * STRIPE_LEN,
2386
+ secret,
2387
+ secretOff + 8 * n
2388
+ );
2389
+ }
2390
+ }
2391
+ function mix2Accs(acc, accOff, secret, secretOff) {
2392
+ return mul128Fold64(
2393
+ acc[accOff] ^ read64LE(secret, secretOff),
2394
+ acc[accOff + 1] ^ read64LE(secret, secretOff + 8)
2395
+ );
2396
+ }
2397
+ function mergeAccs(acc, key, keyOff, start) {
2398
+ let result = start;
2399
+ for (let i = 0; i < 4; i++) {
2400
+ result = u64(result + mix2Accs(acc, 2 * i, key, keyOff + 16 * i));
2401
+ }
2402
+ return xxh3Avalanche(result);
2403
+ }
2404
+ function hashLong(input, length, secret, secretSize) {
2405
+ const nbRounds = Math.floor(
2406
+ (secretSize - STRIPE_LEN) / XXH_SECRET_CONSUME_RATE
2407
+ );
2408
+ const blockLen = STRIPE_LEN * nbRounds;
2409
+ const nbBlocks = Math.floor((length - 1) / blockLen);
2410
+ const nbStripes = Math.floor(
2411
+ (length - 1 - blockLen * nbBlocks) / STRIPE_LEN
2412
+ );
2413
+ const acc = [
2414
+ BigInt(PRIME32_3),
2415
+ PRIME64_1,
2416
+ PRIME64_2,
2417
+ PRIME64_3,
2418
+ PRIME64_4,
2419
+ BigInt(PRIME32_2),
2420
+ PRIME64_5,
2421
+ BigInt(PRIME32_1)
2422
+ ];
2423
+ for (let n = 0; n < nbBlocks; n++) {
2424
+ accumulateLoop(acc, input, n * blockLen, secret, 0, nbRounds);
2425
+ scrambleAcc(acc, secret, secretSize - STRIPE_LEN);
2426
+ }
2427
+ accumulateLoop(acc, input, nbBlocks * blockLen, secret, 0, nbStripes);
2428
+ accumulate512(
2429
+ acc,
2430
+ input,
2431
+ length - STRIPE_LEN,
2432
+ secret,
2433
+ secretSize - STRIPE_LEN - XXH_SECRET_LASTACC_START
2434
+ );
2435
+ return mergeAccs(
2436
+ acc,
2437
+ secret,
2438
+ XXH_SECRET_MERGEACCS_START,
2439
+ u64(BigInt(length) * PRIME64_1)
2440
+ );
2441
+ }
2442
+ function hashLongWithSeed(input, length, seed) {
2443
+ const secret = new Uint8Array(XXH_SECRET_DEFAULT_SIZE);
2444
+ for (let i = 0; i < XXH_SECRET_DEFAULT_SIZE / 16; i++) {
2445
+ const off = 16 * i;
2446
+ write64LE(secret, off, u64(read64LE(DEFAULT_SECRET, off) + seed));
2447
+ write64LE(
2448
+ secret,
2449
+ off + 8,
2450
+ u64(read64LE(DEFAULT_SECRET, off + 8) - seed)
2451
+ );
2452
+ }
2453
+ return hashLong(input, length, secret, secret.length);
2454
+ }
2455
+ var textEncoder2 = new TextEncoder();
2456
+ function xxh3_64(input, seed = 0n) {
2457
+ const len = input.length;
2458
+ if (len <= XXH3_MIDSIZE_MAX) {
2459
+ return hashShort(input, len, DEFAULT_SECRET, u64(seed));
2460
+ }
2461
+ return hashLongWithSeed(input, len, u64(seed));
2462
+ }
2463
+ function xxh3_64str(input, seed = 0n) {
2464
+ return xxh3_64(textEncoder2.encode(input), seed);
2465
+ }
2466
+ function computeMethodHash(methodName) {
2467
+ const utf8 = textEncoder2.encode(methodName);
2468
+ const prefixValue = Math.imul(67211, utf8.length) >>> 0;
2469
+ const prefixed = new Uint8Array(4 + utf8.length);
2470
+ prefixed[0] = prefixValue & 255;
2471
+ prefixed[1] = prefixValue >>> 8 & 255;
2472
+ prefixed[2] = prefixValue >>> 16 & 255;
2473
+ prefixed[3] = prefixValue >>> 24 & 255;
2474
+ prefixed.set(utf8, 4);
2475
+ const hash64 = xxh3_64(prefixed);
2476
+ return Number(hash64 & 0xffffffffn) | 0;
2477
+ }
2478
+
2479
+ // src/rpc-method-registry.ts
2480
+ var RpcMethodRegistry = class {
2481
+ _nameToHash = /* @__PURE__ */ new Map();
2482
+ _hashToName = /* @__PURE__ */ new Map();
2483
+ /** Register a method name and compute its hash. */
2484
+ register(methodName) {
2485
+ if (this._nameToHash.has(methodName)) return;
2486
+ const hash = computeMethodHash(methodName);
2487
+ const existing = this._hashToName.get(hash);
2488
+ if (existing !== void 0 && existing !== methodName) {
2489
+ console.warn(
2490
+ `[RpcMethodRegistry] hash collision: "${methodName}" and "${existing}" both hash to ${hash}`
2491
+ );
2492
+ return;
2493
+ }
2494
+ this._nameToHash.set(methodName, hash);
2495
+ this._hashToName.set(hash, methodName);
2496
+ }
2497
+ /** Register all wire method names from a service definition. */
2498
+ registerService(serviceName, methods) {
2499
+ for (const [, def] of methods) {
2500
+ const wireName = `${serviceName}.${def.name}:${def.wireArgCount}`;
2501
+ this.register(wireName);
2502
+ }
2503
+ }
2504
+ /** Get the hash for a method name. Computes on demand if not registered. */
2505
+ getHash(methodName) {
2506
+ let hash = this._nameToHash.get(methodName);
2507
+ if (hash === void 0) {
2508
+ this.register(methodName);
2509
+ hash = this._nameToHash.get(methodName);
2510
+ }
2511
+ return hash;
2512
+ }
2513
+ /** Get the method name for a hash. Returns undefined if not registered. */
2514
+ getName(hash) {
2515
+ return this._hashToName.get(hash);
2516
+ }
2517
+ /** Get the hash for a method name, throwing if not registered. */
2518
+ requireHash(methodName) {
2519
+ return this.getHash(methodName);
2520
+ }
2521
+ };
2522
+
1208
2523
  // src/rpc-hub.ts
1209
2524
  var RpcHub = class {
1210
2525
  hubId;
@@ -1212,9 +2527,16 @@ var RpcHub = class {
1212
2527
  serviceHost;
1213
2528
  systemCallSender = new RpcSystemCallSender();
1214
2529
  systemCallHandler = new RpcSystemCallHandler();
2530
+ /** Method registry for compact format hash ↔ name resolution.
2531
+ * Created lazily, populated by addService/addClient. */
2532
+ registry = new RpcMethodRegistry();
1215
2533
  constructor(hubId) {
1216
2534
  this.hubId = hubId ?? crypto.randomUUID();
1217
2535
  this.serviceHost = new RpcServiceHost();
2536
+ this.systemCallSender.registry = this.registry;
2537
+ for (const methodName of Object.values(RpcSystemCalls)) {
2538
+ this.registry.register(methodName);
2539
+ }
1218
2540
  }
1219
2541
  addPeer(peer) {
1220
2542
  this.peers.set(peer.ref, peer);
@@ -1252,10 +2574,12 @@ var RpcHub = class {
1252
2574
  wrappedImpl[methodDef.name] = methodDef.callTypeId !== 0 ? this._wrapServerMethod(methodDef, fn, impl) : fn;
1253
2575
  }
1254
2576
  this.serviceHost.register(def, wrappedImpl);
2577
+ this.registry.registerService(def.name, def.methods);
1255
2578
  }
1256
2579
  /** Create a typed client proxy for a service on a remote peer. */
1257
2580
  addClient(peer, defOrContract) {
1258
2581
  const def = this._resolveServiceDef(defOrContract);
2582
+ this.registry.registerService(def.name, def.methods);
1259
2583
  const overloads = /* @__PURE__ */ new Map();
1260
2584
  for (const methodDef of def.methods.values()) {
1261
2585
  let byArgCount = overloads.get(methodDef.name);
@@ -1263,7 +2587,10 @@ var RpcHub = class {
1263
2587
  byArgCount = /* @__PURE__ */ new Map();
1264
2588
  overloads.set(methodDef.name, byArgCount);
1265
2589
  }
1266
- byArgCount.set(methodDef.argCount, this._createClientMethod(peer, methodDef));
2590
+ byArgCount.set(
2591
+ methodDef.argCount,
2592
+ this._createClientMethod(peer, methodDef)
2593
+ );
1267
2594
  }
1268
2595
  const methods = /* @__PURE__ */ new Map();
1269
2596
  for (const [name, byArgCount] of overloads) {
@@ -1272,7 +2599,10 @@ var RpcHub = class {
1272
2599
  } else {
1273
2600
  methods.set(name, (...args) => {
1274
2601
  const fn = byArgCount.get(args.length);
1275
- if (!fn) throw new Error(`No overload of ${name} accepts ${args.length} arguments`);
2602
+ if (!fn)
2603
+ throw new Error(
2604
+ `No overload of ${name} accepts ${args.length} arguments`
2605
+ );
1276
2606
  return fn(...args);
1277
2607
  });
1278
2608
  }
@@ -1288,7 +2618,8 @@ var RpcHub = class {
1288
2618
  /** Build an RpcServiceDef from decorator metadata on a contract class. Override in FusionHub for compute support. */
1289
2619
  _buildServiceDef(cls) {
1290
2620
  const svcMeta = getServiceMeta(cls);
1291
- if (svcMeta === void 0) throw new Error("Contract class missing @rpcService metadata");
2621
+ if (svcMeta === void 0)
2622
+ throw new Error("Contract class missing @rpcService metadata");
1292
2623
  const methodsMeta = getMethodsMeta(cls) ?? {};
1293
2624
  const methods = /* @__PURE__ */ new Map();
1294
2625
  for (const [name, meta] of Object.entries(methodsMeta)) {
@@ -1331,7 +2662,10 @@ var RpcHub = class {
1331
2662
  const outboundCall = peer.call(wireName, callArgs);
1332
2663
  const result = await outboundCall.result.promise;
1333
2664
  const ref = parseStreamRef(result);
1334
- if (!ref) throw new Error(`Expected stream reference, got: ${JSON.stringify(result)}`);
2665
+ if (!ref)
2666
+ throw new Error(
2667
+ `Expected stream reference, got: ${JSON.stringify(result)}`
2668
+ );
1335
2669
  const stream = new RpcStream(ref, peer);
1336
2670
  peer.remoteObjects.register(stream);
1337
2671
  return stream;
@@ -1370,7 +2704,10 @@ function createRpcClient(peer, serviceDef) {
1370
2704
  }
1371
2705
  methods.set(name, (...args) => {
1372
2706
  const fn = fns.get(args.length);
1373
- if (!fn) throw new Error(`No overload of ${name} accepts ${args.length} arguments`);
2707
+ if (!fn)
2708
+ throw new Error(
2709
+ `No overload of ${name} accepts ${args.length} arguments`
2710
+ );
1374
2711
  return fn(...args);
1375
2712
  });
1376
2713
  }
@@ -1415,12 +2752,10 @@ function getStateDescription(state, useLastError = false) {
1415
2752
  case 1 /* JustDisconnected */:
1416
2753
  return "Just disconnected, reconnecting...";
1417
2754
  case 2 /* Disconnected */:
1418
- if (state.reconnectsIn > 0)
1419
- break;
2755
+ if (state.reconnectsIn > 0) break;
1420
2756
  return "Reconnecting...";
1421
2757
  }
1422
- if (state.lastError == null || !useLastError)
1423
- return "Disconnected.";
2758
+ if (state.lastError == null || !useLastError) return "Disconnected.";
1424
2759
  let message = state.lastError.message.trim();
1425
2760
  if (!(message.endsWith(".") || message.endsWith("!") || message.endsWith("?")))
1426
2761
  message += ".";
@@ -1451,7 +2786,9 @@ var RpcPeerStateMonitor = class {
1451
2786
  _onDisconnected = (ev) => {
1452
2787
  const wasConnected = this._rawConnected;
1453
2788
  this._rawConnected = false;
1454
- this._lastError = new Error(`Connection closed: ${ev.reason} (code ${ev.code})`);
2789
+ this._lastError = new Error(
2790
+ `Connection closed: ${ev.reason} (code ${ev.code})`
2791
+ );
1455
2792
  if (wasConnected) {
1456
2793
  this._disconnectedAt = Date.now();
1457
2794
  }
@@ -1468,16 +2805,26 @@ var RpcPeerStateMonitor = class {
1468
2805
  this._rawConnected = peer.isConnected;
1469
2806
  if (this._rawConnected) {
1470
2807
  this._connectedAt = Date.now();
1471
- this._state = { kind: 3 /* JustConnected */, reconnectsIn: 0 };
2808
+ this._state = {
2809
+ kind: 3 /* JustConnected */,
2810
+ reconnectsIn: 0
2811
+ };
1472
2812
  } else {
1473
2813
  this._disconnectedAt = Date.now();
1474
- this._state = { kind: 1 /* JustDisconnected */, reconnectsIn: 0 };
2814
+ this._state = {
2815
+ kind: 1 /* JustDisconnected */,
2816
+ reconnectsIn: 0
2817
+ };
1475
2818
  }
1476
2819
  peer.connected.add(this._onConnected);
1477
2820
  peer.disconnected.add(this._onDisconnected);
1478
2821
  peer.reconnectsAtChanged.add(this._onReconnectsAtChanged);
1479
- peer.reconnectDelayer.cancelDelaysChanged.add(this._onCancelDelaysChanged);
1480
- this._scheduleRecompute(this._rawConnected ? JustConnectedPeriodMs : JustDisconnectedPeriodMs);
2822
+ peer.reconnectDelayer.cancelDelaysChanged.add(
2823
+ this._onCancelDelaysChanged
2824
+ );
2825
+ this._scheduleRecompute(
2826
+ this._rawConnected ? JustConnectedPeriodMs : JustDisconnectedPeriodMs
2827
+ );
1481
2828
  }
1482
2829
  get state() {
1483
2830
  return this._state;
@@ -1490,7 +2837,9 @@ var RpcPeerStateMonitor = class {
1490
2837
  this.peer.connected.remove(this._onConnected);
1491
2838
  this.peer.disconnected.remove(this._onDisconnected);
1492
2839
  this.peer.reconnectsAtChanged.remove(this._onReconnectsAtChanged);
1493
- this.peer.reconnectDelayer.cancelDelaysChanged.remove(this._onCancelDelaysChanged);
2840
+ this.peer.reconnectDelayer.cancelDelaysChanged.remove(
2841
+ this._onCancelDelaysChanged
2842
+ );
1494
2843
  }
1495
2844
  _recompute() {
1496
2845
  if (this._timer !== void 0) {
@@ -1502,24 +2851,46 @@ var RpcPeerStateMonitor = class {
1502
2851
  if (this._rawConnected) {
1503
2852
  const connectedFor = now - this._connectedAt;
1504
2853
  if (connectedFor < JustConnectedPeriodMs) {
1505
- newState = { kind: 3 /* JustConnected */, reconnectsIn: 0 };
2854
+ newState = {
2855
+ kind: 3 /* JustConnected */,
2856
+ reconnectsIn: 0
2857
+ };
1506
2858
  this._scheduleRecompute(JustConnectedPeriodMs - connectedFor);
1507
2859
  } else {
1508
- newState = { kind: 0 /* Connected */, reconnectsIn: 0 };
2860
+ newState = {
2861
+ kind: 0 /* Connected */,
2862
+ reconnectsIn: 0
2863
+ };
1509
2864
  }
1510
2865
  } else {
1511
2866
  const disconnectedFor = now - this._disconnectedAt;
1512
2867
  if (disconnectedFor < JustDisconnectedPeriodMs) {
1513
- newState = { kind: 1 /* JustDisconnected */, lastError: this._lastError, reconnectsIn: 0 };
1514
- this._scheduleRecompute(JustDisconnectedPeriodMs - disconnectedFor);
2868
+ newState = {
2869
+ kind: 1 /* JustDisconnected */,
2870
+ lastError: this._lastError,
2871
+ reconnectsIn: 0
2872
+ };
2873
+ this._scheduleRecompute(
2874
+ JustDisconnectedPeriodMs - disconnectedFor
2875
+ );
1515
2876
  } else {
1516
2877
  const reconnectsAt = this.peer.reconnectsAt;
1517
2878
  const reconnectsIn = reconnectsAt > 0 ? Math.max(0, reconnectsAt - now) : 0;
1518
2879
  if (reconnectsIn < MinReconnectsInMs) {
1519
- newState = { kind: 2 /* Disconnected */, lastError: this._lastError, reconnectsIn: 0 };
2880
+ newState = {
2881
+ kind: 2 /* Disconnected */,
2882
+ lastError: this._lastError,
2883
+ reconnectsIn: 0
2884
+ };
1520
2885
  } else {
1521
- newState = { kind: 2 /* Disconnected */, lastError: this._lastError, reconnectsIn };
1522
- this._scheduleRecompute(Math.min(1e3, reconnectsIn - MinReconnectsInMs));
2886
+ newState = {
2887
+ kind: 2 /* Disconnected */,
2888
+ lastError: this._lastError,
2889
+ reconnectsIn
2890
+ };
2891
+ this._scheduleRecompute(
2892
+ Math.min(1e3, reconnectsIn - MinReconnectsInMs)
2893
+ );
1523
2894
  }
1524
2895
  }
1525
2896
  }
@@ -1534,10 +2905,13 @@ var RpcPeerStateMonitor = class {
1534
2905
  }
1535
2906
  _scheduleRecompute(delayMs) {
1536
2907
  if (this._timer !== void 0) clearTimeout(this._timer);
1537
- this._timer = setTimeout(() => {
1538
- this._timer = void 0;
1539
- this._recompute();
1540
- }, Math.max(0, delayMs));
2908
+ this._timer = setTimeout(
2909
+ () => {
2910
+ this._timer = void 0;
2911
+ this._recompute();
2912
+ },
2913
+ Math.max(0, delayMs)
2914
+ );
1541
2915
  }
1542
2916
  };
1543
2917
  export {
@@ -1552,7 +2926,11 @@ export {
1552
2926
  RpcHub,
1553
2927
  RpcInboundCall,
1554
2928
  RpcInboundCallTracker,
2929
+ RpcJsonSerializationFormat,
1555
2930
  RpcMessageChannelConnection,
2931
+ RpcMessagePackCompactSerializationFormat,
2932
+ RpcMessagePackSerializationFormat,
2933
+ RpcMethodRegistry,
1556
2934
  RpcObjectKind,
1557
2935
  RpcOutboundCall,
1558
2936
  RpcOutboundCallTracker,
@@ -1561,6 +2939,7 @@ export {
1561
2939
  RpcPeerStateKind,
1562
2940
  RpcPeerStateMonitor,
1563
2941
  RpcRemoteObjectTracker,
2942
+ RpcSerializationFormat,
1564
2943
  RpcServerPeer,
1565
2944
  RpcServiceHost,
1566
2945
  RpcSharedObjectTracker,
@@ -1572,10 +2951,16 @@ export {
1572
2951
  RpcType,
1573
2952
  RpcWebSocketConnection,
1574
2953
  WebSocketState,
2954
+ computeMethodHash,
2955
+ createBinaryEncoder,
1575
2956
  createMessageChannelPair,
1576
2957
  createRpcClient,
2958
+ defaultBinaryDecoder,
2959
+ defaultBinaryEncoder,
1577
2960
  defaultConnectionUrlResolver,
1578
2961
  defineRpcService,
2962
+ deserializeBinaryMessage,
2963
+ deserializeCompactBinaryMessage,
1579
2964
  deserializeMessage,
1580
2965
  getMethodsMeta,
1581
2966
  getServiceMeta,
@@ -1586,9 +2971,16 @@ export {
1586
2971
  resolveStreamRefs,
1587
2972
  rpcMethod,
1588
2973
  rpcService,
2974
+ serializeBinaryFrame,
2975
+ serializeBinaryMessage,
2976
+ serializeCompactBinaryMessage,
1589
2977
  serializeFrame,
1590
2978
  serializeMessage,
2979
+ splitBinaryFrame,
2980
+ splitCompactBinaryFrame,
1591
2981
  splitFrame,
1592
- wireMethodName
2982
+ wireMethodName,
2983
+ xxh3_64,
2984
+ xxh3_64str
1593
2985
  };
1594
2986
  //# sourceMappingURL=index.js.map