@waku/sds 0.0.4-383e0b2.0 → 0.0.4-4c18ca2.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/bundle/index.js +1269 -208
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/message_channel/command_queue.d.ts +1 -1
- package/dist/message_channel/message_channel.d.ts +64 -28
- package/dist/message_channel/message_channel.js +194 -139
- package/dist/message_channel/message_channel.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +13 -1
- package/src/message_channel/command_queue.ts +1 -2
- package/src/message_channel/message_channel.ts +221 -165
@@ -1,6 +1,7 @@
|
|
1
1
|
import { TypedEventEmitter } from "@libp2p/interface";
|
2
2
|
import { sha256 } from "@noble/hashes/sha256";
|
3
3
|
import { bytesToHex } from "@noble/hashes/utils";
|
4
|
+
import { Logger } from "@waku/utils";
|
4
5
|
|
5
6
|
import { DefaultBloomFilter } from "../bloom_filter/bloom.js";
|
6
7
|
|
@@ -21,6 +22,8 @@ export const DEFAULT_BLOOM_FILTER_OPTIONS = {
|
|
21
22
|
const DEFAULT_CAUSAL_HISTORY_SIZE = 2;
|
22
23
|
const DEFAULT_RECEIVED_MESSAGE_TIMEOUT = 1000 * 60 * 5; // 5 minutes
|
23
24
|
|
25
|
+
const log = new Logger("sds:message-channel");
|
26
|
+
|
24
27
|
interface MessageChannelOptions {
|
25
28
|
causalHistorySize?: number;
|
26
29
|
receivedMessageTimeoutEnabled?: boolean;
|
@@ -28,13 +31,13 @@ interface MessageChannelOptions {
|
|
28
31
|
}
|
29
32
|
|
30
33
|
export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
34
|
+
public readonly channelId: ChannelId;
|
31
35
|
private lamportTimestamp: number;
|
32
36
|
private filter: DefaultBloomFilter;
|
33
37
|
private outgoingBuffer: Message[];
|
34
38
|
private acknowledgements: Map<string, number>;
|
35
39
|
private incomingBuffer: Message[];
|
36
40
|
private localHistory: { timestamp: number; historyEntry: HistoryEntry }[];
|
37
|
-
public channelId: ChannelId;
|
38
41
|
private causalHistorySize: number;
|
39
42
|
private acknowledgementCount: number;
|
40
43
|
private timeReceived: Map<string, number>;
|
@@ -82,8 +85,30 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
82
85
|
options.receivedMessageTimeout ?? DEFAULT_RECEIVED_MESSAGE_TIMEOUT;
|
83
86
|
}
|
84
87
|
|
85
|
-
|
86
|
-
|
88
|
+
public static getMessageId(payload: Uint8Array): string {
|
89
|
+
return bytesToHex(sha256(payload));
|
90
|
+
}
|
91
|
+
|
92
|
+
/**
|
93
|
+
* Processes all queued tasks sequentially to ensure proper message ordering.
|
94
|
+
*
|
95
|
+
* This method should be called periodically by the library consumer to execute
|
96
|
+
* queued send/receive operations in the correct sequence.
|
97
|
+
*
|
98
|
+
* @example
|
99
|
+
* ```typescript
|
100
|
+
* const channel = new MessageChannel("my-channel");
|
101
|
+
*
|
102
|
+
* // Queue some operations
|
103
|
+
* await channel.sendMessage(payload, callback);
|
104
|
+
* channel.receiveMessage(incomingMessage);
|
105
|
+
*
|
106
|
+
* // Process all queued operations
|
107
|
+
* await channel.processTasks();
|
108
|
+
* ```
|
109
|
+
*
|
110
|
+
* @throws Will emit a 'taskError' event if any task fails, but continues processing remaining tasks
|
111
|
+
*/
|
87
112
|
public async processTasks(): Promise<void> {
|
88
113
|
while (this.tasks.length > 0) {
|
89
114
|
const item = this.tasks.shift();
|
@@ -91,35 +116,33 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
91
116
|
continue;
|
92
117
|
}
|
93
118
|
|
94
|
-
// Use a generic helper function to ensure type safety
|
95
119
|
await this.executeTask(item);
|
96
120
|
}
|
97
121
|
}
|
98
122
|
|
99
|
-
private async executeTask<A extends Command>(item: Task<A>): Promise<void> {
|
100
|
-
const handler = this.handlers[item.command];
|
101
|
-
await handler(item.params as ParamsByAction[A]);
|
102
|
-
}
|
103
|
-
|
104
|
-
public static getMessageId(payload: Uint8Array): string {
|
105
|
-
return bytesToHex(sha256(payload));
|
106
|
-
}
|
107
|
-
|
108
123
|
/**
|
109
|
-
*
|
124
|
+
* Queues a message to be sent on this channel.
|
110
125
|
*
|
111
|
-
*
|
112
|
-
*
|
126
|
+
* The message will be processed sequentially when processTasks() is called.
|
127
|
+
* This ensures proper lamport timestamp ordering and causal history tracking.
|
113
128
|
*
|
114
|
-
*
|
115
|
-
*
|
116
|
-
*
|
117
|
-
* light push or relay.
|
129
|
+
* @param payload - The message content as a byte array
|
130
|
+
* @param callback - Optional callback function called after the message is processed
|
131
|
+
* @returns Promise that resolves when the message is queued (not sent)
|
118
132
|
*
|
119
|
-
*
|
133
|
+
* @example
|
134
|
+
* ```typescript
|
135
|
+
* const channel = new MessageChannel("chat-room");
|
136
|
+
* const message = new TextEncoder().encode("Hello, world!");
|
120
137
|
*
|
121
|
-
*
|
122
|
-
*
|
138
|
+
* await channel.sendMessage(message, async (processedMessage) => {
|
139
|
+
* console.log("Message processed:", processedMessage.messageId);
|
140
|
+
* return { success: true };
|
141
|
+
* });
|
142
|
+
*
|
143
|
+
* // Actually send the message
|
144
|
+
* await channel.processTasks();
|
145
|
+
* ```
|
123
146
|
*/
|
124
147
|
public async sendMessage(
|
125
148
|
payload: Uint8Array,
|
@@ -137,49 +160,6 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
137
160
|
});
|
138
161
|
}
|
139
162
|
|
140
|
-
public async _sendMessage(
|
141
|
-
payload: Uint8Array,
|
142
|
-
callback?: (message: Message) => Promise<{
|
143
|
-
success: boolean;
|
144
|
-
retrievalHint?: Uint8Array;
|
145
|
-
}>
|
146
|
-
): Promise<void> {
|
147
|
-
this.lamportTimestamp++;
|
148
|
-
|
149
|
-
const messageId = MessageChannel.getMessageId(payload);
|
150
|
-
|
151
|
-
const message: Message = {
|
152
|
-
messageId,
|
153
|
-
channelId: this.channelId,
|
154
|
-
lamportTimestamp: this.lamportTimestamp,
|
155
|
-
causalHistory: this.localHistory
|
156
|
-
.slice(-this.causalHistorySize)
|
157
|
-
.map(({ historyEntry }) => historyEntry),
|
158
|
-
bloomFilter: this.filter.toBytes(),
|
159
|
-
content: payload
|
160
|
-
};
|
161
|
-
|
162
|
-
this.outgoingBuffer.push(message);
|
163
|
-
|
164
|
-
if (callback) {
|
165
|
-
const { success, retrievalHint } = await callback(message);
|
166
|
-
if (success) {
|
167
|
-
this.filter.insert(messageId);
|
168
|
-
this.localHistory.push({
|
169
|
-
timestamp: this.lamportTimestamp,
|
170
|
-
historyEntry: {
|
171
|
-
messageId,
|
172
|
-
retrievalHint
|
173
|
-
}
|
174
|
-
});
|
175
|
-
this.timeReceived.set(messageId, Date.now());
|
176
|
-
this.safeDispatchEvent(MessageChannelEvent.MessageSent, {
|
177
|
-
detail: message
|
178
|
-
});
|
179
|
-
}
|
180
|
-
}
|
181
|
-
}
|
182
|
-
|
183
163
|
/**
|
184
164
|
* Sends a short-lived message without synchronization or reliability requirements.
|
185
165
|
*
|
@@ -206,39 +186,25 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
206
186
|
});
|
207
187
|
}
|
208
188
|
|
209
|
-
public async _sendEphemeralMessage(
|
210
|
-
payload: Uint8Array,
|
211
|
-
callback?: (message: Message) => Promise<boolean>
|
212
|
-
): Promise<void> {
|
213
|
-
const message: Message = {
|
214
|
-
messageId: MessageChannel.getMessageId(payload),
|
215
|
-
channelId: this.channelId,
|
216
|
-
content: payload,
|
217
|
-
lamportTimestamp: undefined,
|
218
|
-
causalHistory: [],
|
219
|
-
bloomFilter: undefined
|
220
|
-
};
|
221
|
-
|
222
|
-
if (callback) {
|
223
|
-
await callback(message);
|
224
|
-
}
|
225
|
-
}
|
226
|
-
|
227
189
|
/**
|
228
|
-
*
|
190
|
+
* Queues a received message for processing.
|
191
|
+
*
|
192
|
+
* The message will be processed when processTasks() is called, ensuring
|
193
|
+
* proper dependency resolution and causal ordering.
|
229
194
|
*
|
230
|
-
*
|
231
|
-
* by inspecting the received message's bloom filter and causal history.
|
232
|
-
* Add the received message to the bloom filter.
|
233
|
-
* If the local history contains every message in the received message's
|
234
|
-
* causal history, deliver the message. Otherwise, add the message to the
|
235
|
-
* incoming buffer.
|
195
|
+
* @param message - The message to receive and process
|
236
196
|
*
|
237
|
-
*
|
197
|
+
* @example
|
198
|
+
* ```typescript
|
199
|
+
* const channel = new MessageChannel("chat-room");
|
238
200
|
*
|
239
|
-
*
|
201
|
+
* // Receive a message from the network
|
202
|
+
* channel.receiveMessage(incomingMessage);
|
203
|
+
*
|
204
|
+
* // Process the received message
|
205
|
+
* await channel.processTasks();
|
206
|
+
* ```
|
240
207
|
*/
|
241
|
-
|
242
208
|
public receiveMessage(message: Message): void {
|
243
209
|
this.tasks.push({
|
244
210
|
command: Command.Receive,
|
@@ -248,67 +214,17 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
248
214
|
});
|
249
215
|
}
|
250
216
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
) {
|
257
|
-
// Received a duplicate message
|
258
|
-
return;
|
259
|
-
}
|
260
|
-
|
261
|
-
if (!message.lamportTimestamp) {
|
262
|
-
// Messages with no timestamp are ephemeral messages and should be delivered immediately
|
263
|
-
this.deliverMessage(message);
|
264
|
-
return;
|
265
|
-
}
|
266
|
-
if (message.content?.length === 0) {
|
267
|
-
this.safeDispatchEvent(MessageChannelEvent.SyncReceived, {
|
268
|
-
detail: message
|
269
|
-
});
|
270
|
-
} else {
|
271
|
-
this.safeDispatchEvent(MessageChannelEvent.MessageReceived, {
|
272
|
-
detail: message
|
273
|
-
});
|
274
|
-
}
|
275
|
-
// review ack status
|
276
|
-
this.reviewAckStatus(message);
|
277
|
-
// add to bloom filter (skip for messages with empty content)
|
278
|
-
if (message.content?.length && message.content.length > 0) {
|
279
|
-
this.filter.insert(message.messageId);
|
280
|
-
}
|
281
|
-
// verify causal history
|
282
|
-
const dependenciesMet = message.causalHistory.every((historyEntry) =>
|
283
|
-
this.localHistory.some(
|
284
|
-
({ historyEntry: { messageId } }) =>
|
285
|
-
messageId === historyEntry.messageId
|
286
|
-
)
|
287
|
-
);
|
288
|
-
if (!dependenciesMet) {
|
289
|
-
this.incomingBuffer.push(message);
|
290
|
-
this.timeReceived.set(message.messageId, Date.now());
|
291
|
-
} else {
|
292
|
-
this.deliverMessage(message);
|
293
|
-
this.safeDispatchEvent(MessageChannelEvent.MessageDelivered, {
|
294
|
-
detail: {
|
295
|
-
messageId: message.messageId,
|
296
|
-
sentOrReceived: "received"
|
297
|
-
}
|
298
|
-
});
|
299
|
-
}
|
300
|
-
}
|
301
|
-
|
302
|
-
// https://rfc.vac.dev/vac/raw/sds/#periodic-incoming-buffer-sweep
|
303
|
-
// Note that even though this function has side effects, it is not async
|
304
|
-
// and does not need to be called through the queue.
|
217
|
+
/**
|
218
|
+
* Processes messages in the incoming buffer, delivering those with satisfied dependencies.
|
219
|
+
*
|
220
|
+
* @returns Array of history entries for messages still missing dependencies
|
221
|
+
*/
|
305
222
|
public sweepIncomingBuffer(): HistoryEntry[] {
|
306
223
|
const { buffer, missing } = this.incomingBuffer.reduce<{
|
307
224
|
buffer: Message[];
|
308
225
|
missing: Set<HistoryEntry>;
|
309
226
|
}>(
|
310
227
|
({ buffer, missing }, message) => {
|
311
|
-
// Check each message for missing dependencies
|
312
228
|
const missingDependencies = message.causalHistory.filter(
|
313
229
|
(messageHistoryEntry) =>
|
314
230
|
!this.localHistory.some(
|
@@ -317,10 +233,8 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
317
233
|
)
|
318
234
|
);
|
319
235
|
if (missingDependencies.length === 0) {
|
320
|
-
// Any message with no missing dependencies is delivered
|
321
|
-
// and removed from the buffer (implicitly by not adding it to the new incoming buffer)
|
322
236
|
this.deliverMessage(message);
|
323
|
-
this.
|
237
|
+
this.safeSendEvent(MessageChannelEvent.MessageDelivered, {
|
324
238
|
detail: {
|
325
239
|
messageId: message.messageId,
|
326
240
|
sentOrReceived: "received"
|
@@ -340,8 +254,6 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
340
254
|
return { buffer, missing };
|
341
255
|
}
|
342
256
|
}
|
343
|
-
// Any message with missing dependencies stays in the buffer
|
344
|
-
// and the missing message IDs are returned for processing.
|
345
257
|
missingDependencies.forEach((dependency) => {
|
346
258
|
missing.add(dependency);
|
347
259
|
});
|
@@ -352,10 +264,9 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
352
264
|
},
|
353
265
|
{ buffer: new Array<Message>(), missing: new Set<HistoryEntry>() }
|
354
266
|
);
|
355
|
-
// Update the incoming buffer to only include messages with no missing dependencies
|
356
267
|
this.incomingBuffer = buffer;
|
357
268
|
|
358
|
-
this.
|
269
|
+
this.safeSendEvent(MessageChannelEvent.MissedMessages, {
|
359
270
|
detail: Array.from(missing)
|
360
271
|
});
|
361
272
|
|
@@ -367,7 +278,6 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
367
278
|
unacknowledged: Message[];
|
368
279
|
possiblyAcknowledged: Message[];
|
369
280
|
} {
|
370
|
-
// Partition all messages in the outgoing buffer into unacknowledged and possibly acknowledged messages
|
371
281
|
return this.outgoingBuffer.reduce<{
|
372
282
|
unacknowledged: Message[];
|
373
283
|
possiblyAcknowledged: Message[];
|
@@ -420,13 +330,161 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
420
330
|
};
|
421
331
|
|
422
332
|
if (callback) {
|
423
|
-
|
424
|
-
|
333
|
+
try {
|
334
|
+
await callback(message);
|
335
|
+
this.safeSendEvent(MessageChannelEvent.SyncSent, {
|
336
|
+
detail: message
|
337
|
+
});
|
338
|
+
return true;
|
339
|
+
} catch (error) {
|
340
|
+
log.error("Callback execution failed in sendSyncMessage:", error);
|
341
|
+
throw error;
|
342
|
+
}
|
343
|
+
}
|
344
|
+
return false;
|
345
|
+
}
|
346
|
+
|
347
|
+
private _receiveMessage(message: Message): void {
|
348
|
+
const isDuplicate =
|
349
|
+
message.content &&
|
350
|
+
message.content.length > 0 &&
|
351
|
+
this.timeReceived.has(message.messageId);
|
352
|
+
|
353
|
+
if (isDuplicate) {
|
354
|
+
return;
|
355
|
+
}
|
356
|
+
|
357
|
+
if (!message.lamportTimestamp) {
|
358
|
+
this.deliverMessage(message);
|
359
|
+
return;
|
360
|
+
}
|
361
|
+
if (message.content?.length === 0) {
|
362
|
+
this.safeSendEvent(MessageChannelEvent.SyncReceived, {
|
363
|
+
detail: message
|
364
|
+
});
|
365
|
+
} else {
|
366
|
+
this.safeSendEvent(MessageChannelEvent.MessageReceived, {
|
425
367
|
detail: message
|
426
368
|
});
|
427
|
-
return true;
|
428
369
|
}
|
429
|
-
|
370
|
+
this.reviewAckStatus(message);
|
371
|
+
if (message.content?.length && message.content.length > 0) {
|
372
|
+
this.filter.insert(message.messageId);
|
373
|
+
}
|
374
|
+
const dependenciesMet = message.causalHistory.every((historyEntry) =>
|
375
|
+
this.localHistory.some(
|
376
|
+
({ historyEntry: { messageId } }) =>
|
377
|
+
messageId === historyEntry.messageId
|
378
|
+
)
|
379
|
+
);
|
380
|
+
if (!dependenciesMet) {
|
381
|
+
this.incomingBuffer.push(message);
|
382
|
+
this.timeReceived.set(message.messageId, Date.now());
|
383
|
+
} else {
|
384
|
+
this.deliverMessage(message);
|
385
|
+
this.safeSendEvent(MessageChannelEvent.MessageDelivered, {
|
386
|
+
detail: {
|
387
|
+
messageId: message.messageId,
|
388
|
+
sentOrReceived: "received"
|
389
|
+
}
|
390
|
+
});
|
391
|
+
}
|
392
|
+
}
|
393
|
+
|
394
|
+
private async executeTask<A extends Command>(item: Task<A>): Promise<void> {
|
395
|
+
try {
|
396
|
+
const handler = this.handlers[item.command];
|
397
|
+
await handler(item.params as ParamsByAction[A]);
|
398
|
+
} catch (error) {
|
399
|
+
log.error(`Task execution failed for command ${item.command}:`, error);
|
400
|
+
this.dispatchEvent(
|
401
|
+
new CustomEvent("taskError", {
|
402
|
+
detail: { command: item.command, error, params: item.params }
|
403
|
+
})
|
404
|
+
);
|
405
|
+
}
|
406
|
+
}
|
407
|
+
|
408
|
+
private safeSendEvent<T extends MessageChannelEvent>(
|
409
|
+
event: T,
|
410
|
+
eventInit?: CustomEventInit
|
411
|
+
): void {
|
412
|
+
try {
|
413
|
+
this.dispatchEvent(new CustomEvent(event, eventInit));
|
414
|
+
} catch (error) {
|
415
|
+
log.error(`Failed to dispatch event ${event}:`, error);
|
416
|
+
}
|
417
|
+
}
|
418
|
+
|
419
|
+
private async _sendMessage(
|
420
|
+
payload: Uint8Array,
|
421
|
+
callback?: (message: Message) => Promise<{
|
422
|
+
success: boolean;
|
423
|
+
retrievalHint?: Uint8Array;
|
424
|
+
}>
|
425
|
+
): Promise<void> {
|
426
|
+
this.lamportTimestamp++;
|
427
|
+
|
428
|
+
const messageId = MessageChannel.getMessageId(payload);
|
429
|
+
|
430
|
+
const message: Message = {
|
431
|
+
messageId,
|
432
|
+
channelId: this.channelId,
|
433
|
+
lamportTimestamp: this.lamportTimestamp,
|
434
|
+
causalHistory: this.localHistory
|
435
|
+
.slice(-this.causalHistorySize)
|
436
|
+
.map(({ historyEntry }) => historyEntry),
|
437
|
+
bloomFilter: this.filter.toBytes(),
|
438
|
+
content: payload
|
439
|
+
};
|
440
|
+
|
441
|
+
this.outgoingBuffer.push(message);
|
442
|
+
|
443
|
+
if (callback) {
|
444
|
+
try {
|
445
|
+
const { success, retrievalHint } = await callback(message);
|
446
|
+
if (success) {
|
447
|
+
this.filter.insert(messageId);
|
448
|
+
this.localHistory.push({
|
449
|
+
timestamp: this.lamportTimestamp,
|
450
|
+
historyEntry: {
|
451
|
+
messageId,
|
452
|
+
retrievalHint
|
453
|
+
}
|
454
|
+
});
|
455
|
+
this.timeReceived.set(messageId, Date.now());
|
456
|
+
this.safeSendEvent(MessageChannelEvent.MessageSent, {
|
457
|
+
detail: message
|
458
|
+
});
|
459
|
+
}
|
460
|
+
} catch (error) {
|
461
|
+
log.error("Callback execution failed in _sendMessage:", error);
|
462
|
+
throw error;
|
463
|
+
}
|
464
|
+
}
|
465
|
+
}
|
466
|
+
|
467
|
+
private async _sendEphemeralMessage(
|
468
|
+
payload: Uint8Array,
|
469
|
+
callback?: (message: Message) => Promise<boolean>
|
470
|
+
): Promise<void> {
|
471
|
+
const message: Message = {
|
472
|
+
messageId: MessageChannel.getMessageId(payload),
|
473
|
+
channelId: this.channelId,
|
474
|
+
content: payload,
|
475
|
+
lamportTimestamp: undefined,
|
476
|
+
causalHistory: [],
|
477
|
+
bloomFilter: undefined
|
478
|
+
};
|
479
|
+
|
480
|
+
if (callback) {
|
481
|
+
try {
|
482
|
+
await callback(message);
|
483
|
+
} catch (error) {
|
484
|
+
log.error("Callback execution failed in _sendEphemeralMessage:", error);
|
485
|
+
throw error;
|
486
|
+
}
|
487
|
+
}
|
430
488
|
}
|
431
489
|
|
432
490
|
// See https://rfc.vac.dev/vac/raw/sds/#deliver-message
|
@@ -470,14 +528,13 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
470
528
|
// to determine the acknowledgement status of messages in the outgoing buffer.
|
471
529
|
// See https://rfc.vac.dev/vac/raw/sds/#review-ack-status
|
472
530
|
private reviewAckStatus(receivedMessage: Message): void {
|
473
|
-
// the participant MUST mark all messages in the received causal_history as acknowledged.
|
474
531
|
receivedMessage.causalHistory.forEach(({ messageId }) => {
|
475
532
|
this.outgoingBuffer = this.outgoingBuffer.filter(
|
476
533
|
({ messageId: outgoingMessageId }) => {
|
477
534
|
if (outgoingMessageId !== messageId) {
|
478
535
|
return true;
|
479
536
|
}
|
480
|
-
this.
|
537
|
+
this.safeSendEvent(MessageChannelEvent.MessageAcknowledged, {
|
481
538
|
detail: messageId
|
482
539
|
});
|
483
540
|
return false;
|
@@ -488,7 +545,6 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
488
545
|
this.filter.insert(messageId);
|
489
546
|
}
|
490
547
|
});
|
491
|
-
// the participant MUST mark all messages included in the bloom_filter as possibly acknowledged
|
492
548
|
if (!receivedMessage.bloomFilter) {
|
493
549
|
return;
|
494
550
|
}
|
@@ -506,7 +562,7 @@ export class MessageChannel extends TypedEventEmitter<MessageChannelEvents> {
|
|
506
562
|
const count = (this.acknowledgements.get(message.messageId) ?? 0) + 1;
|
507
563
|
if (count < this.acknowledgementCount) {
|
508
564
|
this.acknowledgements.set(message.messageId, count);
|
509
|
-
this.
|
565
|
+
this.safeSendEvent(MessageChannelEvent.PartialAcknowledgement, {
|
510
566
|
detail: {
|
511
567
|
messageId: message.messageId,
|
512
568
|
count
|