@dxos/messaging 0.5.9-main.07b4bad → 0.5.9-main.1c1903d

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.
Files changed (44) hide show
  1. package/dist/lib/browser/index.mjs +812 -559
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +778 -545
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/types/src/messenger-monitor.d.ts +8 -0
  8. package/dist/types/src/messenger-monitor.d.ts.map +1 -0
  9. package/dist/types/src/messenger.d.ts +1 -0
  10. package/dist/types/src/messenger.d.ts.map +1 -1
  11. package/dist/types/src/signal-client/signal-client-monitor.d.ts +30 -0
  12. package/dist/types/src/signal-client/signal-client-monitor.d.ts.map +1 -0
  13. package/dist/types/src/signal-client/signal-client.d.ts +25 -50
  14. package/dist/types/src/signal-client/signal-client.d.ts.map +1 -1
  15. package/dist/types/src/signal-client/signal-local-state.d.ts +46 -0
  16. package/dist/types/src/signal-client/signal-local-state.d.ts.map +1 -0
  17. package/dist/types/src/signal-client/signal-rpc-client-monitor.d.ts +6 -0
  18. package/dist/types/src/signal-client/signal-rpc-client-monitor.d.ts.map +1 -0
  19. package/dist/types/src/signal-client/signal-rpc-client.d.ts +4 -2
  20. package/dist/types/src/signal-client/signal-rpc-client.d.ts.map +1 -1
  21. package/dist/types/src/signal-manager/memory-signal-manager.d.ts +0 -2
  22. package/dist/types/src/signal-manager/memory-signal-manager.d.ts.map +1 -1
  23. package/dist/types/src/signal-manager/signal-manager.d.ts +0 -2
  24. package/dist/types/src/signal-manager/signal-manager.d.ts.map +1 -1
  25. package/dist/types/src/signal-manager/websocket-signal-manager-monitor.d.ts +8 -0
  26. package/dist/types/src/signal-manager/websocket-signal-manager-monitor.d.ts.map +1 -0
  27. package/dist/types/src/signal-manager/websocket-signal-manager.d.ts +7 -3
  28. package/dist/types/src/signal-manager/websocket-signal-manager.d.ts.map +1 -1
  29. package/dist/types/src/signal-methods.d.ts +6 -4
  30. package/dist/types/src/signal-methods.d.ts.map +1 -1
  31. package/package.json +13 -12
  32. package/src/messenger-monitor.ts +20 -0
  33. package/src/messenger.ts +16 -5
  34. package/src/signal-client/signal-client-monitor.ts +111 -0
  35. package/src/signal-client/signal-client.test.ts +111 -259
  36. package/src/signal-client/signal-client.ts +141 -252
  37. package/src/signal-client/signal-local-state.ts +156 -0
  38. package/src/signal-client/signal-rpc-client-monitor.ts +15 -0
  39. package/src/signal-client/signal-rpc-client.ts +38 -21
  40. package/src/signal-manager/memory-signal-manager.ts +0 -2
  41. package/src/signal-manager/signal-manager.ts +0 -2
  42. package/src/signal-manager/websocket-signal-manager-monitor.ts +20 -0
  43. package/src/signal-manager/websocket-signal-manager.ts +48 -26
  44. package/src/signal-methods.ts +6 -4
@@ -43,6 +43,7 @@ var import_keys = require("@dxos/keys");
43
43
  var import_log = require("@dxos/log");
44
44
  var import_protocols = require("@dxos/protocols");
45
45
  var import_util = require("@dxos/util");
46
+ var import_tracing = require("@dxos/tracing");
46
47
  var import_async2 = require("@dxos/async");
47
48
  var import_context2 = require("@dxos/context");
48
49
  var import_invariant2 = require("@dxos/invariant");
@@ -50,31 +51,52 @@ var import_keys2 = require("@dxos/keys");
50
51
  var import_log2 = require("@dxos/log");
51
52
  var import_protocols2 = require("@dxos/protocols");
52
53
  var import_signal = require("@dxos/protocols/proto/dxos/mesh/signal");
53
- var import_util2 = require("@dxos/util");
54
- var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
54
+ var import_tracing2 = require("@dxos/tracing");
55
55
  var import_async3 = require("@dxos/async");
56
56
  var import_context3 = require("@dxos/context");
57
- var import_invariant3 = require("@dxos/invariant");
58
57
  var import_keys3 = require("@dxos/keys");
59
58
  var import_log3 = require("@dxos/log");
60
- var import_protocols3 = require("@dxos/protocols");
61
- var import_rpc = require("@dxos/rpc");
59
+ var import_util2 = require("@dxos/util");
60
+ var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
62
61
  var import_async4 = require("@dxos/async");
63
62
  var import_context4 = require("@dxos/context");
64
- var import_invariant4 = require("@dxos/invariant");
63
+ var import_invariant3 = require("@dxos/invariant");
65
64
  var import_keys4 = require("@dxos/keys");
66
65
  var import_log4 = require("@dxos/log");
67
- var import_protocols4 = require("@dxos/protocols");
68
- var import_util3 = require("@dxos/util");
66
+ var import_protocols3 = require("@dxos/protocols");
67
+ var import_rpc = require("@dxos/rpc");
68
+ var import_tracing3 = require("@dxos/tracing");
69
69
  var import_async5 = require("@dxos/async");
70
70
  var import_context5 = require("@dxos/context");
71
- var import_invariant5 = require("@dxos/invariant");
71
+ var import_invariant4 = require("@dxos/invariant");
72
72
  var import_keys5 = require("@dxos/keys");
73
73
  var import_log5 = require("@dxos/log");
74
+ var import_protocols4 = require("@dxos/protocols");
75
+ var import_util3 = require("@dxos/util");
76
+ var import_async6 = require("@dxos/async");
77
+ var import_context6 = require("@dxos/context");
78
+ var import_invariant5 = require("@dxos/invariant");
79
+ var import_keys6 = require("@dxos/keys");
80
+ var import_log6 = require("@dxos/log");
74
81
  var import_protocols5 = require("@dxos/protocols");
82
+ var import_util4 = require("@dxos/util");
83
+ var import_tracing4 = require("@dxos/tracing");
75
84
  var import_invariant6 = require("@dxos/invariant");
76
- var import_log6 = require("@dxos/log");
85
+ var import_log7 = require("@dxos/log");
77
86
  var import_services = require("@dxos/protocols/proto/dxos/client/services");
87
+ var MessengerMonitor = class {
88
+ recordMessageAckFailed() {
89
+ import_tracing.trace.metrics.increment("mesh.signal.messenger.failed-ack", 1);
90
+ }
91
+ recordReliableMessage(params) {
92
+ import_tracing.trace.metrics.increment("mesh.signal.messenger.reliable-send", 1, {
93
+ tags: {
94
+ success: params.sent,
95
+ attempts: params.sendAttempts
96
+ }
97
+ });
98
+ }
99
+ };
78
100
  var MESSAGE_TIMEOUT = 1e4;
79
101
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/messenger.ts";
80
102
  var ReliablePayload = import_protocols.schema.getCodecForType("dxos.mesh.messaging.ReliablePayload");
@@ -82,6 +104,7 @@ var Acknowledgement = import_protocols.schema.getCodecForType("dxos.mesh.messagi
82
104
  var RECEIVED_MESSAGES_GC_INTERVAL = 12e4;
83
105
  var Messenger = class {
84
106
  constructor({ signalManager, retryDelay = 300 }) {
107
+ this._monitor = new MessengerMonitor();
85
108
  this._listeners = new import_util.ComplexMap(({ peerId, payloadType }) => peerId.toHex() + payloadType);
86
109
  this._defaultListeners = new import_util.ComplexMap(import_keys.PublicKey.hash);
87
110
  this._onAckCallbacks = new import_util.ComplexMap(import_keys.PublicKey.hash);
@@ -101,14 +124,14 @@ var Messenger = class {
101
124
  id: traceId
102
125
  }), {
103
126
  F: __dxlog_file,
104
- L: 69,
127
+ L: 71,
105
128
  S: this,
106
129
  C: (f, a) => f(...a)
107
130
  });
108
131
  this._ctx = new import_context.Context({
109
132
  onError: (err) => import_log.log.catch(err, void 0, {
110
133
  F: __dxlog_file,
111
- L: 71,
134
+ L: 73,
112
135
  S: this,
113
136
  C: (f, a) => f(...a)
114
137
  })
@@ -118,7 +141,7 @@ var Messenger = class {
118
141
  from: message.author
119
142
  }, {
120
143
  F: __dxlog_file,
121
- L: 75,
144
+ L: 77,
122
145
  S: this,
123
146
  C: (f, a) => f(...a)
124
147
  });
@@ -132,7 +155,7 @@ var Messenger = class {
132
155
  id: traceId
133
156
  }), {
134
157
  F: __dxlog_file,
135
- L: 90,
158
+ L: 92,
136
159
  S: this,
137
160
  C: (f, a) => f(...a)
138
161
  });
@@ -147,7 +170,7 @@ var Messenger = class {
147
170
  async sendMessage({ author, recipient, payload }) {
148
171
  (0, import_invariant.invariant)(!this._closed, "Closed", {
149
172
  F: __dxlog_file,
150
- L: 102,
173
+ L: 104,
151
174
  S: this,
152
175
  A: [
153
176
  "!this._closed",
@@ -161,7 +184,7 @@ var Messenger = class {
161
184
  };
162
185
  (0, import_invariant.invariant)(!this._onAckCallbacks.has(reliablePayload.messageId), void 0, {
163
186
  F: __dxlog_file,
164
- L: 109,
187
+ L: 111,
165
188
  S: this,
166
189
  A: [
167
190
  "!this._onAckCallbacks.has(reliablePayload.messageId!)",
@@ -174,12 +197,13 @@ var Messenger = class {
174
197
  recipient
175
198
  }, {
176
199
  F: __dxlog_file,
177
- L: 110,
200
+ L: 112,
178
201
  S: this,
179
202
  C: (f, a) => f(...a)
180
203
  });
181
204
  let messageReceived;
182
205
  let timeoutHit;
206
+ let sendAttempts = 0;
183
207
  const promise = new Promise((resolve, reject) => {
184
208
  messageReceived = resolve;
185
209
  timeoutHit = reject;
@@ -189,10 +213,11 @@ var Messenger = class {
189
213
  messageId: reliablePayload.messageId
190
214
  }, {
191
215
  F: __dxlog_file,
192
- L: 124,
216
+ L: 127,
193
217
  S: this,
194
218
  C: (f, a) => f(...a)
195
219
  });
220
+ sendAttempts++;
196
221
  await this._encodeAndSend({
197
222
  author,
198
223
  recipient,
@@ -201,7 +226,7 @@ var Messenger = class {
201
226
  err
202
227
  }, {
203
228
  F: __dxlog_file,
204
- L: 126,
229
+ L: 130,
205
230
  S: this,
206
231
  C: (f, a) => f(...a)
207
232
  }));
@@ -211,18 +236,26 @@ var Messenger = class {
211
236
  messageId: reliablePayload.messageId
212
237
  }, {
213
238
  F: __dxlog_file,
214
- L: 135,
239
+ L: 139,
215
240
  S: this,
216
241
  C: (f, a) => f(...a)
217
242
  });
218
243
  this._onAckCallbacks.delete(reliablePayload.messageId);
219
244
  timeoutHit(new import_protocols.TimeoutError("signaling message not delivered", new import_async.TimeoutError(MESSAGE_TIMEOUT, "Message not delivered")));
220
245
  void messageContext.dispose();
246
+ this._monitor.recordReliableMessage({
247
+ sendAttempts,
248
+ sent: false
249
+ });
221
250
  }, MESSAGE_TIMEOUT);
222
251
  this._onAckCallbacks.set(reliablePayload.messageId, () => {
223
252
  messageReceived();
224
253
  this._onAckCallbacks.delete(reliablePayload.messageId);
225
254
  void messageContext.dispose();
255
+ this._monitor.recordReliableMessage({
256
+ sendAttempts,
257
+ sent: true
258
+ });
226
259
  });
227
260
  await this._encodeAndSend({
228
261
  author,
@@ -238,7 +271,7 @@ var Messenger = class {
238
271
  async listen({ peerId, payloadType, onMessage }) {
239
272
  (0, import_invariant.invariant)(!this._closed, "Closed", {
240
273
  F: __dxlog_file,
241
- L: 171,
274
+ L: 177,
242
275
  S: this,
243
276
  A: [
244
277
  "!this._closed",
@@ -302,7 +335,7 @@ var Messenger = class {
302
335
  async _handleReliablePayload({ author, recipient, payload }) {
303
336
  (0, import_invariant.invariant)(payload.type_url === "dxos.mesh.messaging.ReliablePayload", void 0, {
304
337
  F: __dxlog_file,
305
- L: 232,
338
+ L: 238,
306
339
  S: this,
307
340
  A: [
308
341
  "payload.type_url === 'dxos.mesh.messaging.ReliablePayload'",
@@ -316,15 +349,20 @@ var Messenger = class {
316
349
  messageId: reliablePayload.messageId
317
350
  }, {
318
351
  F: __dxlog_file,
319
- L: 235,
352
+ L: 241,
320
353
  S: this,
321
354
  C: (f, a) => f(...a)
322
355
  });
323
- await this._sendAcknowledgement({
324
- author,
325
- recipient,
326
- messageId: reliablePayload.messageId
327
- });
356
+ try {
357
+ await this._sendAcknowledgement({
358
+ author,
359
+ recipient,
360
+ messageId: reliablePayload.messageId
361
+ });
362
+ } catch (err) {
363
+ this._monitor.recordMessageAckFailed();
364
+ throw err;
365
+ }
328
366
  if (this._receivedMessages.has(reliablePayload.messageId)) {
329
367
  return;
330
368
  }
@@ -338,7 +376,7 @@ var Messenger = class {
338
376
  async _handleAcknowledgement({ payload }) {
339
377
  (0, import_invariant.invariant)(payload.type_url === "dxos.mesh.messaging.Acknowledgement", void 0, {
340
378
  F: __dxlog_file,
341
- L: 258,
379
+ L: 269,
342
380
  S: this,
343
381
  A: [
344
382
  "payload.type_url === 'dxos.mesh.messaging.Acknowledgement'",
@@ -354,7 +392,7 @@ var Messenger = class {
354
392
  to: author
355
393
  }, {
356
394
  F: __dxlog_file,
357
- L: 271,
395
+ L: 282,
358
396
  S: this,
359
397
  C: (f, a) => f(...a)
360
398
  });
@@ -405,26 +443,279 @@ var Messenger = class {
405
443
  elapsed
406
444
  }, {
407
445
  F: __dxlog_file,
408
- L: 319,
446
+ L: 330,
409
447
  S: this,
410
448
  C: (f, a) => f(...a)
411
449
  });
412
450
  }
413
451
  }
414
452
  };
415
- var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/signal-client/signal-rpc-client.ts";
453
+ var SignalClientMonitor = class {
454
+ constructor() {
455
+ this._performance = {
456
+ sentMessages: 0,
457
+ receivedMessages: 0,
458
+ reconnectCounter: 0,
459
+ joinCounter: 0,
460
+ leaveCounter: 0
461
+ };
462
+ this._connectionStarted = /* @__PURE__ */ new Date();
463
+ this._lastStateChange = /* @__PURE__ */ new Date();
464
+ }
465
+ getRecordedTimestamps() {
466
+ return {
467
+ connectionStarted: this._connectionStarted,
468
+ lastStateChange: this._lastStateChange
469
+ };
470
+ }
471
+ recordStateChangeTime() {
472
+ this._lastStateChange = /* @__PURE__ */ new Date();
473
+ }
474
+ recordConnectionStartTime() {
475
+ this._connectionStarted = /* @__PURE__ */ new Date();
476
+ }
477
+ recordReconnect(params) {
478
+ this._performance.reconnectCounter++;
479
+ import_tracing2.trace.metrics.increment("mesh.signal.signal-client.reconnect", 1, {
480
+ tags: {
481
+ success: params.success
482
+ }
483
+ });
484
+ }
485
+ recordJoin() {
486
+ this._performance.joinCounter++;
487
+ }
488
+ recordLeave() {
489
+ this._performance.leaveCounter++;
490
+ }
491
+ recordMessageReceived(message) {
492
+ this._performance.receivedMessages++;
493
+ import_tracing2.trace.metrics.increment("mesh.signal.signal-client.received-total", 1, {
494
+ tags: createIdentityTags(message)
495
+ });
496
+ import_tracing2.trace.metrics.distribution("mesh.signal.signal-client.bytes-in", getByteCount(message), {
497
+ tags: createIdentityTags(message)
498
+ });
499
+ }
500
+ async recordMessageSending(message, sendMessage) {
501
+ this._performance.sentMessages++;
502
+ const tags = createIdentityTags(message);
503
+ let success = true;
504
+ try {
505
+ const reqStart = Date.now();
506
+ await sendMessage();
507
+ const reqDuration = Date.now() - reqStart;
508
+ import_tracing2.trace.metrics.distribution("mesh.signal.signal-client.send-duration", reqDuration, {
509
+ tags
510
+ });
511
+ import_tracing2.trace.metrics.distribution("mesh.signal.signal-client.bytes-out", getByteCount(message), {
512
+ tags
513
+ });
514
+ } catch (err) {
515
+ success = false;
516
+ }
517
+ import_tracing2.trace.metrics.increment("mesh.signal.signal-client.sent-total", 1, {
518
+ tags: {
519
+ ...tags,
520
+ success
521
+ }
522
+ });
523
+ }
524
+ recordStreamCloseErrors(count) {
525
+ import_tracing2.trace.metrics.increment("mesh.signal.signal-client.stream-close-errors", count);
526
+ }
527
+ recordReconciliation(params) {
528
+ import_tracing2.trace.metrics.increment("mesh.signal.signal-client.reconciliation", 1, {
529
+ tags: {
530
+ success: params.success
531
+ }
532
+ });
533
+ }
534
+ };
535
+ var getByteCount = (message) => {
536
+ return message.author.asUint8Array().length + message.recipient.asUint8Array().length + message.payload.type_url.length + message.payload.value.length;
537
+ };
538
+ var createIdentityTags = (message) => {
539
+ return {
540
+ peer: message.author.toHex()
541
+ };
542
+ };
543
+ var __dxlog_file2 = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/signal-client/signal-local-state.ts";
544
+ var SignalLocalState = class {
545
+ constructor(_onMessage, _onSwarmEvent) {
546
+ this._onMessage = _onMessage;
547
+ this._onSwarmEvent = _onSwarmEvent;
548
+ this._swarmStreams = new import_util2.ComplexMap(({ topic, peerId }) => topic.toHex() + peerId.toHex());
549
+ this._joinedTopics = new import_util2.ComplexSet(({ topic, peerId }) => topic.toHex() + peerId.toHex());
550
+ this._subscribedMessages = new import_util2.ComplexSet(({ peerId }) => peerId.toHex());
551
+ this.messageStreams = new import_util2.ComplexMap((key) => key.toHex());
552
+ this.reconciled = new import_async3.Event();
553
+ }
554
+ async safeCloseStreams() {
555
+ const streams = [
556
+ ...this._swarmStreams.values()
557
+ ].concat([
558
+ ...this.messageStreams.values()
559
+ ]);
560
+ this._swarmStreams.clear();
561
+ this.messageStreams.clear();
562
+ const failureCount = (await (0, import_util2.safeAwaitAll)(streams, (s) => s.close())).length;
563
+ return {
564
+ failureCount
565
+ };
566
+ }
567
+ join({ topic, peerId }) {
568
+ this._joinedTopics.add({
569
+ topic,
570
+ peerId
571
+ });
572
+ }
573
+ leave({ topic, peerId }) {
574
+ void this._swarmStreams.get({
575
+ topic,
576
+ peerId
577
+ })?.close();
578
+ this._swarmStreams.delete({
579
+ topic,
580
+ peerId
581
+ });
582
+ this._joinedTopics.delete({
583
+ topic,
584
+ peerId
585
+ });
586
+ }
587
+ subscribeMessages(peerId) {
588
+ this._subscribedMessages.add({
589
+ peerId
590
+ });
591
+ }
592
+ unsubscribeMessages(peerId) {
593
+ (0, import_log3.log)("unsubscribing from messages", {
594
+ peerId
595
+ }, {
596
+ F: __dxlog_file2,
597
+ L: 76,
598
+ S: this,
599
+ C: (f, a) => f(...a)
600
+ });
601
+ this._subscribedMessages.delete({
602
+ peerId
603
+ });
604
+ void this.messageStreams.get(peerId)?.close();
605
+ this.messageStreams.delete(peerId);
606
+ }
607
+ async reconcile(ctx, client) {
608
+ await this._reconcileSwarmSubscriptions(ctx, client);
609
+ await this._reconcileMessageSubscriptions(ctx, client);
610
+ this.reconciled.emit();
611
+ }
612
+ async _reconcileSwarmSubscriptions(ctx, client) {
613
+ for (const { topic, peerId } of this._swarmStreams.keys()) {
614
+ if (this._joinedTopics.has({
615
+ topic,
616
+ peerId
617
+ })) {
618
+ continue;
619
+ }
620
+ void this._swarmStreams.get({
621
+ topic,
622
+ peerId
623
+ })?.close();
624
+ this._swarmStreams.delete({
625
+ topic,
626
+ peerId
627
+ });
628
+ }
629
+ for (const { topic, peerId } of this._joinedTopics.values()) {
630
+ if (this._swarmStreams.has({
631
+ topic,
632
+ peerId
633
+ })) {
634
+ continue;
635
+ }
636
+ const swarmStream = await (0, import_async3.asyncTimeout)((0, import_context3.cancelWithContext)(ctx, client.join({
637
+ topic,
638
+ peerId
639
+ })), 5e3);
640
+ swarmStream.subscribe(async (swarmEvent) => {
641
+ if (this._joinedTopics.has({
642
+ topic,
643
+ peerId
644
+ })) {
645
+ (0, import_log3.log)("swarm event", {
646
+ swarmEvent
647
+ }, {
648
+ F: __dxlog_file2,
649
+ L: 112,
650
+ S: this,
651
+ C: (f, a) => f(...a)
652
+ });
653
+ await this._onSwarmEvent({
654
+ topic,
655
+ swarmEvent
656
+ });
657
+ }
658
+ });
659
+ this._swarmStreams.set({
660
+ topic,
661
+ peerId
662
+ }, swarmStream);
663
+ }
664
+ }
665
+ async _reconcileMessageSubscriptions(ctx, client) {
666
+ for (const peerId of this.messageStreams.keys()) {
667
+ if (this._subscribedMessages.has({
668
+ peerId
669
+ })) {
670
+ continue;
671
+ }
672
+ void this.messageStreams.get(peerId)?.close();
673
+ this.messageStreams.delete(peerId);
674
+ }
675
+ for (const { peerId } of this._subscribedMessages.values()) {
676
+ if (this.messageStreams.has(peerId)) {
677
+ continue;
678
+ }
679
+ const messageStream = await (0, import_async3.asyncTimeout)((0, import_context3.cancelWithContext)(ctx, client.receiveMessages(peerId)), 5e3);
680
+ messageStream.subscribe(async (signalMessage) => {
681
+ if (this._subscribedMessages.has({
682
+ peerId
683
+ })) {
684
+ const message = {
685
+ author: import_keys3.PublicKey.from(signalMessage.author),
686
+ recipient: import_keys3.PublicKey.from(signalMessage.recipient),
687
+ payload: signalMessage.payload
688
+ };
689
+ await this._onMessage(message);
690
+ }
691
+ });
692
+ this.messageStreams.set(peerId, messageStream);
693
+ }
694
+ }
695
+ };
696
+ var SignalRpcClientMonitor = class {
697
+ recordClientCloseFailure(params) {
698
+ import_tracing3.trace.metrics.increment("mesh.signal.signal-rpc-client.close-failure", 1, {
699
+ tags: {
700
+ reason: params.failureReason
701
+ }
702
+ });
703
+ }
704
+ };
705
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/signal-client/signal-rpc-client.ts";
416
706
  var SIGNAL_KEEPALIVE_INTERVAL = 1e4;
417
707
  var SignalRPCClient = class {
418
708
  constructor({ url, callbacks = {} }) {
419
- this._connectTrigger = new import_async3.Trigger();
709
+ this._connectTrigger = new import_async4.Trigger();
420
710
  this._closed = false;
421
- this._closeComplete = new import_async3.Trigger();
422
- const traceId = import_keys3.PublicKey.random().toHex();
423
- import_log3.log.trace("dxos.mesh.signal-rpc-client.constructor", import_protocols3.trace.begin({
711
+ this._closeComplete = new import_async4.Trigger();
712
+ this._monitor = new SignalRpcClientMonitor();
713
+ const traceId = import_keys4.PublicKey.random().toHex();
714
+ import_log4.log.trace("dxos.mesh.signal-rpc-client.constructor", import_protocols3.trace.begin({
424
715
  id: traceId
425
716
  }), {
426
- F: __dxlog_file2,
427
- L: 55,
717
+ F: __dxlog_file3,
718
+ L: 60,
428
719
  S: this,
429
720
  C: (f, a) => f(...a)
430
721
  });
@@ -444,9 +735,9 @@ var SignalRPCClient = class {
444
735
  try {
445
736
  this._socket.send(msg);
446
737
  } catch (err) {
447
- import_log3.log.warn("send error", err, {
448
- F: __dxlog_file2,
449
- L: 74,
738
+ import_log4.log.warn("send error", err, {
739
+ F: __dxlog_file3,
740
+ L: 79,
450
741
  S: this,
451
742
  C: (f, a) => f(...a)
452
743
  });
@@ -469,26 +760,32 @@ var SignalRPCClient = class {
469
760
  this._socket.onopen = async () => {
470
761
  try {
471
762
  await this._rpc.open();
472
- (0, import_log3.log)(`RPC open ${this._url}`, void 0, {
473
- F: __dxlog_file2,
474
- L: 95,
763
+ if (this._closed) {
764
+ await this._safeCloseRpc();
765
+ return;
766
+ }
767
+ (0, import_log4.log)(`RPC open ${this._url}`, void 0, {
768
+ F: __dxlog_file3,
769
+ L: 104,
475
770
  S: this,
476
771
  C: (f, a) => f(...a)
477
772
  });
478
773
  this._callbacks.onConnected?.();
479
774
  this._connectTrigger.wake();
480
- this._keepaliveCtx = new import_context3.Context();
481
- (0, import_async3.scheduleTaskInterval)(this._keepaliveCtx, async () => {
775
+ this._keepaliveCtx = new import_context4.Context();
776
+ (0, import_async4.scheduleTaskInterval)(this._keepaliveCtx, async () => {
482
777
  this._socket?.send("__ping__");
483
778
  }, SIGNAL_KEEPALIVE_INTERVAL);
484
779
  } catch (err) {
485
780
  this._callbacks.onError?.(err);
781
+ this._socket.close();
782
+ this._closed = true;
486
783
  }
487
784
  };
488
785
  this._socket.onclose = async () => {
489
- (0, import_log3.log)(`Disconnected ${this._url}`, void 0, {
490
- F: __dxlog_file2,
491
- L: 116,
786
+ (0, import_log4.log)(`Disconnected ${this._url}`, void 0, {
787
+ F: __dxlog_file3,
788
+ L: 127,
492
789
  S: this,
493
790
  C: (f, a) => f(...a)
494
791
  });
@@ -498,89 +795,73 @@ var SignalRPCClient = class {
498
795
  };
499
796
  this._socket.onerror = async (event) => {
500
797
  if (this._closed) {
798
+ this._socket.close();
501
799
  return;
502
800
  }
503
- this._callbacks.onError?.(event.error ?? new Error(event.message));
504
- this._connectTrigger.reset();
505
- try {
506
- await this._rpc?.close();
507
- } catch (err) {
508
- import_log3.log.catch(err, void 0, {
509
- F: __dxlog_file2,
510
- L: 134,
511
- S: this,
512
- C: (f, a) => f(...a)
513
- });
514
- }
515
801
  this._closed = true;
516
- import_log3.log.warn(event.message ?? "Socket error", {
802
+ this._callbacks.onError?.(event.error ?? new Error(event.message));
803
+ await this._safeCloseRpc();
804
+ import_log4.log.warn(`Socket ${event.type ?? "unknown"} error`, {
805
+ message: event.message,
517
806
  url: this._url
518
807
  }, {
519
- F: __dxlog_file2,
520
- L: 138,
808
+ F: __dxlog_file3,
809
+ L: 143,
521
810
  S: this,
522
811
  C: (f, a) => f(...a)
523
812
  });
524
813
  };
525
- import_log3.log.trace("dxos.mesh.signal-rpc-client.constructor", import_protocols3.trace.end({
814
+ import_log4.log.trace("dxos.mesh.signal-rpc-client.constructor", import_protocols3.trace.end({
526
815
  id: traceId
527
816
  }), {
528
- F: __dxlog_file2,
529
- L: 141,
817
+ F: __dxlog_file3,
818
+ L: 146,
530
819
  S: this,
531
820
  C: (f, a) => f(...a)
532
821
  });
533
822
  }
534
823
  async close() {
535
- await this._keepaliveCtx?.dispose();
824
+ if (this._closed) {
825
+ return;
826
+ }
536
827
  this._closed = true;
828
+ await this._keepaliveCtx?.dispose();
537
829
  try {
538
- await this._rpc?.close();
539
- if (this._socket?.readyState === import_isomorphic_ws.default.OPEN || this._socket?.readyState === import_isomorphic_ws.default.CONNECTING) {
830
+ await this._safeCloseRpc();
831
+ if (this._socket.readyState === import_isomorphic_ws.default.OPEN || this._socket.readyState === import_isomorphic_ws.default.CONNECTING) {
540
832
  this._socket.close();
541
833
  }
542
834
  await this._closeComplete.wait({
543
835
  timeout: 1e3
544
836
  });
545
837
  } catch (err) {
546
- import_log3.log.warn("close error", err, {
547
- F: __dxlog_file2,
548
- L: 156,
549
- S: this,
550
- C: (f, a) => f(...a)
838
+ const failureReason = err instanceof import_async4.TimeoutError ? "timeout" : err?.constructor?.name ?? "unknown";
839
+ this._monitor.recordClientCloseFailure({
840
+ failureReason
551
841
  });
552
842
  }
553
843
  }
554
844
  async join({ topic, peerId }) {
555
- (0, import_log3.log)("join", {
845
+ (0, import_log4.log)("join", {
556
846
  topic,
557
847
  peerId,
558
848
  metadata: this._callbacks?.getMetadata?.()
559
849
  }, {
560
- F: __dxlog_file2,
561
- L: 161,
850
+ F: __dxlog_file3,
851
+ L: 172,
562
852
  S: this,
563
853
  C: (f, a) => f(...a)
564
854
  });
565
- await this._connectTrigger.wait();
566
855
  (0, import_invariant3.invariant)(!this._closed, "SignalRPCClient is closed", {
567
- F: __dxlog_file2,
568
- L: 163,
856
+ F: __dxlog_file3,
857
+ L: 173,
569
858
  S: this,
570
859
  A: [
571
860
  "!this._closed",
572
861
  "'SignalRPCClient is closed'"
573
862
  ]
574
863
  });
575
- (0, import_invariant3.invariant)(this._rpc, "Rpc is not initialized", {
576
- F: __dxlog_file2,
577
- L: 164,
578
- S: this,
579
- A: [
580
- "this._rpc",
581
- "'Rpc is not initialized'"
582
- ]
583
- });
864
+ await this._connectTrigger.wait();
584
865
  const swarmStream = this._rpc.rpc.Signal.join({
585
866
  swarm: topic.asUint8Array(),
586
867
  peer: peerId.asUint8Array(),
@@ -590,17 +871,17 @@ var SignalRPCClient = class {
590
871
  return swarmStream;
591
872
  }
592
873
  async receiveMessages(peerId) {
593
- (0, import_log3.log)("receiveMessages", {
874
+ (0, import_log4.log)("receiveMessages", {
594
875
  peerId
595
876
  }, {
596
- F: __dxlog_file2,
597
- L: 175,
877
+ F: __dxlog_file3,
878
+ L: 185,
598
879
  S: this,
599
880
  C: (f, a) => f(...a)
600
881
  });
601
882
  (0, import_invariant3.invariant)(!this._closed, "SignalRPCClient is closed", {
602
- F: __dxlog_file2,
603
- L: 176,
883
+ F: __dxlog_file3,
884
+ L: 186,
604
885
  S: this,
605
886
  A: [
606
887
  "!this._closed",
@@ -608,15 +889,6 @@ var SignalRPCClient = class {
608
889
  ]
609
890
  });
610
891
  await this._connectTrigger.wait();
611
- (0, import_invariant3.invariant)(this._rpc, "Rpc is not initialized", {
612
- F: __dxlog_file2,
613
- L: 178,
614
- S: this,
615
- A: [
616
- "this._rpc",
617
- "'Rpc is not initialized'"
618
- ]
619
- });
620
892
  const messageStream = this._rpc.rpc.Signal.receiveMessages({
621
893
  peer: peerId.asUint8Array()
622
894
  });
@@ -624,20 +896,20 @@ var SignalRPCClient = class {
624
896
  return messageStream;
625
897
  }
626
898
  async sendMessage({ author, recipient, payload }) {
627
- (0, import_log3.log)("sendMessage", {
899
+ (0, import_log4.log)("sendMessage", {
628
900
  author,
629
901
  recipient,
630
902
  payload,
631
903
  metadata: this._callbacks?.getMetadata?.()
632
904
  }, {
633
- F: __dxlog_file2,
634
- L: 187,
905
+ F: __dxlog_file3,
906
+ L: 196,
635
907
  S: this,
636
908
  C: (f, a) => f(...a)
637
909
  });
638
910
  (0, import_invariant3.invariant)(!this._closed, "SignalRPCClient is closed", {
639
- F: __dxlog_file2,
640
- L: 188,
911
+ F: __dxlog_file3,
912
+ L: 197,
641
913
  S: this,
642
914
  A: [
643
915
  "!this._closed",
@@ -645,15 +917,6 @@ var SignalRPCClient = class {
645
917
  ]
646
918
  });
647
919
  await this._connectTrigger.wait();
648
- (0, import_invariant3.invariant)(this._rpc, "Rpc is not initialized", {
649
- F: __dxlog_file2,
650
- L: 190,
651
- S: this,
652
- A: [
653
- "this._rpc",
654
- "'Rpc is not initialized'"
655
- ]
656
- });
657
920
  await this._rpc.rpc.Signal.sendMessage({
658
921
  author: author.asUint8Array(),
659
922
  recipient: recipient.asUint8Array(),
@@ -661,51 +924,57 @@ var SignalRPCClient = class {
661
924
  metadata: this._callbacks?.getMetadata?.()
662
925
  });
663
926
  }
927
+ async _safeCloseRpc() {
928
+ try {
929
+ this._connectTrigger.reset();
930
+ await this._rpc.close();
931
+ } catch (err) {
932
+ import_log4.log.catch(err, void 0, {
933
+ F: __dxlog_file3,
934
+ L: 212,
935
+ S: this,
936
+ C: (f, a) => f(...a)
937
+ });
938
+ }
939
+ }
664
940
  };
665
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/signal-client/signal-client.ts";
941
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/signal-client/signal-client.ts";
666
942
  var DEFAULT_RECONNECT_TIMEOUT = 100;
667
943
  var MAX_RECONNECT_TIMEOUT = 5e3;
668
944
  var ERROR_RECONCILE_DELAY = 1e3;
669
945
  var RECONCILE_INTERVAL = 5e3;
670
- var SignalClient = class {
946
+ var SignalClient = class extends import_context2.Resource {
671
947
  /**
672
948
  * @param _host Signal server websocket URL.
949
+ * @param onMessage called when a new message is received.
950
+ * @param onSwarmEvent called when a new swarm event is received.
951
+ * @param _getMetadata signal-message metadata provider, called for every message.
673
952
  */
674
- constructor(_host, _onMessage, _onSwarmEvent, _getMetadata) {
953
+ constructor(_host, onMessage, onSwarmEvent, _getMetadata) {
954
+ super();
675
955
  this._host = _host;
676
- this._onMessage = _onMessage;
677
- this._onSwarmEvent = _onSwarmEvent;
678
956
  this._getMetadata = _getMetadata;
957
+ this._monitor = new SignalClientMonitor();
679
958
  this._state = import_signal.SignalState.CLOSED;
680
- this._reconnectAfter = DEFAULT_RECONNECT_TIMEOUT;
681
- this._connectionStarted = /* @__PURE__ */ new Date();
682
- this._lastStateChange = /* @__PURE__ */ new Date();
959
+ this._lastReconciliationFailed = false;
683
960
  this._clientReady = new import_async2.Trigger();
684
- this.statusChanged = new import_async2.Event();
685
- this.commandTrace = new import_async2.Event();
686
- this._swarmStreams = new import_util2.ComplexMap(({ topic, peerId }) => topic.toHex() + peerId.toHex());
687
- this._joinedTopics = new import_util2.ComplexSet(({ topic, peerId }) => topic.toHex() + peerId.toHex());
688
- this._messageStreams = new import_util2.ComplexMap((key) => key.toHex());
689
- this._subscribedMessages = new import_util2.ComplexSet(({ peerId }) => peerId.toHex());
690
- this._reconciled = new import_async2.Event();
961
+ this._reconnectAfter = DEFAULT_RECONNECT_TIMEOUT;
691
962
  this._instanceId = import_keys2.PublicKey.random().toHex();
692
- this._performance = {
693
- sentMessages: 0,
694
- receivedMessages: 0,
695
- reconnectCounter: 0,
696
- joinCounter: 0,
697
- leaveCounter: 0
698
- };
963
+ this.statusChanged = new import_async2.Event();
699
964
  if (!this._host.startsWith("wss://") && !this._host.startsWith("ws://")) {
700
965
  throw new Error(`Signal server requires a websocket URL. Provided: ${this._host}`);
701
966
  }
967
+ this.localState = new SignalLocalState((message) => {
968
+ this._monitor.recordMessageReceived(message);
969
+ return onMessage(message);
970
+ }, onSwarmEvent);
702
971
  }
703
- async open() {
972
+ async _open() {
704
973
  import_log2.log.trace("dxos.mesh.signal-client.open", import_protocols2.trace.begin({
705
974
  id: this._instanceId
706
975
  }), {
707
- F: __dxlog_file3,
708
- L: 128,
976
+ F: __dxlog_file4,
977
+ L: 83,
709
978
  S: this,
710
979
  C: (f, a) => f(...a)
711
980
  });
@@ -715,26 +984,33 @@ var SignalClient = class {
715
984
  ].includes(this._state)) {
716
985
  return;
717
986
  }
718
- this._ctx = new import_context2.Context({
719
- onError: (err) => {
720
- if (this._state === import_signal.SignalState.CLOSED || this._ctx?.disposed) {
721
- return;
722
- }
723
- if (this._state === import_signal.SignalState.CONNECTED) {
724
- import_log2.log.warn("SignalClient error:", err, {
725
- F: __dxlog_file3,
726
- L: 140,
727
- S: this,
728
- C: (f, a) => f(...a)
729
- });
730
- }
731
- this._scheduleReconcileAfterError();
732
- }
733
- });
987
+ this._setState(import_signal.SignalState.CONNECTING);
734
988
  this._reconcileTask = new import_async2.DeferredTask(this._ctx, async () => {
735
- await this._reconcileSwarmSubscriptions();
736
- await this._reconcileMessageSubscriptions();
737
- this._reconciled.emit();
989
+ try {
990
+ await (0, import_context2.cancelWithContext)(this._connectionCtx, this._clientReady.wait({
991
+ timeout: 5e3
992
+ }));
993
+ (0, import_invariant2.invariant)(this._state === import_signal.SignalState.CONNECTED, "Not connected to Signal Server", {
994
+ F: __dxlog_file4,
995
+ L: 93,
996
+ S: this,
997
+ A: [
998
+ "this._state === SignalState.CONNECTED",
999
+ "'Not connected to Signal Server'"
1000
+ ]
1001
+ });
1002
+ await this.localState.reconcile(this._connectionCtx, this._client);
1003
+ this._monitor.recordReconciliation({
1004
+ success: true
1005
+ });
1006
+ this._lastReconciliationFailed = false;
1007
+ } catch (err) {
1008
+ this._lastReconciliationFailed = true;
1009
+ this._monitor.recordReconciliation({
1010
+ success: false
1011
+ });
1012
+ throw err;
1013
+ }
738
1014
  });
739
1015
  (0, import_async2.scheduleTaskInterval)(this._ctx, async () => {
740
1016
  if (this._state === import_signal.SignalState.CONNECTED) {
@@ -742,23 +1018,46 @@ var SignalClient = class {
742
1018
  }
743
1019
  }, RECONCILE_INTERVAL);
744
1020
  this._reconnectTask = new import_async2.DeferredTask(this._ctx, async () => {
745
- await this._reconnect();
1021
+ try {
1022
+ await this._reconnect();
1023
+ this._monitor.recordReconnect({
1024
+ success: true
1025
+ });
1026
+ } catch (err) {
1027
+ this._monitor.recordReconnect({
1028
+ success: false
1029
+ });
1030
+ throw err;
1031
+ }
746
1032
  });
747
- this._setState(import_signal.SignalState.CONNECTING);
748
1033
  this._createClient();
749
1034
  import_log2.log.trace("dxos.mesh.signal-client.open", import_protocols2.trace.end({
750
1035
  id: this._instanceId
751
1036
  }), {
752
- F: __dxlog_file3,
753
- L: 169,
1037
+ F: __dxlog_file4,
1038
+ L: 126,
754
1039
  S: this,
755
1040
  C: (f, a) => f(...a)
756
1041
  });
757
1042
  }
758
- async close() {
1043
+ async _catch(err) {
1044
+ if (this._state === import_signal.SignalState.CLOSED || this._ctx.disposed) {
1045
+ return;
1046
+ }
1047
+ if (this._state === import_signal.SignalState.CONNECTED && !this._lastReconciliationFailed) {
1048
+ import_log2.log.warn("SignalClient error:", err, {
1049
+ F: __dxlog_file4,
1050
+ L: 135,
1051
+ S: this,
1052
+ C: (f, a) => f(...a)
1053
+ });
1054
+ }
1055
+ this._scheduleReconcileAfterError();
1056
+ }
1057
+ async _close() {
759
1058
  (0, import_log2.log)("closing...", void 0, {
760
- F: __dxlog_file3,
761
- L: 173,
1059
+ F: __dxlog_file4,
1060
+ L: 141,
762
1061
  S: this,
763
1062
  C: (f, a) => f(...a)
764
1063
  });
@@ -767,14 +1066,11 @@ var SignalClient = class {
767
1066
  ].includes(this._state)) {
768
1067
  return;
769
1068
  }
770
- await this._ctx?.dispose();
771
- this._clientReady.reset();
772
- await this._client?.close();
773
- this._client = void 0;
774
1069
  this._setState(import_signal.SignalState.CLOSED);
1070
+ await this._safeResetClient();
775
1071
  (0, import_log2.log)("closed", void 0, {
776
- F: __dxlog_file3,
777
- L: 184,
1072
+ F: __dxlog_file4,
1073
+ L: 149,
778
1074
  S: this,
779
1075
  C: (f, a) => f(...a)
780
1076
  });
@@ -785,228 +1081,181 @@ var SignalClient = class {
785
1081
  state: this._state,
786
1082
  error: this._lastError?.message,
787
1083
  reconnectIn: this._reconnectAfter,
788
- connectionStarted: this._connectionStarted,
789
- lastStateChange: this._lastStateChange
1084
+ ...this._monitor.getRecordedTimestamps()
790
1085
  };
791
1086
  }
792
- async join({ topic, peerId }) {
1087
+ async join(args) {
793
1088
  (0, import_log2.log)("joining", {
794
- topic,
795
- peerId
1089
+ topic: args.topic,
1090
+ peerId: args.peerId
796
1091
  }, {
797
- F: __dxlog_file3,
798
- L: 199,
1092
+ F: __dxlog_file4,
1093
+ L: 163,
799
1094
  S: this,
800
1095
  C: (f, a) => f(...a)
801
1096
  });
802
- this._performance.joinCounter++;
803
- this._joinedTopics.add({
804
- topic,
805
- peerId
806
- });
807
- this._reconcileTask.schedule();
1097
+ this._monitor.recordJoin();
1098
+ this.localState.join(args);
1099
+ this._reconcileTask?.schedule();
808
1100
  }
809
- async leave({ topic, peerId }) {
810
- this._performance.leaveCounter++;
1101
+ async leave(args) {
811
1102
  (0, import_log2.log)("leaving", {
812
- topic,
813
- peerId
1103
+ topic: args.topic,
1104
+ peerId: args.peerId
814
1105
  }, {
815
- F: __dxlog_file3,
816
- L: 207,
817
- S: this,
818
- C: (f, a) => f(...a)
819
- });
820
- void this._swarmStreams.get({
821
- topic,
822
- peerId
823
- })?.close();
824
- this._swarmStreams.delete({
825
- topic,
826
- peerId
827
- });
828
- this._joinedTopics.delete({
829
- topic,
830
- peerId
1106
+ F: __dxlog_file4,
1107
+ L: 170,
1108
+ S: this,
1109
+ C: (f, a) => f(...a)
831
1110
  });
1111
+ this._monitor.recordLeave();
1112
+ this.localState.leave(args);
832
1113
  }
833
1114
  async sendMessage(msg) {
834
- this._performance.sentMessages++;
835
- await this._clientReady.wait();
836
- (0, import_invariant2.invariant)(this._state === import_signal.SignalState.CONNECTED, "Not connected to Signal Server", {
837
- F: __dxlog_file3,
838
- L: 216,
839
- S: this,
840
- A: [
841
- "this._state === SignalState.CONNECTED",
842
- "'Not connected to Signal Server'"
843
- ]
1115
+ return this._monitor.recordMessageSending(msg, async () => {
1116
+ await this._clientReady.wait();
1117
+ (0, import_invariant2.invariant)(this._state === import_signal.SignalState.CONNECTED, "Not connected to Signal Server", {
1118
+ F: __dxlog_file4,
1119
+ L: 178,
1120
+ S: this,
1121
+ A: [
1122
+ "this._state === SignalState.CONNECTED",
1123
+ "'Not connected to Signal Server'"
1124
+ ]
1125
+ });
1126
+ await this._client.sendMessage(msg);
844
1127
  });
845
- await this._client.sendMessage(msg);
846
1128
  }
847
1129
  async subscribeMessages(peerId) {
848
1130
  (0, import_log2.log)("subscribing to messages", {
849
1131
  peerId
850
1132
  }, {
851
- F: __dxlog_file3,
852
- L: 221,
1133
+ F: __dxlog_file4,
1134
+ L: 184,
853
1135
  S: this,
854
1136
  C: (f, a) => f(...a)
855
1137
  });
856
- this._subscribedMessages.add({
857
- peerId
858
- });
859
- this._reconcileTask.schedule();
1138
+ this.localState.subscribeMessages(peerId);
1139
+ this._reconcileTask?.schedule();
860
1140
  }
861
1141
  async unsubscribeMessages(peerId) {
862
1142
  (0, import_log2.log)("unsubscribing from messages", {
863
1143
  peerId
864
1144
  }, {
865
- F: __dxlog_file3,
866
- L: 227,
1145
+ F: __dxlog_file4,
1146
+ L: 190,
867
1147
  S: this,
868
1148
  C: (f, a) => f(...a)
869
1149
  });
870
- this._subscribedMessages.delete({
871
- peerId
872
- });
873
- void this._messageStreams.get(peerId)?.close();
874
- this._messageStreams.delete(peerId);
1150
+ this.localState.unsubscribeMessages(peerId);
875
1151
  }
876
1152
  _scheduleReconcileAfterError() {
877
- (0, import_async2.scheduleTask)(this._ctx, () => {
878
- this._reconcileTask.schedule();
879
- }, ERROR_RECONCILE_DELAY);
880
- }
881
- _setState(newState) {
882
- this._state = newState;
883
- this._lastStateChange = /* @__PURE__ */ new Date();
884
- (0, import_log2.log)("signal state changed", {
885
- status: this.getStatus()
886
- }, {
887
- F: __dxlog_file3,
888
- L: 246,
889
- S: this,
890
- C: (f, a) => f(...a)
891
- });
892
- this.statusChanged.emit(this.getStatus());
1153
+ (0, import_async2.scheduleTask)(this._ctx, () => this._reconcileTask.schedule(), ERROR_RECONCILE_DELAY);
893
1154
  }
894
1155
  _createClient() {
895
1156
  (0, import_log2.log)("creating client", {
896
1157
  host: this._host,
897
1158
  state: this._state
898
1159
  }, {
899
- F: __dxlog_file3,
900
- L: 251,
1160
+ F: __dxlog_file4,
1161
+ L: 199,
901
1162
  S: this,
902
1163
  C: (f, a) => f(...a)
903
1164
  });
904
1165
  (0, import_invariant2.invariant)(!this._client, "Client already created", {
905
- F: __dxlog_file3,
906
- L: 252,
1166
+ F: __dxlog_file4,
1167
+ L: 200,
907
1168
  S: this,
908
1169
  A: [
909
1170
  "!this._client",
910
1171
  "'Client already created'"
911
1172
  ]
912
1173
  });
913
- this._connectionStarted = /* @__PURE__ */ new Date();
1174
+ this._monitor.recordConnectionStartTime();
914
1175
  this._connectionCtx = this._ctx.derive();
915
1176
  this._connectionCtx.onDispose(async () => {
916
1177
  (0, import_log2.log)("connection context disposed", void 0, {
917
- F: __dxlog_file3,
918
- L: 259,
1178
+ F: __dxlog_file4,
1179
+ L: 207,
919
1180
  S: this,
920
1181
  C: (f, a) => f(...a)
921
1182
  });
922
- await Promise.all(Array.from(this._swarmStreams.values()).map((stream) => stream.close()));
923
- await Promise.all(Array.from(this._messageStreams.values()).map((stream) => stream.close()));
924
- this._swarmStreams.clear();
925
- this._messageStreams.clear();
1183
+ const { failureCount } = await this.localState.safeCloseStreams();
1184
+ this._monitor.recordStreamCloseErrors(failureCount);
926
1185
  });
927
1186
  try {
928
- this._client = new SignalRPCClient({
1187
+ const client = new SignalRPCClient({
929
1188
  url: this._host,
930
1189
  callbacks: {
931
1190
  onConnected: () => {
932
- (0, import_log2.log)("socket connected", void 0, {
933
- F: __dxlog_file3,
934
- L: 271,
935
- S: this,
936
- C: (f, a) => f(...a)
937
- });
938
- this._lastError = void 0;
939
- this._reconnectAfter = DEFAULT_RECONNECT_TIMEOUT;
940
- this._setState(import_signal.SignalState.CONNECTED);
941
- this._clientReady.wake();
942
- this._reconcileTask.schedule();
1191
+ if (client === this._client) {
1192
+ (0, import_log2.log)("socket connected", void 0, {
1193
+ F: __dxlog_file4,
1194
+ L: 218,
1195
+ S: this,
1196
+ C: (f, a) => f(...a)
1197
+ });
1198
+ this._onConnected();
1199
+ }
943
1200
  },
944
1201
  onDisconnected: () => {
1202
+ if (client !== this._client) {
1203
+ return;
1204
+ }
945
1205
  (0, import_log2.log)("socket disconnected", {
946
1206
  state: this._state
947
1207
  }, {
948
- F: __dxlog_file3,
949
- L: 280,
1208
+ F: __dxlog_file4,
1209
+ L: 227,
950
1210
  S: this,
951
1211
  C: (f, a) => f(...a)
952
1212
  });
953
1213
  if (this._state === import_signal.SignalState.ERROR) {
954
1214
  this._setState(import_signal.SignalState.DISCONNECTED);
955
- return;
956
- }
957
- if (this._state !== import_signal.SignalState.CONNECTED && this._state !== import_signal.SignalState.CONNECTING) {
958
- this._incrementReconnectTimeout();
1215
+ } else {
1216
+ this._onDisconnected();
959
1217
  }
960
- this._setState(import_signal.SignalState.DISCONNECTED);
961
- this._reconnectTask.schedule();
962
1218
  },
963
1219
  onError: (error) => {
964
- (0, import_log2.log)("socket error", {
965
- error,
966
- state: this._state
967
- }, {
968
- F: __dxlog_file3,
969
- L: 295,
970
- S: this,
971
- C: (f, a) => f(...a)
972
- });
973
- this._lastError = error;
974
- if (this._state !== import_signal.SignalState.CONNECTED && this._state !== import_signal.SignalState.CONNECTING) {
975
- this._incrementReconnectTimeout();
1220
+ if (client === this._client) {
1221
+ (0, import_log2.log)("socket error", {
1222
+ error,
1223
+ state: this._state
1224
+ }, {
1225
+ F: __dxlog_file4,
1226
+ L: 239,
1227
+ S: this,
1228
+ C: (f, a) => f(...a)
1229
+ });
1230
+ this._onDisconnected({
1231
+ error
1232
+ });
976
1233
  }
977
- this._setState(import_signal.SignalState.ERROR);
978
- this._reconnectTask.schedule();
979
1234
  },
980
1235
  getMetadata: this._getMetadata
981
1236
  }
982
1237
  });
983
- } catch (err) {
984
- if (this._state !== import_signal.SignalState.CONNECTED && this._state !== import_signal.SignalState.CONNECTING) {
985
- this._incrementReconnectTimeout();
986
- }
987
- this._lastError = err;
988
- this._setState(import_signal.SignalState.DISCONNECTED);
989
- this._reconnectTask.schedule();
1238
+ this._client = client;
1239
+ } catch (error) {
1240
+ this._client = void 0;
1241
+ this._onDisconnected({
1242
+ error
1243
+ });
990
1244
  }
991
1245
  }
992
- _incrementReconnectTimeout() {
993
- this._reconnectAfter *= 2;
994
- this._reconnectAfter = Math.min(this._reconnectAfter, MAX_RECONNECT_TIMEOUT);
995
- }
996
1246
  async _reconnect() {
997
1247
  (0, import_log2.log)(`reconnecting in ${this._reconnectAfter}ms`, {
998
1248
  state: this._state
999
1249
  }, {
1000
- F: __dxlog_file3,
1001
- L: 323,
1250
+ F: __dxlog_file4,
1251
+ L: 254,
1002
1252
  S: this,
1003
1253
  C: (f, a) => f(...a)
1004
1254
  });
1005
- this._performance.reconnectCounter++;
1006
1255
  if (this._state === import_signal.SignalState.RECONNECTING) {
1007
- import_log2.log.warn("Signal api already reconnecting.", void 0, {
1008
- F: __dxlog_file3,
1009
- L: 327,
1256
+ import_log2.log.info("Signal api already reconnecting.", void 0, {
1257
+ F: __dxlog_file4,
1258
+ L: 257,
1010
1259
  S: this,
1011
1260
  C: (f, a) => f(...a)
1012
1261
  });
@@ -1015,137 +1264,84 @@ var SignalClient = class {
1015
1264
  if (this._state === import_signal.SignalState.CLOSED) {
1016
1265
  return;
1017
1266
  }
1018
- this._clientReady.reset();
1019
- await this._connectionCtx?.dispose();
1020
- this._client?.close().catch(() => {
1021
- });
1022
- this._client = void 0;
1023
- await (0, import_context2.cancelWithContext)(this._ctx, (0, import_async2.sleep)(this._reconnectAfter));
1024
1267
  this._setState(import_signal.SignalState.RECONNECTING);
1268
+ await this._safeResetClient();
1269
+ await (0, import_context2.cancelWithContext)(this._ctx, (0, import_async2.sleep)(this._reconnectAfter));
1025
1270
  this._createClient();
1026
1271
  }
1027
- async _reconcileSwarmSubscriptions() {
1028
- await (0, import_async2.asyncTimeout)((0, import_context2.cancelWithContext)(this._connectionCtx, this._clientReady.wait()), 5e3);
1029
- const client = this._client;
1030
- (0, import_invariant2.invariant)(this._state === import_signal.SignalState.CONNECTED, "Not connected to Signal Server", {
1031
- F: __dxlog_file3,
1032
- L: 352,
1033
- S: this,
1034
- A: [
1035
- "this._state === SignalState.CONNECTED",
1036
- "'Not connected to Signal Server'"
1037
- ]
1038
- });
1039
- for (const { topic, peerId } of this._swarmStreams.keys()) {
1040
- if (this._joinedTopics.has({
1041
- topic,
1042
- peerId
1043
- })) {
1044
- continue;
1045
- }
1046
- void this._swarmStreams.get({
1047
- topic,
1048
- peerId
1049
- })?.close();
1050
- this._swarmStreams.delete({
1051
- topic,
1052
- peerId
1053
- });
1272
+ _onConnected() {
1273
+ this._lastError = void 0;
1274
+ this._lastReconciliationFailed = false;
1275
+ this._reconnectAfter = DEFAULT_RECONNECT_TIMEOUT;
1276
+ this._setState(import_signal.SignalState.CONNECTED);
1277
+ this._clientReady.wake();
1278
+ this._reconcileTask.schedule();
1279
+ }
1280
+ _onDisconnected(options) {
1281
+ this._updateReconnectTimeout();
1282
+ if (this._state === import_signal.SignalState.CLOSED) {
1283
+ return;
1054
1284
  }
1055
- for (const { topic, peerId } of this._joinedTopics.values()) {
1056
- if (this._swarmStreams.has({
1057
- topic,
1058
- peerId
1059
- })) {
1060
- continue;
1061
- }
1062
- const swarmStream = await (0, import_async2.asyncTimeout)((0, import_context2.cancelWithContext)(this._connectionCtx, client.join({
1063
- topic,
1064
- peerId
1065
- })), 5e3);
1066
- swarmStream.subscribe(async (swarmEvent) => {
1067
- (0, import_log2.log)("swarm event", {
1068
- swarmEvent
1069
- }, {
1070
- F: __dxlog_file3,
1071
- L: 379,
1072
- S: this,
1073
- C: (f, a) => f(...a)
1074
- });
1075
- await this._onSwarmEvent({
1076
- topic,
1077
- swarmEvent
1078
- });
1079
- });
1080
- this._swarmStreams.set({
1081
- topic,
1082
- peerId
1083
- }, swarmStream);
1285
+ if (options?.error) {
1286
+ this._lastError = options.error;
1287
+ this._setState(import_signal.SignalState.ERROR);
1288
+ } else {
1289
+ this._setState(import_signal.SignalState.DISCONNECTED);
1084
1290
  }
1291
+ this._reconnectTask.schedule();
1085
1292
  }
1086
- async _reconcileMessageSubscriptions() {
1087
- await (0, import_async2.asyncTimeout)((0, import_context2.cancelWithContext)(this._connectionCtx, this._clientReady.wait()), 5e3);
1088
- const client = this._client;
1089
- (0, import_invariant2.invariant)(this._state === import_signal.SignalState.CONNECTED, "Not connected to Signal Server", {
1090
- F: __dxlog_file3,
1091
- L: 392,
1293
+ _setState(newState) {
1294
+ this._state = newState;
1295
+ this._monitor.recordStateChangeTime();
1296
+ (0, import_log2.log)("signal state changed", {
1297
+ status: this.getStatus()
1298
+ }, {
1299
+ F: __dxlog_file4,
1300
+ L: 298,
1092
1301
  S: this,
1093
- A: [
1094
- "this._state === SignalState.CONNECTED",
1095
- "'Not connected to Signal Server'"
1096
- ]
1302
+ C: (f, a) => f(...a)
1097
1303
  });
1098
- for (const peerId of this._messageStreams.keys()) {
1099
- if (this._subscribedMessages.has({
1100
- peerId
1101
- })) {
1102
- continue;
1103
- }
1104
- void this._messageStreams.get(peerId)?.close();
1105
- this._messageStreams.delete(peerId);
1106
- }
1107
- for (const { peerId } of this._subscribedMessages.values()) {
1108
- if (this._messageStreams.has(peerId)) {
1109
- continue;
1110
- }
1111
- const messageStream = await (0, import_async2.asyncTimeout)((0, import_context2.cancelWithContext)(this._connectionCtx, client.receiveMessages(peerId)), 5e3);
1112
- messageStream.subscribe(async (message) => {
1113
- this._performance.receivedMessages++;
1114
- await this._onMessage({
1115
- author: import_keys2.PublicKey.from(message.author),
1116
- recipient: import_keys2.PublicKey.from(message.recipient),
1117
- payload: message.payload
1118
- });
1119
- });
1120
- this._messageStreams.set(peerId, messageStream);
1304
+ this.statusChanged.emit(this.getStatus());
1305
+ }
1306
+ _updateReconnectTimeout() {
1307
+ if (this._state !== import_signal.SignalState.CONNECTED && this._state !== import_signal.SignalState.CONNECTING) {
1308
+ this._reconnectAfter *= 2;
1309
+ this._reconnectAfter = Math.min(this._reconnectAfter, MAX_RECONNECT_TIMEOUT);
1121
1310
  }
1122
1311
  }
1312
+ async _safeResetClient() {
1313
+ await this._connectionCtx?.dispose();
1314
+ this._connectionCtx = void 0;
1315
+ this._clientReady.reset();
1316
+ await this._client?.close().catch(() => {
1317
+ });
1318
+ this._client = void 0;
1319
+ }
1123
1320
  };
1124
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/signal-manager/memory-signal-manager.ts";
1321
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/signal-manager/memory-signal-manager.ts";
1125
1322
  var MemorySignalManagerContext = class {
1126
1323
  constructor() {
1127
- this.swarmEvent = new import_async4.Event();
1128
- this.swarms = new import_util3.ComplexMap(import_keys4.PublicKey.hash);
1129
- this.connections = new import_util3.ComplexMap(import_keys4.PublicKey.hash);
1324
+ this.swarmEvent = new import_async5.Event();
1325
+ this.swarms = new import_util3.ComplexMap(import_keys5.PublicKey.hash);
1326
+ this.connections = new import_util3.ComplexMap(import_keys5.PublicKey.hash);
1130
1327
  }
1131
1328
  };
1132
1329
  var MemorySignalManager = class {
1133
1330
  constructor(_context) {
1134
1331
  this._context = _context;
1135
- this.statusChanged = new import_async4.Event();
1136
- this.commandTrace = new import_async4.Event();
1137
- this.swarmEvent = new import_async4.Event();
1138
- this.onMessage = new import_async4.Event();
1332
+ this.statusChanged = new import_async5.Event();
1333
+ this.swarmEvent = new import_async5.Event();
1334
+ this.onMessage = new import_async5.Event();
1139
1335
  this._joinedSwarms = new import_util3.ComplexSet(({ topic, peerId }) => topic.toHex() + peerId.toHex());
1140
- this._freezeTrigger = new import_async4.Trigger().wake();
1141
- this._ctx = new import_context4.Context();
1336
+ this._freezeTrigger = new import_async5.Trigger().wake();
1337
+ this._ctx = new import_context5.Context();
1142
1338
  this._ctx.onDispose(this._context.swarmEvent.on((data) => this.swarmEvent.emit(data)));
1143
1339
  }
1144
1340
  async open() {
1145
1341
  if (!this._ctx.disposed) {
1146
1342
  return;
1147
1343
  }
1148
- this._ctx = new import_context4.Context();
1344
+ this._ctx = new import_context5.Context();
1149
1345
  this._ctx.onDispose(this._context.swarmEvent.on((data) => this.swarmEvent.emit(data)));
1150
1346
  await Promise.all([
1151
1347
  ...this._joinedSwarms.values()
@@ -1169,8 +1365,8 @@ var MemorySignalManager = class {
1169
1365
  }
1170
1366
  async join({ topic, peerId }) {
1171
1367
  (0, import_invariant4.invariant)(!this._ctx.disposed, "Closed", {
1172
- F: __dxlog_file4,
1173
- L: 102,
1368
+ F: __dxlog_file5,
1369
+ L: 100,
1174
1370
  S: this,
1175
1371
  A: [
1176
1372
  "!this._ctx.disposed",
@@ -1182,7 +1378,7 @@ var MemorySignalManager = class {
1182
1378
  peerId
1183
1379
  });
1184
1380
  if (!this._context.swarms.has(topic)) {
1185
- this._context.swarms.set(topic, new import_util3.ComplexSet(import_keys4.PublicKey.hash));
1381
+ this._context.swarms.set(topic, new import_util3.ComplexSet(import_keys5.PublicKey.hash));
1186
1382
  }
1187
1383
  this._context.swarms.get(topic).add(peerId);
1188
1384
  this._context.swarmEvent.emit({
@@ -1210,8 +1406,8 @@ var MemorySignalManager = class {
1210
1406
  }
1211
1407
  async leave({ topic, peerId }) {
1212
1408
  (0, import_invariant4.invariant)(!this._ctx.disposed, "Closed", {
1213
- F: __dxlog_file4,
1214
- L: 138,
1409
+ F: __dxlog_file5,
1410
+ L: 136,
1215
1411
  S: this,
1216
1412
  A: [
1217
1413
  "!this._ctx.disposed",
@@ -1223,7 +1419,7 @@ var MemorySignalManager = class {
1223
1419
  peerId
1224
1420
  });
1225
1421
  if (!this._context.swarms.has(topic)) {
1226
- this._context.swarms.set(topic, new import_util3.ComplexSet(import_keys4.PublicKey.hash));
1422
+ this._context.swarms.set(topic, new import_util3.ComplexSet(import_keys5.PublicKey.hash));
1227
1423
  }
1228
1424
  this._context.swarms.get(topic).delete(peerId);
1229
1425
  const swarmEvent = {
@@ -1237,19 +1433,19 @@ var MemorySignalManager = class {
1237
1433
  });
1238
1434
  }
1239
1435
  async sendMessage({ author, recipient, payload }) {
1240
- (0, import_log4.log)("send message", {
1436
+ (0, import_log5.log)("send message", {
1241
1437
  author,
1242
1438
  recipient,
1243
1439
  ...dec(payload)
1244
1440
  }, {
1245
- F: __dxlog_file4,
1246
- L: 158,
1441
+ F: __dxlog_file5,
1442
+ L: 156,
1247
1443
  S: this,
1248
1444
  C: (f, a) => f(...a)
1249
1445
  });
1250
1446
  (0, import_invariant4.invariant)(recipient, void 0, {
1251
- F: __dxlog_file4,
1252
- L: 160,
1447
+ F: __dxlog_file5,
1448
+ L: 158,
1253
1449
  S: this,
1254
1450
  A: [
1255
1451
  "recipient",
@@ -1257,8 +1453,8 @@ var MemorySignalManager = class {
1257
1453
  ]
1258
1454
  });
1259
1455
  (0, import_invariant4.invariant)(!this._ctx.disposed, "Closed", {
1260
- F: __dxlog_file4,
1261
- L: 161,
1456
+ F: __dxlog_file5,
1457
+ L: 159,
1262
1458
  S: this,
1263
1459
  A: [
1264
1460
  "!this._ctx.disposed",
@@ -1268,24 +1464,24 @@ var MemorySignalManager = class {
1268
1464
  await this._freezeTrigger.wait();
1269
1465
  const remote = this._context.connections.get(recipient);
1270
1466
  if (!remote) {
1271
- import_log4.log.warn("recipient is not subscribed for messages", {
1467
+ import_log5.log.warn("recipient is not subscribed for messages", {
1272
1468
  author,
1273
1469
  recipient
1274
1470
  }, {
1275
- F: __dxlog_file4,
1276
- L: 167,
1471
+ F: __dxlog_file5,
1472
+ L: 165,
1277
1473
  S: this,
1278
1474
  C: (f, a) => f(...a)
1279
1475
  });
1280
1476
  return;
1281
1477
  }
1282
1478
  if (remote._ctx.disposed) {
1283
- import_log4.log.warn("recipient is disposed", {
1479
+ import_log5.log.warn("recipient is disposed", {
1284
1480
  author,
1285
1481
  recipient
1286
1482
  }, {
1287
- F: __dxlog_file4,
1288
- L: 172,
1483
+ F: __dxlog_file5,
1484
+ L: 170,
1289
1485
  S: this,
1290
1486
  C: (f, a) => f(...a)
1291
1487
  });
@@ -1293,24 +1489,24 @@ var MemorySignalManager = class {
1293
1489
  }
1294
1490
  remote._freezeTrigger.wait().then(() => {
1295
1491
  if (remote._ctx.disposed) {
1296
- import_log4.log.warn("recipient is disposed", {
1492
+ import_log5.log.warn("recipient is disposed", {
1297
1493
  author,
1298
1494
  recipient
1299
1495
  }, {
1300
- F: __dxlog_file4,
1301
- L: 180,
1496
+ F: __dxlog_file5,
1497
+ L: 178,
1302
1498
  S: this,
1303
1499
  C: (f, a) => f(...a)
1304
1500
  });
1305
1501
  return;
1306
1502
  }
1307
- (0, import_log4.log)("receive message", {
1503
+ (0, import_log5.log)("receive message", {
1308
1504
  author,
1309
1505
  recipient,
1310
1506
  ...dec(payload)
1311
1507
  }, {
1312
- F: __dxlog_file4,
1313
- L: 184,
1508
+ F: __dxlog_file5,
1509
+ L: 182,
1314
1510
  S: this,
1315
1511
  C: (f, a) => f(...a)
1316
1512
  });
@@ -1320,33 +1516,33 @@ var MemorySignalManager = class {
1320
1516
  payload
1321
1517
  });
1322
1518
  }).catch((err) => {
1323
- import_log4.log.error("error while waiting for freeze", {
1519
+ import_log5.log.error("error while waiting for freeze", {
1324
1520
  err
1325
1521
  }, {
1326
- F: __dxlog_file4,
1327
- L: 189,
1522
+ F: __dxlog_file5,
1523
+ L: 187,
1328
1524
  S: this,
1329
1525
  C: (f, a) => f(...a)
1330
1526
  });
1331
1527
  });
1332
1528
  }
1333
1529
  async subscribeMessages(peerId) {
1334
- (0, import_log4.log)("subscribing", {
1530
+ (0, import_log5.log)("subscribing", {
1335
1531
  peerId
1336
1532
  }, {
1337
- F: __dxlog_file4,
1338
- L: 194,
1533
+ F: __dxlog_file5,
1534
+ L: 192,
1339
1535
  S: this,
1340
1536
  C: (f, a) => f(...a)
1341
1537
  });
1342
1538
  this._context.connections.set(peerId, this);
1343
1539
  }
1344
1540
  async unsubscribeMessages(peerId) {
1345
- (0, import_log4.log)("unsubscribing", {
1541
+ (0, import_log5.log)("unsubscribing", {
1346
1542
  peerId
1347
1543
  }, {
1348
- F: __dxlog_file4,
1349
- L: 199,
1544
+ F: __dxlog_file5,
1545
+ L: 197,
1350
1546
  S: this,
1351
1547
  C: (f, a) => f(...a)
1352
1548
  });
@@ -1372,6 +1568,19 @@ var dec = (payload) => {
1372
1568
  }
1373
1569
  return {};
1374
1570
  };
1571
+ var WebsocketSignalManagerMonitor = class {
1572
+ recordRateLimitExceeded() {
1573
+ import_tracing4.trace.metrics.increment("mesh.signal.signal-manager.rate-limit-hit", 1);
1574
+ }
1575
+ recordServerFailure(params) {
1576
+ import_tracing4.trace.metrics.increment("mesh.signal.signal-manager.server-failure", 1, {
1577
+ tags: {
1578
+ server: params.serverName,
1579
+ restarted: params.willRestart
1580
+ }
1581
+ });
1582
+ }
1583
+ };
1375
1584
  function _ts_decorate(decorators, target, key, desc) {
1376
1585
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1377
1586
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
@@ -1382,7 +1591,7 @@ function _ts_decorate(decorators, target, key, desc) {
1382
1591
  r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1383
1592
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1384
1593
  }
1385
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/signal-manager/websocket-signal-manager.ts";
1594
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/signal-manager/websocket-signal-manager.ts";
1386
1595
  var MAX_SERVER_FAILURES = 5;
1387
1596
  var WSS_SIGNAL_SERVER_REBOOT_DELAY = 3e3;
1388
1597
  var WebsocketSignalManager = class {
@@ -1390,18 +1599,18 @@ var WebsocketSignalManager = class {
1390
1599
  this._hosts = _hosts;
1391
1600
  this._getMetadata = _getMetadata;
1392
1601
  this._servers = /* @__PURE__ */ new Map();
1602
+ this._monitor = new WebsocketSignalManagerMonitor();
1393
1603
  this._opened = false;
1394
1604
  this.failureCount = /* @__PURE__ */ new Map();
1395
- this.statusChanged = new import_async5.Event();
1396
- this.commandTrace = new import_async5.Event();
1397
- this.swarmEvent = new import_async5.Event();
1398
- this.onMessage = new import_async5.Event();
1399
- this._instanceId = import_keys5.PublicKey.random().toHex();
1400
- (0, import_log5.log)("Created WebsocketSignalManager", {
1605
+ this.statusChanged = new import_async6.Event();
1606
+ this.swarmEvent = new import_async6.Event();
1607
+ this.onMessage = new import_async6.Event();
1608
+ this._instanceId = import_keys6.PublicKey.random().toHex();
1609
+ (0, import_log6.log)("Created WebsocketSignalManager", {
1401
1610
  hosts: this._hosts
1402
1611
  }, {
1403
- F: __dxlog_file5,
1404
- L: 51,
1612
+ F: __dxlog_file6,
1613
+ L: 58,
1405
1614
  S: this,
1406
1615
  C: (f, a) => f(...a)
1407
1616
  });
@@ -1413,39 +1622,37 @@ var WebsocketSignalManager = class {
1413
1622
  server.statusChanged.on(() => this.statusChanged.emit(this.getStatus()));
1414
1623
  this._servers.set(host.server, server);
1415
1624
  this.failureCount.set(host.server, 0);
1416
- server.commandTrace.on((trace5) => this.commandTrace.emit(trace5));
1417
1625
  }
1626
+ this._failedServersBitfield = import_util4.BitField.zeros(this._hosts.length);
1418
1627
  }
1419
1628
  async open() {
1420
1629
  if (this._opened) {
1421
1630
  return;
1422
1631
  }
1423
- (0, import_log5.log)("open signal manager", {
1632
+ (0, import_log6.log)("open signal manager", {
1424
1633
  hosts: this._hosts
1425
1634
  }, {
1426
- F: __dxlog_file5,
1427
- L: 78,
1635
+ F: __dxlog_file6,
1636
+ L: 85,
1428
1637
  S: this,
1429
1638
  C: (f, a) => f(...a)
1430
1639
  });
1431
- import_log5.log.trace("dxos.mesh.websocket-signal-manager.open", import_protocols5.trace.begin({
1640
+ import_log6.log.trace("dxos.mesh.websocket-signal-manager.open", import_protocols5.trace.begin({
1432
1641
  id: this._instanceId
1433
1642
  }), {
1434
- F: __dxlog_file5,
1435
- L: 79,
1643
+ F: __dxlog_file6,
1644
+ L: 86,
1436
1645
  S: this,
1437
1646
  C: (f, a) => f(...a)
1438
1647
  });
1439
1648
  this._initContext();
1440
- [
1441
- ...this._servers.values()
1442
- ].forEach((server) => server.open());
1649
+ await (0, import_util4.safeAwaitAll)(this._servers.values(), (server) => server.open());
1443
1650
  this._opened = true;
1444
- import_log5.log.trace("dxos.mesh.websocket-signal-manager.open", import_protocols5.trace.end({
1651
+ import_log6.log.trace("dxos.mesh.websocket-signal-manager.open", import_protocols5.trace.end({
1445
1652
  id: this._instanceId
1446
1653
  }), {
1447
- F: __dxlog_file5,
1448
- L: 87,
1654
+ F: __dxlog_file6,
1655
+ L: 93,
1449
1656
  S: this,
1450
1657
  C: (f, a) => f(...a)
1451
1658
  });
@@ -1456,20 +1663,20 @@ var WebsocketSignalManager = class {
1456
1663
  }
1457
1664
  this._opened = false;
1458
1665
  await this._ctx.dispose();
1459
- await Promise.all(Array.from(this._servers.values()).map((server) => server.close()));
1666
+ await (0, import_util4.safeAwaitAll)(this._servers.values(), (server) => server.close());
1460
1667
  }
1461
1668
  async restartServer(serverName) {
1462
- (0, import_log5.log)("restarting server", {
1669
+ (0, import_log6.log)("restarting server", {
1463
1670
  serverName
1464
1671
  }, {
1465
- F: __dxlog_file5,
1466
- L: 103,
1672
+ F: __dxlog_file6,
1673
+ L: 107,
1467
1674
  S: this,
1468
1675
  C: (f, a) => f(...a)
1469
1676
  });
1470
1677
  (0, import_invariant5.invariant)(this._opened, "server already closed", {
1471
- F: __dxlog_file5,
1472
- L: 104,
1678
+ F: __dxlog_file6,
1679
+ L: 108,
1473
1680
  S: this,
1474
1681
  A: [
1475
1682
  "this._opened",
@@ -1478,8 +1685,8 @@ var WebsocketSignalManager = class {
1478
1685
  });
1479
1686
  const server = this._servers.get(serverName);
1480
1687
  (0, import_invariant5.invariant)(server, "server not found", {
1481
- F: __dxlog_file5,
1482
- L: 107,
1688
+ F: __dxlog_file6,
1689
+ L: 111,
1483
1690
  S: this,
1484
1691
  A: [
1485
1692
  "server",
@@ -1487,25 +1694,25 @@ var WebsocketSignalManager = class {
1487
1694
  ]
1488
1695
  });
1489
1696
  await server.close();
1490
- await (0, import_async5.sleep)(WSS_SIGNAL_SERVER_REBOOT_DELAY);
1697
+ await (0, import_async6.sleep)(WSS_SIGNAL_SERVER_REBOOT_DELAY);
1491
1698
  await server.open();
1492
1699
  }
1493
1700
  getStatus() {
1494
1701
  return Array.from(this._servers.values()).map((server) => server.getStatus());
1495
1702
  }
1496
1703
  async join({ topic, peerId }) {
1497
- (0, import_log5.log)("join", {
1704
+ (0, import_log6.log)("join", {
1498
1705
  topic,
1499
1706
  peerId
1500
1707
  }, {
1501
- F: __dxlog_file5,
1502
- L: 120,
1708
+ F: __dxlog_file6,
1709
+ L: 124,
1503
1710
  S: this,
1504
1711
  C: (f, a) => f(...a)
1505
1712
  });
1506
1713
  (0, import_invariant5.invariant)(this._opened, "Closed", {
1507
- F: __dxlog_file5,
1508
- L: 121,
1714
+ F: __dxlog_file6,
1715
+ L: 125,
1509
1716
  S: this,
1510
1717
  A: [
1511
1718
  "this._opened",
@@ -1518,18 +1725,18 @@ var WebsocketSignalManager = class {
1518
1725
  }));
1519
1726
  }
1520
1727
  async leave({ topic, peerId }) {
1521
- (0, import_log5.log)("leaving", {
1728
+ (0, import_log6.log)("leaving", {
1522
1729
  topic,
1523
1730
  peerId
1524
1731
  }, {
1525
- F: __dxlog_file5,
1526
- L: 127,
1732
+ F: __dxlog_file6,
1733
+ L: 131,
1527
1734
  S: this,
1528
1735
  C: (f, a) => f(...a)
1529
1736
  });
1530
1737
  (0, import_invariant5.invariant)(this._opened, "Closed", {
1531
- F: __dxlog_file5,
1532
- L: 128,
1738
+ F: __dxlog_file6,
1739
+ L: 132,
1533
1740
  S: this,
1534
1741
  A: [
1535
1742
  "this._opened",
@@ -1542,89 +1749,115 @@ var WebsocketSignalManager = class {
1542
1749
  }));
1543
1750
  }
1544
1751
  async sendMessage({ author, recipient, payload }) {
1545
- (0, import_log5.log)("signal", {
1752
+ (0, import_log6.log)("signal", {
1546
1753
  recipient
1547
1754
  }, {
1548
- F: __dxlog_file5,
1549
- L: 142,
1755
+ F: __dxlog_file6,
1756
+ L: 145,
1550
1757
  S: this,
1551
1758
  C: (f, a) => f(...a)
1552
1759
  });
1553
1760
  (0, import_invariant5.invariant)(this._opened, "Closed", {
1554
- F: __dxlog_file5,
1555
- L: 143,
1761
+ F: __dxlog_file6,
1762
+ L: 146,
1556
1763
  S: this,
1557
1764
  A: [
1558
1765
  "this._opened",
1559
1766
  "'Closed'"
1560
1767
  ]
1561
1768
  });
1562
- void this._forEachServer(async (server, serverName) => {
1769
+ void this._forEachServer(async (server, serverName, index) => {
1563
1770
  void server.sendMessage({
1564
1771
  author,
1565
1772
  recipient,
1566
1773
  payload
1567
- }).catch((err) => {
1774
+ }).then(() => this._clearServerFailedFlag(serverName, index)).catch((err) => {
1568
1775
  if (err instanceof import_protocols5.RateLimitExceededError) {
1569
- import_log5.log.info("WSS rate limit exceeded", {
1776
+ import_log6.log.info("WSS rate limit exceeded", {
1570
1777
  err
1571
1778
  }, {
1572
- F: __dxlog_file5,
1573
- L: 148,
1779
+ F: __dxlog_file6,
1780
+ L: 154,
1574
1781
  S: this,
1575
1782
  C: (f, a) => f(...a)
1576
1783
  });
1784
+ this._monitor.recordRateLimitExceeded();
1577
1785
  } else if (err instanceof import_protocols5.TimeoutError || err.constructor.name === "TimeoutError") {
1578
- import_log5.log.info("WSS sendMessage timeout", {
1786
+ import_log6.log.info("WSS sendMessage timeout", {
1579
1787
  err
1580
1788
  }, {
1581
- F: __dxlog_file5,
1582
- L: 150,
1789
+ F: __dxlog_file6,
1790
+ L: 157,
1583
1791
  S: this,
1584
1792
  C: (f, a) => f(...a)
1585
1793
  });
1586
- void this.checkServerFailure(serverName);
1794
+ void this.checkServerFailure(serverName, index);
1587
1795
  } else {
1588
- import_log5.log.info(`error sending to ${serverName}`, {
1796
+ import_log6.log.warn(`error sending to ${serverName}`, {
1589
1797
  err
1590
1798
  }, {
1591
- F: __dxlog_file5,
1592
- L: 153,
1799
+ F: __dxlog_file6,
1800
+ L: 160,
1593
1801
  S: this,
1594
1802
  C: (f, a) => f(...a)
1595
1803
  });
1596
- void this.checkServerFailure(serverName);
1804
+ void this.checkServerFailure(serverName, index);
1597
1805
  }
1598
1806
  });
1599
1807
  });
1600
1808
  }
1601
- async checkServerFailure(serverName) {
1809
+ async checkServerFailure(serverName, index) {
1602
1810
  const failureCount = this.failureCount.get(serverName) ?? 0;
1603
- if (failureCount > MAX_SERVER_FAILURES) {
1604
- import_log5.log.warn(`too many failures sending to ${serverName} (${failureCount} > ${MAX_SERVER_FAILURES}), restarting`, void 0, {
1605
- F: __dxlog_file5,
1606
- L: 164,
1607
- S: this,
1608
- C: (f, a) => f(...a)
1609
- });
1811
+ const isRestartRequired = failureCount > MAX_SERVER_FAILURES;
1812
+ this._monitor.recordServerFailure({
1813
+ serverName,
1814
+ willRestart: isRestartRequired
1815
+ });
1816
+ if (isRestartRequired) {
1817
+ if (!import_util4.BitField.get(this._failedServersBitfield, index)) {
1818
+ import_log6.log.warn("too many failures for ws-server, restarting", {
1819
+ serverName,
1820
+ failureCount
1821
+ }, {
1822
+ F: __dxlog_file6,
1823
+ L: 174,
1824
+ S: this,
1825
+ C: (f, a) => f(...a)
1826
+ });
1827
+ import_util4.BitField.set(this._failedServersBitfield, index, true);
1828
+ }
1610
1829
  await this.restartServer(serverName);
1611
1830
  this.failureCount.set(serverName, 0);
1612
1831
  return;
1613
1832
  }
1614
1833
  this.failureCount.set(serverName, (this.failureCount.get(serverName) ?? 0) + 1);
1615
1834
  }
1835
+ _clearServerFailedFlag(serverName, index) {
1836
+ if (import_util4.BitField.get(this._failedServersBitfield, index)) {
1837
+ import_log6.log.info("server connection restored", {
1838
+ serverName
1839
+ }, {
1840
+ F: __dxlog_file6,
1841
+ L: 187,
1842
+ S: this,
1843
+ C: (f, a) => f(...a)
1844
+ });
1845
+ import_util4.BitField.set(this._failedServersBitfield, index, false);
1846
+ this.failureCount.set(serverName, 0);
1847
+ }
1848
+ }
1616
1849
  async subscribeMessages(peerId) {
1617
- (0, import_log5.log)("subscribed for message stream", {
1850
+ (0, import_log6.log)("subscribed for message stream", {
1618
1851
  peerId
1619
1852
  }, {
1620
- F: __dxlog_file5,
1621
- L: 174,
1853
+ F: __dxlog_file6,
1854
+ L: 194,
1622
1855
  S: this,
1623
1856
  C: (f, a) => f(...a)
1624
1857
  });
1625
1858
  (0, import_invariant5.invariant)(this._opened, "Closed", {
1626
- F: __dxlog_file5,
1627
- L: 175,
1859
+ F: __dxlog_file6,
1860
+ L: 195,
1628
1861
  S: this,
1629
1862
  A: [
1630
1863
  "this._opened",
@@ -1634,17 +1867,17 @@ var WebsocketSignalManager = class {
1634
1867
  await this._forEachServer(async (server) => server.subscribeMessages(peerId));
1635
1868
  }
1636
1869
  async unsubscribeMessages(peerId) {
1637
- (0, import_log5.log)("subscribed for message stream", {
1870
+ (0, import_log6.log)("subscribed for message stream", {
1638
1871
  peerId
1639
1872
  }, {
1640
- F: __dxlog_file5,
1641
- L: 181,
1873
+ F: __dxlog_file6,
1874
+ L: 201,
1642
1875
  S: this,
1643
1876
  C: (f, a) => f(...a)
1644
1877
  });
1645
1878
  (0, import_invariant5.invariant)(this._opened, "Closed", {
1646
- F: __dxlog_file5,
1647
- L: 182,
1879
+ F: __dxlog_file6,
1880
+ L: 202,
1648
1881
  S: this,
1649
1882
  A: [
1650
1883
  "this._opened",
@@ -1654,42 +1887,42 @@ var WebsocketSignalManager = class {
1654
1887
  await this._forEachServer(async (server) => server.unsubscribeMessages(peerId));
1655
1888
  }
1656
1889
  _initContext() {
1657
- this._ctx = new import_context5.Context({
1658
- onError: (err) => import_log5.log.catch(err, void 0, {
1659
- F: __dxlog_file5,
1660
- L: 189,
1890
+ this._ctx = new import_context6.Context({
1891
+ onError: (err) => import_log6.log.catch(err, void 0, {
1892
+ F: __dxlog_file6,
1893
+ L: 209,
1661
1894
  S: this,
1662
1895
  C: (f, a) => f(...a)
1663
1896
  })
1664
1897
  });
1665
1898
  }
1666
1899
  async _forEachServer(fn) {
1667
- return Promise.all(Array.from(this._servers.entries()).map(([serverName, server]) => fn(server, serverName)));
1900
+ return Promise.all(Array.from(this._servers.entries()).map(([serverName, server], idx) => fn(server, serverName, idx)));
1668
1901
  }
1669
1902
  };
1670
1903
  _ts_decorate([
1671
- import_async5.synchronized
1904
+ import_async6.synchronized
1672
1905
  ], WebsocketSignalManager.prototype, "open", null);
1673
1906
  _ts_decorate([
1674
- import_async5.synchronized
1907
+ import_async6.synchronized
1675
1908
  ], WebsocketSignalManager.prototype, "close", null);
1676
1909
  _ts_decorate([
1677
- import_async5.synchronized
1910
+ import_async6.synchronized
1678
1911
  ], WebsocketSignalManager.prototype, "join", null);
1679
1912
  _ts_decorate([
1680
- import_async5.synchronized
1913
+ import_async6.synchronized
1681
1914
  ], WebsocketSignalManager.prototype, "leave", null);
1682
1915
  _ts_decorate([
1683
- import_async5.synchronized
1916
+ import_async6.synchronized
1684
1917
  ], WebsocketSignalManager.prototype, "checkServerFailure", null);
1685
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/signal-manager/utils.ts";
1918
+ var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/core/mesh/messaging/src/signal-manager/utils.ts";
1686
1919
  var setIdentityTags = ({ identityService, devicesService, setTag }) => {
1687
1920
  identityService.queryIdentity().subscribe((idqr) => {
1688
1921
  if (!idqr?.identity?.identityKey) {
1689
- (0, import_log6.log)("empty response from identity service", {
1922
+ (0, import_log7.log)("empty response from identity service", {
1690
1923
  idqr
1691
1924
  }, {
1692
- F: __dxlog_file6,
1925
+ F: __dxlog_file7,
1693
1926
  L: 21,
1694
1927
  S: void 0,
1695
1928
  C: (f, a) => f(...a)
@@ -1700,10 +1933,10 @@ var setIdentityTags = ({ identityService, devicesService, setTag }) => {
1700
1933
  });
1701
1934
  devicesService.queryDevices().subscribe((dqr) => {
1702
1935
  if (!dqr || !dqr.devices || dqr.devices.length === 0) {
1703
- (0, import_log6.log)("empty response from device service", {
1936
+ (0, import_log7.log)("empty response from device service", {
1704
1937
  device: dqr
1705
1938
  }, {
1706
- F: __dxlog_file6,
1939
+ F: __dxlog_file7,
1707
1940
  L: 30,
1708
1941
  S: void 0,
1709
1942
  C: (f, a) => f(...a)
@@ -1711,7 +1944,7 @@ var setIdentityTags = ({ identityService, devicesService, setTag }) => {
1711
1944
  return;
1712
1945
  }
1713
1946
  (0, import_invariant6.invariant)(dqr, "empty response from device service", {
1714
- F: __dxlog_file6,
1947
+ F: __dxlog_file7,
1715
1948
  L: 33,
1716
1949
  S: void 0,
1717
1950
  A: [
@@ -1721,10 +1954,10 @@ var setIdentityTags = ({ identityService, devicesService, setTag }) => {
1721
1954
  });
1722
1955
  const thisDevice = dqr.devices.find((device) => device.kind === import_services.DeviceKind.CURRENT);
1723
1956
  if (!thisDevice) {
1724
- (0, import_log6.log)("no current device", {
1957
+ (0, import_log7.log)("no current device", {
1725
1958
  device: dqr
1726
1959
  }, {
1727
- F: __dxlog_file6,
1960
+ F: __dxlog_file7,
1728
1961
  L: 37,
1729
1962
  S: void 0,
1730
1963
  C: (f, a) => f(...a)