@dxos/messaging 0.5.9-main.ea1d25b → 0.5.9-main.f099efe

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