@dxos/messaging 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/chunk-memory-signal-manager.mjs +652 -0
- package/dist/lib/chunk-memory-signal-manager.mjs.map +1 -0
- package/dist/lib/index.mjs +377 -0
- package/dist/lib/index.mjs.map +1 -0
- package/dist/lib/testing.mjs +288 -0
- package/dist/lib/testing.mjs.map +1 -0
- package/dist/types/src/index.d.ts +0 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/messenger.blueprint-test.d.ts.map +1 -1
- package/dist/types/src/messenger.d.ts +1 -1
- package/dist/types/src/messenger.d.ts.map +1 -1
- package/dist/types/src/signal-manager/edge-signal-manager.d.ts +21 -4
- package/dist/types/src/signal-manager/edge-signal-manager.d.ts.map +1 -1
- package/dist/types/src/signal-manager/edge-signal-manager.test.d.ts +2 -0
- package/dist/types/src/signal-manager/edge-signal-manager.test.d.ts.map +1 -0
- package/dist/types/src/signal-manager/index.d.ts +1 -2
- package/dist/types/src/signal-manager/index.d.ts.map +1 -1
- package/dist/types/src/signal-manager/memory-signal-manager.d.ts +12 -10
- package/dist/types/src/signal-manager/memory-signal-manager.d.ts.map +1 -1
- package/dist/types/src/signal-manager/memory-signal-manager.test.d.ts +2 -0
- package/dist/types/src/signal-manager/memory-signal-manager.test.d.ts.map +1 -0
- package/dist/types/src/signal-manager/signal-manager.d.ts +5 -0
- package/dist/types/src/signal-manager/signal-manager.d.ts.map +1 -1
- package/dist/types/src/signal-manager/utils.d.ts +6 -4
- package/dist/types/src/signal-manager/utils.d.ts.map +1 -1
- package/dist/types/src/signal-methods.d.ts +33 -22
- package/dist/types/src/signal-methods.d.ts.map +1 -1
- package/dist/types/src/testing/index.d.ts +1 -0
- package/dist/types/src/testing/index.d.ts.map +1 -1
- package/dist/types/src/testing/test-edge-mesh.d.ts +61 -0
- package/dist/types/src/testing/test-edge-mesh.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +19 -19
- package/src/index.ts +0 -1
- package/src/messenger.blueprint-test.ts +0 -25
- package/src/messenger.node.test.ts +1 -1
- package/src/messenger.ts +42 -18
- package/src/signal-manager/edge-signal-manager.test.ts +193 -0
- package/src/signal-manager/edge-signal-manager.ts +145 -12
- package/src/signal-manager/index.ts +1 -2
- package/src/signal-manager/memory-signal-manager.test.ts +199 -0
- package/src/signal-manager/memory-signal-manager.ts +87 -47
- package/src/signal-manager/signal-manager.ts +7 -0
- package/src/signal-manager/utils.ts +30 -21
- package/src/signal-methods.ts +37 -26
- package/src/testing/index.ts +1 -0
- package/src/testing/test-edge-mesh.ts +196 -0
- package/src/testing/utils.ts +2 -2
- package/dist/lib/neutral/chunk-EC4H56J5.mjs +0 -497
- package/dist/lib/neutral/chunk-EC4H56J5.mjs.map +0 -7
- package/dist/lib/neutral/index.mjs +0 -1249
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
- package/dist/lib/neutral/testing/index.mjs +0 -155
- package/dist/lib/neutral/testing/index.mjs.map +0 -7
- package/dist/types/src/signal-client/index.d.ts +0 -2
- package/dist/types/src/signal-client/index.d.ts.map +0 -1
- package/dist/types/src/signal-client/signal-client-monitor.d.ts +0 -30
- package/dist/types/src/signal-client/signal-client-monitor.d.ts.map +0 -1
- package/dist/types/src/signal-client/signal-client.d.ts +0 -59
- package/dist/types/src/signal-client/signal-client.d.ts.map +0 -1
- package/dist/types/src/signal-client/signal-client.node.test.d.ts +0 -2
- package/dist/types/src/signal-client/signal-client.node.test.d.ts.map +0 -1
- package/dist/types/src/signal-client/signal-local-state.d.ts +0 -38
- package/dist/types/src/signal-client/signal-local-state.d.ts.map +0 -1
- package/dist/types/src/signal-client/signal-rpc-client-monitor.d.ts +0 -6
- package/dist/types/src/signal-client/signal-rpc-client-monitor.d.ts.map +0 -1
- package/dist/types/src/signal-client/signal-rpc-client.d.ts +0 -47
- package/dist/types/src/signal-client/signal-rpc-client.d.ts.map +0 -1
- package/dist/types/src/signal-client/signal-rpc-client.node.test.d.ts +0 -2
- package/dist/types/src/signal-client/signal-rpc-client.node.test.d.ts.map +0 -1
- package/dist/types/src/signal-manager/websocket-signal-manager-monitor.d.ts +0 -8
- package/dist/types/src/signal-manager/websocket-signal-manager-monitor.d.ts.map +0 -1
- package/dist/types/src/signal-manager/websocket-signal-manager.d.ts +0 -41
- package/dist/types/src/signal-manager/websocket-signal-manager.d.ts.map +0 -1
- package/dist/types/src/signal-manager/websocket-signal-manager.node.test.d.ts +0 -2
- package/dist/types/src/signal-manager/websocket-signal-manager.node.test.d.ts.map +0 -1
- package/src/signal-client/index.ts +0 -5
- package/src/signal-client/signal-client-monitor.ts +0 -111
- package/src/signal-client/signal-client.node.test.ts +0 -185
- package/src/signal-client/signal-client.ts +0 -339
- package/src/signal-client/signal-local-state.ts +0 -174
- package/src/signal-client/signal-rpc-client-monitor.ts +0 -15
- package/src/signal-client/signal-rpc-client.node.test.ts +0 -102
- package/src/signal-client/signal-rpc-client.ts +0 -227
- package/src/signal-manager/websocket-signal-manager-monitor.ts +0 -20
- package/src/signal-manager/websocket-signal-manager.node.test.ts +0 -112
- package/src/signal-manager/websocket-signal-manager.ts +0 -188
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
import { Event, TimeoutError, Trigger, scheduleExponentialBackoffTaskInterval, scheduleTask, scheduleTaskInterval } from "@dxos/async";
|
|
2
|
+
import { Context } from "@dxos/context";
|
|
3
|
+
import { invariant } from "@dxos/invariant";
|
|
4
|
+
import { PublicKey } from "@dxos/keys";
|
|
5
|
+
import { log } from "@dxos/log";
|
|
6
|
+
import { TimeoutError as TimeoutError$1 } from "@dxos/protocols";
|
|
7
|
+
import { schema } from "@dxos/protocols/proto";
|
|
8
|
+
import { ComplexMap, ComplexSet } from "@dxos/util";
|
|
9
|
+
import { trace } from "@dxos/tracing";
|
|
10
|
+
//#region src/messenger-monitor.ts
|
|
11
|
+
var MessengerMonitor = class {
|
|
12
|
+
recordMessageAckFailed() {
|
|
13
|
+
trace.metrics.increment("dxos.mesh.signal.messenger.failed-ack", 1);
|
|
14
|
+
}
|
|
15
|
+
recordReliableMessage(params) {
|
|
16
|
+
trace.metrics.increment("dxos.mesh.signal.messenger.reliable-send", 1, { tags: {
|
|
17
|
+
success: params.sent,
|
|
18
|
+
attempts: params.sendAttempts
|
|
19
|
+
} });
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/timeouts.ts
|
|
24
|
+
/**
|
|
25
|
+
* Timeout for retrying messages.
|
|
26
|
+
*/
|
|
27
|
+
var MESSAGE_TIMEOUT = 1e4;
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/messenger.ts
|
|
30
|
+
var __dxlog_file$1 = "/__w/dxos/dxos/packages/core/mesh/messaging/src/messenger.ts";
|
|
31
|
+
var ReliablePayload = schema.getCodecForType("dxos.mesh.messaging.ReliablePayload");
|
|
32
|
+
var Acknowledgement = schema.getCodecForType("dxos.mesh.messaging.Acknowledgement");
|
|
33
|
+
var RECEIVED_MESSAGES_GC_INTERVAL = 12e4;
|
|
34
|
+
/**
|
|
35
|
+
* Reliable messenger that works trough signal network.
|
|
36
|
+
*/
|
|
37
|
+
var Messenger = class {
|
|
38
|
+
_monitor = new MessengerMonitor();
|
|
39
|
+
_signalManager;
|
|
40
|
+
_listeners = new ComplexMap(({ peerId, payloadType }) => peerId + payloadType);
|
|
41
|
+
_defaultListeners = /* @__PURE__ */ new Map();
|
|
42
|
+
_onAckCallbacks = new ComplexMap(PublicKey.hash);
|
|
43
|
+
_receivedMessages = new ComplexSet(PublicKey.hash);
|
|
44
|
+
/**
|
|
45
|
+
* Keys scheduled to be cleared from _receivedMessages on the next iteration.
|
|
46
|
+
*/
|
|
47
|
+
_toClear = new ComplexSet(PublicKey.hash);
|
|
48
|
+
_ctx;
|
|
49
|
+
_closed = true;
|
|
50
|
+
_retryDelay;
|
|
51
|
+
constructor({ signalManager, retryDelay = 1e3 }) {
|
|
52
|
+
this._signalManager = signalManager;
|
|
53
|
+
this._retryDelay = retryDelay;
|
|
54
|
+
this.open();
|
|
55
|
+
}
|
|
56
|
+
open() {
|
|
57
|
+
if (!this._closed) return;
|
|
58
|
+
log("opening messenger", void 0, {
|
|
59
|
+
"~LogMeta": "~LogMeta",
|
|
60
|
+
F: __dxlog_file$1,
|
|
61
|
+
L: 71,
|
|
62
|
+
S: this
|
|
63
|
+
});
|
|
64
|
+
this._ctx = new Context({ onError: (err) => log.catch(err, void 0, {
|
|
65
|
+
"~LogMeta": "~LogMeta",
|
|
66
|
+
F: __dxlog_file$1,
|
|
67
|
+
L: 73,
|
|
68
|
+
S: this
|
|
69
|
+
}) }, {
|
|
70
|
+
"~LogMeta": "~LogMeta",
|
|
71
|
+
F: __dxlog_file$1,
|
|
72
|
+
L: 72
|
|
73
|
+
});
|
|
74
|
+
scheduleTaskInterval(this._ctx, async () => {
|
|
75
|
+
this._performGc();
|
|
76
|
+
}, RECEIVED_MESSAGES_GC_INTERVAL);
|
|
77
|
+
this._closed = false;
|
|
78
|
+
log("opened messenger", void 0, {
|
|
79
|
+
"~LogMeta": "~LogMeta",
|
|
80
|
+
F: __dxlog_file$1,
|
|
81
|
+
L: 86,
|
|
82
|
+
S: this
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async close() {
|
|
86
|
+
if (this._closed) return;
|
|
87
|
+
this._closed = true;
|
|
88
|
+
await this._ctx.dispose();
|
|
89
|
+
}
|
|
90
|
+
async sendMessage(ctx, message) {
|
|
91
|
+
invariant(!this._closed, "Closed", {
|
|
92
|
+
"~LogMeta": "~LogMeta",
|
|
93
|
+
F: __dxlog_file$1,
|
|
94
|
+
L: 102,
|
|
95
|
+
S: this,
|
|
96
|
+
A: ["!this._closed", "'Closed'"]
|
|
97
|
+
});
|
|
98
|
+
const { author, recipient, payload } = message;
|
|
99
|
+
invariant(recipient, "Recipient is required", {
|
|
100
|
+
"~LogMeta": "~LogMeta",
|
|
101
|
+
F: __dxlog_file$1,
|
|
102
|
+
L: 105,
|
|
103
|
+
S: this,
|
|
104
|
+
A: ["recipient", "'Recipient is required'"]
|
|
105
|
+
});
|
|
106
|
+
const messageContext = this._ctx.derive();
|
|
107
|
+
const reliablePayload = {
|
|
108
|
+
messageId: PublicKey.random(),
|
|
109
|
+
payload
|
|
110
|
+
};
|
|
111
|
+
invariant(!this._onAckCallbacks.has(reliablePayload.messageId), void 0, {
|
|
112
|
+
"~LogMeta": "~LogMeta",
|
|
113
|
+
F: __dxlog_file$1,
|
|
114
|
+
L: 112,
|
|
115
|
+
S: this,
|
|
116
|
+
A: ["!this._onAckCallbacks.has(reliablePayload.messageId!)", ""]
|
|
117
|
+
});
|
|
118
|
+
log("send message", {
|
|
119
|
+
messageId: reliablePayload.messageId,
|
|
120
|
+
author,
|
|
121
|
+
recipient
|
|
122
|
+
}, {
|
|
123
|
+
"~LogMeta": "~LogMeta",
|
|
124
|
+
F: __dxlog_file$1,
|
|
125
|
+
L: 113,
|
|
126
|
+
S: this
|
|
127
|
+
});
|
|
128
|
+
let messageReceived;
|
|
129
|
+
let timeoutHit;
|
|
130
|
+
let sendAttempts = 0;
|
|
131
|
+
const promise = new Promise((resolve, reject) => {
|
|
132
|
+
messageReceived = resolve;
|
|
133
|
+
timeoutHit = reject;
|
|
134
|
+
});
|
|
135
|
+
scheduleExponentialBackoffTaskInterval(messageContext, async () => {
|
|
136
|
+
log("retrying message", { messageId: reliablePayload.messageId }, {
|
|
137
|
+
"~LogMeta": "~LogMeta",
|
|
138
|
+
F: __dxlog_file$1,
|
|
139
|
+
L: 128,
|
|
140
|
+
S: this
|
|
141
|
+
});
|
|
142
|
+
sendAttempts++;
|
|
143
|
+
await this._encodeAndSend(ctx, {
|
|
144
|
+
author,
|
|
145
|
+
recipient,
|
|
146
|
+
reliablePayload
|
|
147
|
+
}).catch((err) => log("failed to send message", { err }, {
|
|
148
|
+
"~LogMeta": "~LogMeta",
|
|
149
|
+
F: __dxlog_file$1,
|
|
150
|
+
L: 131,
|
|
151
|
+
S: this
|
|
152
|
+
}));
|
|
153
|
+
}, this._retryDelay);
|
|
154
|
+
scheduleTask(messageContext, () => {
|
|
155
|
+
log("message not delivered", { messageId: reliablePayload.messageId }, {
|
|
156
|
+
"~LogMeta": "~LogMeta",
|
|
157
|
+
F: __dxlog_file$1,
|
|
158
|
+
L: 140,
|
|
159
|
+
S: this
|
|
160
|
+
});
|
|
161
|
+
this._onAckCallbacks.delete(reliablePayload.messageId);
|
|
162
|
+
timeoutHit(new TimeoutError$1({
|
|
163
|
+
message: "signaling message not delivered",
|
|
164
|
+
cause: new TimeoutError(MESSAGE_TIMEOUT, "Message not delivered")
|
|
165
|
+
}));
|
|
166
|
+
messageContext.dispose();
|
|
167
|
+
this._monitor.recordReliableMessage({
|
|
168
|
+
sendAttempts,
|
|
169
|
+
sent: false
|
|
170
|
+
});
|
|
171
|
+
}, MESSAGE_TIMEOUT);
|
|
172
|
+
this._onAckCallbacks.set(reliablePayload.messageId, () => {
|
|
173
|
+
messageReceived();
|
|
174
|
+
this._onAckCallbacks.delete(reliablePayload.messageId);
|
|
175
|
+
messageContext.dispose();
|
|
176
|
+
this._monitor.recordReliableMessage({
|
|
177
|
+
sendAttempts,
|
|
178
|
+
sent: true
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
await this._encodeAndSend(ctx, {
|
|
182
|
+
author,
|
|
183
|
+
recipient,
|
|
184
|
+
reliablePayload
|
|
185
|
+
});
|
|
186
|
+
return promise;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Subscribes onMessage function to messages that contains payload with payloadType.
|
|
190
|
+
* @param payloadType if not specified, onMessage will be subscribed to all types of messages.
|
|
191
|
+
*/
|
|
192
|
+
async listen({ peer, payloadType, onMessage }) {
|
|
193
|
+
invariant(!this._closed, "Closed", {
|
|
194
|
+
"~LogMeta": "~LogMeta",
|
|
195
|
+
F: __dxlog_file$1,
|
|
196
|
+
L: 178,
|
|
197
|
+
S: this,
|
|
198
|
+
A: ["!this._closed", "'Closed'"]
|
|
199
|
+
});
|
|
200
|
+
invariant(peer.peerKey, "Peer key is required", {
|
|
201
|
+
"~LogMeta": "~LogMeta",
|
|
202
|
+
F: __dxlog_file$1,
|
|
203
|
+
L: 179,
|
|
204
|
+
S: this,
|
|
205
|
+
A: ["peer.peerKey", "'Peer key is required'"]
|
|
206
|
+
});
|
|
207
|
+
const peerKey = peer.peerKey;
|
|
208
|
+
const unsubscribe = await this._signalManager.subscribeMessages({
|
|
209
|
+
peer,
|
|
210
|
+
onMessage: (message) => {
|
|
211
|
+
if (this._closed) return;
|
|
212
|
+
log("received message", { from: message.author }, {
|
|
213
|
+
"~LogMeta": "~LogMeta",
|
|
214
|
+
F: __dxlog_file$1,
|
|
215
|
+
L: 194,
|
|
216
|
+
S: this
|
|
217
|
+
});
|
|
218
|
+
this._handleMessage(message).catch((err) => log.catch(err, void 0, {
|
|
219
|
+
"~LogMeta": "~LogMeta",
|
|
220
|
+
F: __dxlog_file$1,
|
|
221
|
+
L: 195,
|
|
222
|
+
S: this
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
const clearDispose = this._ctx.onDispose(unsubscribe);
|
|
227
|
+
let listeners;
|
|
228
|
+
if (!payloadType) {
|
|
229
|
+
listeners = this._defaultListeners.get(peerKey);
|
|
230
|
+
if (!listeners) {
|
|
231
|
+
listeners = /* @__PURE__ */ new Set();
|
|
232
|
+
this._defaultListeners.set(peerKey, listeners);
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
listeners = this._listeners.get({
|
|
236
|
+
peerId: peerKey,
|
|
237
|
+
payloadType
|
|
238
|
+
});
|
|
239
|
+
if (!listeners) {
|
|
240
|
+
listeners = /* @__PURE__ */ new Set();
|
|
241
|
+
this._listeners.set({
|
|
242
|
+
peerId: peerKey,
|
|
243
|
+
payloadType
|
|
244
|
+
}, listeners);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
listeners.add(onMessage);
|
|
248
|
+
return { unsubscribe: async () => {
|
|
249
|
+
clearDispose();
|
|
250
|
+
listeners.delete(onMessage);
|
|
251
|
+
await unsubscribe();
|
|
252
|
+
} };
|
|
253
|
+
}
|
|
254
|
+
async _encodeAndSend(ctx, { author, recipient, reliablePayload }) {
|
|
255
|
+
await this._signalManager.sendMessage(ctx, {
|
|
256
|
+
author,
|
|
257
|
+
recipient,
|
|
258
|
+
payload: {
|
|
259
|
+
type_url: "dxos.mesh.messaging.ReliablePayload",
|
|
260
|
+
value: ReliablePayload.encode(reliablePayload, { preserveAny: true })
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
async _handleMessage(message) {
|
|
265
|
+
switch (message.payload.type_url) {
|
|
266
|
+
case "dxos.mesh.messaging.ReliablePayload":
|
|
267
|
+
await this._handleReliablePayload(message);
|
|
268
|
+
break;
|
|
269
|
+
case "dxos.mesh.messaging.Acknowledgement":
|
|
270
|
+
await this._handleAcknowledgement({ payload: message.payload });
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
async _handleReliablePayload(message) {
|
|
275
|
+
const { author, recipient, payload } = message;
|
|
276
|
+
invariant(payload.type_url === "dxos.mesh.messaging.ReliablePayload", void 0, {
|
|
277
|
+
"~LogMeta": "~LogMeta",
|
|
278
|
+
F: __dxlog_file$1,
|
|
279
|
+
L: 263,
|
|
280
|
+
S: this,
|
|
281
|
+
A: ["payload.type_url === 'dxos.mesh.messaging.ReliablePayload'", ""]
|
|
282
|
+
});
|
|
283
|
+
invariant(recipient, "Recipient is required", {
|
|
284
|
+
"~LogMeta": "~LogMeta",
|
|
285
|
+
F: __dxlog_file$1,
|
|
286
|
+
L: 264,
|
|
287
|
+
S: this,
|
|
288
|
+
A: ["recipient", "'Recipient is required'"]
|
|
289
|
+
});
|
|
290
|
+
const reliablePayload = ReliablePayload.decode(payload.value, { preserveAny: true });
|
|
291
|
+
log("handling message", { messageId: reliablePayload.messageId }, {
|
|
292
|
+
"~LogMeta": "~LogMeta",
|
|
293
|
+
F: __dxlog_file$1,
|
|
294
|
+
L: 267,
|
|
295
|
+
S: this
|
|
296
|
+
});
|
|
297
|
+
try {
|
|
298
|
+
await this._sendAcknowledgement(this._ctx, {
|
|
299
|
+
author,
|
|
300
|
+
recipient,
|
|
301
|
+
messageId: reliablePayload.messageId
|
|
302
|
+
});
|
|
303
|
+
} catch (err) {
|
|
304
|
+
this._monitor.recordMessageAckFailed();
|
|
305
|
+
throw err;
|
|
306
|
+
}
|
|
307
|
+
if (this._receivedMessages.has(reliablePayload.messageId)) return;
|
|
308
|
+
this._receivedMessages.add(reliablePayload.messageId);
|
|
309
|
+
await this._callListeners({
|
|
310
|
+
author,
|
|
311
|
+
recipient,
|
|
312
|
+
payload: reliablePayload.payload
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
async _handleAcknowledgement({ payload }) {
|
|
316
|
+
invariant(payload.type_url === "dxos.mesh.messaging.Acknowledgement", void 0, {
|
|
317
|
+
"~LogMeta": "~LogMeta",
|
|
318
|
+
F: __dxlog_file$1,
|
|
319
|
+
L: 295,
|
|
320
|
+
S: this,
|
|
321
|
+
A: ["payload.type_url === 'dxos.mesh.messaging.Acknowledgement'", ""]
|
|
322
|
+
});
|
|
323
|
+
this._onAckCallbacks.get(Acknowledgement.decode(payload.value).messageId)?.();
|
|
324
|
+
}
|
|
325
|
+
async _sendAcknowledgement(ctx, { author, recipient, messageId }) {
|
|
326
|
+
log("sending ACK", {
|
|
327
|
+
messageId,
|
|
328
|
+
from: recipient,
|
|
329
|
+
to: author
|
|
330
|
+
}, {
|
|
331
|
+
"~LogMeta": "~LogMeta",
|
|
332
|
+
F: __dxlog_file$1,
|
|
333
|
+
L: 311,
|
|
334
|
+
S: this
|
|
335
|
+
});
|
|
336
|
+
await this._signalManager.sendMessage(ctx, {
|
|
337
|
+
author: recipient,
|
|
338
|
+
recipient: author,
|
|
339
|
+
payload: {
|
|
340
|
+
type_url: "dxos.mesh.messaging.Acknowledgement",
|
|
341
|
+
value: Acknowledgement.encode({ messageId })
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
async _callListeners(message) {
|
|
346
|
+
const { recipient } = message;
|
|
347
|
+
invariant(recipient?.peerKey, "Peer key is required", {
|
|
348
|
+
"~LogMeta": "~LogMeta",
|
|
349
|
+
F: __dxlog_file$1,
|
|
350
|
+
L: 325,
|
|
351
|
+
S: this,
|
|
352
|
+
A: ["recipient?.peerKey", "'Peer key is required'"]
|
|
353
|
+
});
|
|
354
|
+
const peerKey = recipient.peerKey;
|
|
355
|
+
{
|
|
356
|
+
const defaultListenerMap = this._defaultListeners.get(peerKey);
|
|
357
|
+
if (defaultListenerMap) for (const listener of defaultListenerMap) await listener(message);
|
|
358
|
+
}
|
|
359
|
+
{
|
|
360
|
+
const listenerMap = this._listeners.get({
|
|
361
|
+
peerId: peerKey,
|
|
362
|
+
payloadType: message.payload.type_url
|
|
363
|
+
});
|
|
364
|
+
if (listenerMap) for (const listener of listenerMap) await listener(message);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
_performGc() {
|
|
368
|
+
const start = performance.now();
|
|
369
|
+
for (const key of this._toClear.keys()) this._receivedMessages.delete(key);
|
|
370
|
+
this._toClear.clear();
|
|
371
|
+
for (const key of this._receivedMessages.keys()) this._toClear.add(key);
|
|
372
|
+
const elapsed = performance.now() - start;
|
|
373
|
+
if (elapsed > 100) log.warn("GC took too long", { elapsed }, {
|
|
374
|
+
"~LogMeta": "~LogMeta",
|
|
375
|
+
F: __dxlog_file$1,
|
|
376
|
+
L: 362,
|
|
377
|
+
S: this
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
//#endregion
|
|
382
|
+
//#region src/signal-methods.ts
|
|
383
|
+
var PeerInfoHash = ({ peerKey }) => peerKey;
|
|
384
|
+
//#endregion
|
|
385
|
+
//#region src/signal-manager/memory-signal-manager.ts
|
|
386
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/core/mesh/messaging/src/signal-manager/memory-signal-manager.ts";
|
|
387
|
+
/**
|
|
388
|
+
* Common signaling context that connects multiple MemorySignalManager instances.
|
|
389
|
+
*/
|
|
390
|
+
var MemorySignalManagerContext = class {
|
|
391
|
+
swarmEvent = new Event();
|
|
392
|
+
swarms = new ComplexMap(PublicKey.hash);
|
|
393
|
+
connections = new ComplexMap(PeerInfoHash);
|
|
394
|
+
};
|
|
395
|
+
/**
|
|
396
|
+
* In memory signal manager for testing.
|
|
397
|
+
*/
|
|
398
|
+
var MemorySignalManager = class {
|
|
399
|
+
_context;
|
|
400
|
+
statusChanged = new Event();
|
|
401
|
+
swarmEvent = new Event();
|
|
402
|
+
/**
|
|
403
|
+
* Active message subscriptions on this manager. Routing is encapsulated here (DX-1125): a delivered
|
|
404
|
+
* message is dispatched to every subscription it matches.
|
|
405
|
+
*/
|
|
406
|
+
_subscriptions = /* @__PURE__ */ new Set();
|
|
407
|
+
/** Will be used to emit SwarmEvents on .open() and .close() */
|
|
408
|
+
_joinedSwarms = new ComplexSet(({ topic, peer }) => topic.toHex() + peer.peerKey);
|
|
409
|
+
_ctx;
|
|
410
|
+
_freezeTrigger = new Trigger().wake();
|
|
411
|
+
constructor(_context) {
|
|
412
|
+
this._context = _context;
|
|
413
|
+
this._ctx = new Context(void 0, {
|
|
414
|
+
"~LogMeta": "~LogMeta",
|
|
415
|
+
F: __dxlog_file,
|
|
416
|
+
L: 75
|
|
417
|
+
});
|
|
418
|
+
this._ctx.onDispose(this._context.swarmEvent.on((data) => this.swarmEvent.emit(data)));
|
|
419
|
+
}
|
|
420
|
+
async open() {
|
|
421
|
+
if (!this._ctx.disposed) return;
|
|
422
|
+
this._ctx = new Context(void 0, {
|
|
423
|
+
"~LogMeta": "~LogMeta",
|
|
424
|
+
F: __dxlog_file,
|
|
425
|
+
L: 84
|
|
426
|
+
});
|
|
427
|
+
this._ctx.onDispose(this._context.swarmEvent.on((data) => this.swarmEvent.emit(data)));
|
|
428
|
+
await Promise.all([...this._joinedSwarms.values()].map((value) => this.join(this._ctx, value)));
|
|
429
|
+
}
|
|
430
|
+
async close() {
|
|
431
|
+
if (this._ctx.disposed) return;
|
|
432
|
+
const joinedSwarmsCopy = new ComplexSet(({ topic, peer }) => topic.toHex() + peer.peerKey, [...this._joinedSwarms.values()]);
|
|
433
|
+
await Promise.all([...this._joinedSwarms.values()].map((value) => this.leave(this._ctx, value)));
|
|
434
|
+
this._joinedSwarms = joinedSwarmsCopy;
|
|
435
|
+
await this._ctx.dispose();
|
|
436
|
+
}
|
|
437
|
+
getStatus() {
|
|
438
|
+
return [];
|
|
439
|
+
}
|
|
440
|
+
async join(_ctx, { topic, peer }) {
|
|
441
|
+
invariant(!this._ctx.disposed, "Closed", {
|
|
442
|
+
"~LogMeta": "~LogMeta",
|
|
443
|
+
F: __dxlog_file,
|
|
444
|
+
L: 113,
|
|
445
|
+
S: this,
|
|
446
|
+
A: ["!this._ctx.disposed", "'Closed'"]
|
|
447
|
+
});
|
|
448
|
+
this._joinedSwarms.add({
|
|
449
|
+
topic,
|
|
450
|
+
peer
|
|
451
|
+
});
|
|
452
|
+
if (!this._context.swarms.has(topic)) this._context.swarms.set(topic, new ComplexSet(PeerInfoHash));
|
|
453
|
+
this._context.swarms.get(topic).add(peer);
|
|
454
|
+
this._context.swarmEvent.emit({
|
|
455
|
+
topic,
|
|
456
|
+
peerAvailable: {
|
|
457
|
+
peer,
|
|
458
|
+
since: /* @__PURE__ */ new Date()
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
for (const [topic, peers] of this._context.swarms) Array.from(peers).forEach((peer) => {
|
|
462
|
+
this.swarmEvent.emit({
|
|
463
|
+
topic,
|
|
464
|
+
peerAvailable: {
|
|
465
|
+
peer,
|
|
466
|
+
since: /* @__PURE__ */ new Date()
|
|
467
|
+
}
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
async leave(_ctx, { topic, peer }) {
|
|
472
|
+
invariant(!this._ctx.disposed, "Closed", {
|
|
473
|
+
"~LogMeta": "~LogMeta",
|
|
474
|
+
F: __dxlog_file,
|
|
475
|
+
L: 145,
|
|
476
|
+
S: this,
|
|
477
|
+
A: ["!this._ctx.disposed", "'Closed'"]
|
|
478
|
+
});
|
|
479
|
+
this._joinedSwarms.delete({
|
|
480
|
+
topic,
|
|
481
|
+
peer
|
|
482
|
+
});
|
|
483
|
+
if (!this._context.swarms.has(topic)) this._context.swarms.set(topic, new ComplexSet(PeerInfoHash));
|
|
484
|
+
this._context.swarms.get(topic).delete(peer);
|
|
485
|
+
const swarmEvent = {
|
|
486
|
+
topic,
|
|
487
|
+
peerLeft: { peer }
|
|
488
|
+
};
|
|
489
|
+
this._context.swarmEvent.emit(swarmEvent);
|
|
490
|
+
}
|
|
491
|
+
async query(_ctx, request) {
|
|
492
|
+
throw new Error("Not implemented");
|
|
493
|
+
}
|
|
494
|
+
async sendMessage(_ctx, message) {
|
|
495
|
+
invariant(!this._ctx.disposed, "Closed", {
|
|
496
|
+
"~LogMeta": "~LogMeta",
|
|
497
|
+
F: __dxlog_file,
|
|
498
|
+
L: 170,
|
|
499
|
+
S: this,
|
|
500
|
+
A: ["!this._ctx.disposed", "'Closed'"]
|
|
501
|
+
});
|
|
502
|
+
const { author, recipient, tags, payload } = message;
|
|
503
|
+
invariant(recipient == null !== !tags?.length, "Exactly one of `recipient` or `tags` must be set", {
|
|
504
|
+
"~LogMeta": "~LogMeta",
|
|
505
|
+
F: __dxlog_file,
|
|
506
|
+
L: 173,
|
|
507
|
+
S: this,
|
|
508
|
+
A: ["(recipient == null) !== !tags?.length", "'Exactly one of `recipient` or `tags` must be set'"]
|
|
509
|
+
});
|
|
510
|
+
await this._freezeTrigger.wait();
|
|
511
|
+
if (recipient != null) {
|
|
512
|
+
log("send message", {
|
|
513
|
+
author,
|
|
514
|
+
recipient,
|
|
515
|
+
...dec(payload)
|
|
516
|
+
}, {
|
|
517
|
+
"~LogMeta": "~LogMeta",
|
|
518
|
+
F: __dxlog_file,
|
|
519
|
+
L: 178,
|
|
520
|
+
S: this
|
|
521
|
+
});
|
|
522
|
+
const remote = this._context.connections.get(recipient);
|
|
523
|
+
if (!remote) {
|
|
524
|
+
log.warn("recipient is not subscribed for messages", {
|
|
525
|
+
author,
|
|
526
|
+
recipient
|
|
527
|
+
}, {
|
|
528
|
+
"~LogMeta": "~LogMeta",
|
|
529
|
+
F: __dxlog_file,
|
|
530
|
+
L: 181,
|
|
531
|
+
S: this
|
|
532
|
+
});
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
remote._deliver(message);
|
|
536
|
+
} else {
|
|
537
|
+
log("broadcast message", {
|
|
538
|
+
author,
|
|
539
|
+
tags,
|
|
540
|
+
...dec(payload)
|
|
541
|
+
}, {
|
|
542
|
+
"~LogMeta": "~LogMeta",
|
|
543
|
+
F: __dxlog_file,
|
|
544
|
+
L: 187,
|
|
545
|
+
S: this
|
|
546
|
+
});
|
|
547
|
+
for (const manager of new Set(this._context.connections.values())) manager._deliver(message);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
async subscribeMessages({ peer, tags = [], onMessage }) {
|
|
551
|
+
invariant(!this._ctx.disposed, "Closed", {
|
|
552
|
+
"~LogMeta": "~LogMeta",
|
|
553
|
+
F: __dxlog_file,
|
|
554
|
+
L: 195,
|
|
555
|
+
S: this,
|
|
556
|
+
A: ["!this._ctx.disposed", "'Closed'"]
|
|
557
|
+
});
|
|
558
|
+
log("subscribing", {
|
|
559
|
+
peer,
|
|
560
|
+
tags
|
|
561
|
+
}, {
|
|
562
|
+
"~LogMeta": "~LogMeta",
|
|
563
|
+
F: __dxlog_file,
|
|
564
|
+
L: 196,
|
|
565
|
+
S: this
|
|
566
|
+
});
|
|
567
|
+
const subscription = {
|
|
568
|
+
peerKey: peer.peerKey,
|
|
569
|
+
tags: new Set(tags),
|
|
570
|
+
onMessage
|
|
571
|
+
};
|
|
572
|
+
this._subscriptions.add(subscription);
|
|
573
|
+
this._context.connections.set(peer, this);
|
|
574
|
+
return async () => {
|
|
575
|
+
log("unsubscribing", {
|
|
576
|
+
peer,
|
|
577
|
+
tags
|
|
578
|
+
}, {
|
|
579
|
+
"~LogMeta": "~LogMeta",
|
|
580
|
+
F: __dxlog_file,
|
|
581
|
+
L: 202,
|
|
582
|
+
S: this
|
|
583
|
+
});
|
|
584
|
+
this._subscriptions.delete(subscription);
|
|
585
|
+
if (![...this._subscriptions].some((sub) => sub.peerKey === peer.peerKey)) this._context.connections.delete(peer);
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
freeze() {
|
|
589
|
+
this._freezeTrigger.reset();
|
|
590
|
+
}
|
|
591
|
+
unfreeze() {
|
|
592
|
+
this._freezeTrigger.wake();
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Route a delivered message to this manager's matching subscriptions once it is unfrozen and open.
|
|
596
|
+
*/
|
|
597
|
+
_deliver(message) {
|
|
598
|
+
if (this._ctx.disposed) {
|
|
599
|
+
log.warn("recipient is disposed", { message }, {
|
|
600
|
+
"~LogMeta": "~LogMeta",
|
|
601
|
+
F: __dxlog_file,
|
|
602
|
+
L: 224,
|
|
603
|
+
S: this
|
|
604
|
+
});
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
this._freezeTrigger.wait().then(() => {
|
|
608
|
+
if (this._ctx.disposed) {
|
|
609
|
+
log.warn("recipient is disposed", { message }, {
|
|
610
|
+
"~LogMeta": "~LogMeta",
|
|
611
|
+
F: __dxlog_file,
|
|
612
|
+
L: 232,
|
|
613
|
+
S: this
|
|
614
|
+
});
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
log("receive message", {
|
|
618
|
+
author: message.author,
|
|
619
|
+
recipient: message.recipient,
|
|
620
|
+
...dec(message.payload)
|
|
621
|
+
}, {
|
|
622
|
+
"~LogMeta": "~LogMeta",
|
|
623
|
+
F: __dxlog_file,
|
|
624
|
+
L: 236,
|
|
625
|
+
S: this
|
|
626
|
+
});
|
|
627
|
+
for (const subscription of this._subscriptions) if (message.recipient != null) {
|
|
628
|
+
if (subscription.peerKey === message.recipient.peerKey) subscription.onMessage(message);
|
|
629
|
+
} else if (message.tags?.some((tag) => subscription.tags.has(tag))) subscription.onMessage(message);
|
|
630
|
+
}).catch((err) => {
|
|
631
|
+
log.error("error while waiting for freeze", { err }, {
|
|
632
|
+
"~LogMeta": "~LogMeta",
|
|
633
|
+
F: __dxlog_file,
|
|
634
|
+
L: 248,
|
|
635
|
+
S: this
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
var dec = (payload) => {
|
|
641
|
+
if (!payload.type_url.endsWith("ReliablePayload")) return {};
|
|
642
|
+
const relPayload = schema.getCodecForType("dxos.mesh.messaging.ReliablePayload").decode(payload.value);
|
|
643
|
+
if (typeof relPayload?.payload?.data === "object") return {
|
|
644
|
+
payload: Object.keys(relPayload?.payload?.data)[0],
|
|
645
|
+
sessionId: relPayload?.payload?.sessionId
|
|
646
|
+
};
|
|
647
|
+
return {};
|
|
648
|
+
};
|
|
649
|
+
//#endregion
|
|
650
|
+
export { Messenger as i, MemorySignalManagerContext as n, PeerInfoHash as r, MemorySignalManager as t };
|
|
651
|
+
|
|
652
|
+
//# sourceMappingURL=chunk-memory-signal-manager.mjs.map
|