@agentunion/fastaun-browser 0.5.10 → 0.5.12
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/CHANGELOG.md +62 -0
- package/_packed_docs/CHANGELOG.md +62 -0
- package/_packed_docs/INDEX.md +3 -3
- package/_packed_docs/KITE_DOCS_GUIDE.md +1 -1
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +21 -3
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +59 -0
- package/_packed_docs/sdk/09-group-rpc-manual.md +29 -2
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +3 -2
- package/_packed_docs/sdk/INDEX.md +2 -1
- package/dist/bundle.js +1145 -381
- package/dist/client/delivery.d.ts +12 -4
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +341 -273
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.d.ts.map +1 -1
- package/dist/client/group-state.js +1 -0
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +38 -13
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +3 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +211 -75
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +15 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +402 -20
- package/dist/client.js.map +1 -1
- package/dist/facades.d.ts +7 -0
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +44 -0
- package/dist/facades.js.map +1 -1
- package/dist/seq-tracker.d.ts +2 -2
- package/dist/seq-tracker.d.ts.map +1 -1
- package/dist/seq-tracker.js +11 -7
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.js +50 -12
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +162 -15
- package/dist/transport.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/client/delivery.js
CHANGED
|
@@ -58,11 +58,35 @@ function positiveSafeSequenceHint(value) {
|
|
|
58
58
|
const parsed = Number(value);
|
|
59
59
|
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : 0;
|
|
60
60
|
}
|
|
61
|
+
function hasNonEmptyInlineMessage(value) {
|
|
62
|
+
return isJsonObject(value)
|
|
63
|
+
&& Object.keys(value).length > 0;
|
|
64
|
+
}
|
|
61
65
|
function nonNegativeSafeSequenceHint(value) {
|
|
62
66
|
if (typeof value !== 'number' || !Number.isSafeInteger(value) || value < 0)
|
|
63
67
|
return null;
|
|
64
68
|
return value;
|
|
65
69
|
}
|
|
70
|
+
function canonicalDeliverySource(source) {
|
|
71
|
+
switch (String(source ?? '').trim()) {
|
|
72
|
+
case 'group-push':
|
|
73
|
+
case 'ordered':
|
|
74
|
+
case 'legacy':
|
|
75
|
+
return 'push';
|
|
76
|
+
case 'tail':
|
|
77
|
+
case 'pull-drained':
|
|
78
|
+
case 'pull_drained':
|
|
79
|
+
return 'pull';
|
|
80
|
+
case 'inline-push':
|
|
81
|
+
return 'inline_push';
|
|
82
|
+
case 'pending-retry':
|
|
83
|
+
return 'pending_retry';
|
|
84
|
+
case '':
|
|
85
|
+
return 'direct';
|
|
86
|
+
default:
|
|
87
|
+
return String(source).trim();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
66
90
|
function deduplicatePlainForwardPageMessages(value, afterSeq) {
|
|
67
91
|
if (!Array.isArray(value))
|
|
68
92
|
return [];
|
|
@@ -95,6 +119,9 @@ function p2pAppEventFromPlainPullMessage(message) {
|
|
|
95
119
|
}
|
|
96
120
|
export class MessageDeliveryEngine {
|
|
97
121
|
runtime;
|
|
122
|
+
syncRun = 0;
|
|
123
|
+
syncingNamespaces = new Set();
|
|
124
|
+
syncSessions = new Map();
|
|
98
125
|
pendingPullDeliveryChanges = null;
|
|
99
126
|
deliveryGeneration = 0;
|
|
100
127
|
realtimeTailResults = null;
|
|
@@ -116,6 +143,7 @@ export class MessageDeliveryEngine {
|
|
|
116
143
|
this.runtime = runtime;
|
|
117
144
|
}
|
|
118
145
|
resetInlineAckState() {
|
|
146
|
+
this.syncStopped('aborted');
|
|
119
147
|
this.inlineGeneration += 1;
|
|
120
148
|
this.deliveryGeneration += 1;
|
|
121
149
|
this.pendingPullDeliveryChanges = null;
|
|
@@ -131,6 +159,8 @@ export class MessageDeliveryEngine {
|
|
|
131
159
|
this.pendingP2PInlineAcks = null;
|
|
132
160
|
this.pendingGroupInlineAcks = null;
|
|
133
161
|
this.pendingGroupInlineAckNamespaces = null;
|
|
162
|
+
this.syncingNamespaces.clear();
|
|
163
|
+
this.syncSessions.clear();
|
|
134
164
|
}
|
|
135
165
|
isInlineGenerationCurrent(generation) {
|
|
136
166
|
return generation === this.inlineGeneration;
|
|
@@ -163,6 +193,95 @@ export class MessageDeliveryEngine {
|
|
|
163
193
|
endRealtimeSync(ns) {
|
|
164
194
|
this.realtimeSyncing?.delete(ns);
|
|
165
195
|
}
|
|
196
|
+
onPullStarted(ns) {
|
|
197
|
+
if (!ns || this.syncingNamespaces.has(ns))
|
|
198
|
+
return;
|
|
199
|
+
if (this.syncingNamespaces.size === 0) {
|
|
200
|
+
this.syncRun += 1;
|
|
201
|
+
this.syncSessions.clear();
|
|
202
|
+
this.runtime.client._dispatcher?.enqueue?.('sync.started', {
|
|
203
|
+
run_id: this.syncRun,
|
|
204
|
+
source: 'pull',
|
|
205
|
+
syncing: true,
|
|
206
|
+
namespaces_pending: 1,
|
|
207
|
+
received_total: 0,
|
|
208
|
+
namespace: ns,
|
|
209
|
+
started_at: Date.now(),
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
this.syncingNamespaces.add(ns);
|
|
213
|
+
this.syncSessions.set(ns, { pages: 0, raw: 0, pulled: 0 });
|
|
214
|
+
}
|
|
215
|
+
onPullPage(ns, pulledCount, hasMore, maxSeen, remaining, rawCount = pulledCount, currentSeq, targetSeq) {
|
|
216
|
+
this.onPullStarted(ns);
|
|
217
|
+
if (!ns)
|
|
218
|
+
return;
|
|
219
|
+
const session = this.syncSessions.get(ns) ?? { pages: 0, raw: 0, pulled: 0 };
|
|
220
|
+
session.pages += 1;
|
|
221
|
+
session.raw += Math.max(0, rawCount);
|
|
222
|
+
session.pulled += Math.max(0, pulledCount);
|
|
223
|
+
this.syncSessions.set(ns, session);
|
|
224
|
+
const received = session.pulled;
|
|
225
|
+
const progress = {
|
|
226
|
+
run_id: this.syncRun,
|
|
227
|
+
source: 'pull',
|
|
228
|
+
namespace: ns,
|
|
229
|
+
page: session.pages,
|
|
230
|
+
page_raw_count: Math.max(0, rawCount),
|
|
231
|
+
page_pulled_count: Math.max(0, pulledCount),
|
|
232
|
+
pulled_count: received,
|
|
233
|
+
received_total: received,
|
|
234
|
+
batch_size: Math.max(0, pulledCount),
|
|
235
|
+
estimated_remaining: hasMore ? undefined : 0,
|
|
236
|
+
max_seen: maxSeen,
|
|
237
|
+
has_more: hasMore,
|
|
238
|
+
};
|
|
239
|
+
if (typeof currentSeq === 'number' && Number.isSafeInteger(currentSeq) && currentSeq >= 0) {
|
|
240
|
+
progress.current_seq = currentSeq;
|
|
241
|
+
}
|
|
242
|
+
if (typeof targetSeq === 'number' && Number.isSafeInteger(targetSeq) && targetSeq >= 0) {
|
|
243
|
+
progress.target_seq = targetSeq;
|
|
244
|
+
}
|
|
245
|
+
if (typeof remaining === 'number' && Number.isSafeInteger(remaining) && remaining >= 0) {
|
|
246
|
+
progress.remaining = remaining;
|
|
247
|
+
progress.estimated_remaining = progress.remaining;
|
|
248
|
+
}
|
|
249
|
+
this.runtime.client._dispatcher?.enqueue?.('sync.progress', progress);
|
|
250
|
+
}
|
|
251
|
+
onPullAborted() {
|
|
252
|
+
this.syncStopped('aborted');
|
|
253
|
+
}
|
|
254
|
+
syncStopped(reason = 'pull_drained') {
|
|
255
|
+
if (this.syncingNamespaces.size === 0)
|
|
256
|
+
return;
|
|
257
|
+
const sessions = [...this.syncSessions.entries()];
|
|
258
|
+
const receivedTotal = sessions.reduce((total, [, session]) => total + session.pulled, 0);
|
|
259
|
+
this.syncingNamespaces.clear();
|
|
260
|
+
this.syncSessions.clear();
|
|
261
|
+
const payload = {
|
|
262
|
+
run_id: this.syncRun,
|
|
263
|
+
source: 'pull',
|
|
264
|
+
syncing: false,
|
|
265
|
+
namespaces_pending: 0,
|
|
266
|
+
received_total: receivedTotal,
|
|
267
|
+
reason,
|
|
268
|
+
stopped_at: Date.now(),
|
|
269
|
+
};
|
|
270
|
+
if (sessions.length === 1) {
|
|
271
|
+
const [namespace, session] = sessions[0];
|
|
272
|
+
payload.namespace = namespace;
|
|
273
|
+
payload.pages = session.pages;
|
|
274
|
+
payload.pulled_count = session.pulled;
|
|
275
|
+
}
|
|
276
|
+
else if (sessions.length > 1) {
|
|
277
|
+
payload.namespaces = sessions.map(([namespace]) => namespace);
|
|
278
|
+
payload.pages = sessions.reduce((total, [, session]) => total + session.pages, 0);
|
|
279
|
+
payload.pulled_count = receivedTotal;
|
|
280
|
+
}
|
|
281
|
+
this.runtime.client._dispatcher?.enqueue?.('sync.stopped', {
|
|
282
|
+
...payload,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
166
285
|
hasPendingPull(ns) {
|
|
167
286
|
const pending = ns.startsWith('p2p:') ? this.pendingP2pPullUpper : this.pendingGroupPullUpper;
|
|
168
287
|
return (pending?.get(ns) ?? 0) > 0;
|
|
@@ -176,7 +295,7 @@ export class MessageDeliveryEngine {
|
|
|
176
295
|
const pipeline = this.runtime.client._rpcPipeline;
|
|
177
296
|
if (pipeline?.hasAnyPullActivity?.() === true)
|
|
178
297
|
return;
|
|
179
|
-
void this.flushPullDeliveryChanges();
|
|
298
|
+
void this.flushPullDeliveryChanges().finally(() => this.syncStopped('pull_drained'));
|
|
180
299
|
}
|
|
181
300
|
deliveryChangeNamespace(event, payload, ns = '') {
|
|
182
301
|
if (event === 'message.received')
|
|
@@ -236,7 +355,11 @@ export class MessageDeliveryEngine {
|
|
|
236
355
|
const changes = this.deliveryChangesPayload(batch.changes);
|
|
237
356
|
if (changes.length === 0)
|
|
238
357
|
return;
|
|
239
|
-
this.runtime.client._dispatcher.enqueue('delivery.changed', {
|
|
358
|
+
this.runtime.client._dispatcher.enqueue('delivery.changed', {
|
|
359
|
+
trigger: batch.trigger,
|
|
360
|
+
source: batch.trigger,
|
|
361
|
+
changes,
|
|
362
|
+
});
|
|
240
363
|
}
|
|
241
364
|
async flushPullDeliveryChanges() {
|
|
242
365
|
const generation = this.deliveryGeneration;
|
|
@@ -249,7 +372,11 @@ export class MessageDeliveryEngine {
|
|
|
249
372
|
const changes = this.deliveryChangesPayload(pending);
|
|
250
373
|
if (changes.length === 0)
|
|
251
374
|
return;
|
|
252
|
-
this.runtime.client._dispatcher.enqueue('delivery.changed', {
|
|
375
|
+
this.runtime.client._dispatcher.enqueue('delivery.changed', {
|
|
376
|
+
trigger: 'pull_drained',
|
|
377
|
+
source: 'pull',
|
|
378
|
+
changes,
|
|
379
|
+
});
|
|
253
380
|
}
|
|
254
381
|
recordPendingPull(ns, seq) {
|
|
255
382
|
if (!ns || !Number.isSafeInteger(seq) || seq <= 0)
|
|
@@ -852,20 +979,28 @@ export class MessageDeliveryEngine {
|
|
|
852
979
|
}
|
|
853
980
|
const message = normalizeGroupMentionMode(rawMessage);
|
|
854
981
|
if (this.recallEventFromGroupMessage(message)) {
|
|
855
|
-
if (await this.publishGroupRecallTombstone(groupId, seq, message)) {
|
|
982
|
+
if (await this.publishGroupRecallTombstone(groupId, seq, message, 'pull')) {
|
|
856
983
|
this.ensurePullOperationCurrent();
|
|
857
984
|
this.markPublishedSeq(ns, seq);
|
|
858
985
|
publishedCount += 1;
|
|
859
986
|
}
|
|
860
987
|
}
|
|
988
|
+
else if (this.isSelfSentGroupMessage(message)) {
|
|
989
|
+
this.markPublishedSeq(ns, seq);
|
|
990
|
+
}
|
|
861
991
|
else if (await this.publishPulledMessage('group.message_created', ns, seq, message, false)) {
|
|
862
992
|
this.ensurePullOperationCurrent();
|
|
863
993
|
publishedCount += 1;
|
|
864
994
|
}
|
|
865
995
|
}
|
|
866
996
|
this.ensurePullOperationCurrent();
|
|
867
|
-
|
|
997
|
+
const contiguousSeq = Number.isSafeInteger(result.contiguous_seq)
|
|
998
|
+
? result.contiguous_seq
|
|
999
|
+
: undefined;
|
|
1000
|
+
if (contiguousSeq === undefined)
|
|
868
1001
|
client._seqTracker.onPullResult(ns, messages, afterSeq);
|
|
1002
|
+
else
|
|
1003
|
+
client._seqTracker.onPullResult(ns, messages, afterSeq, contiguousSeq);
|
|
869
1004
|
const commitTarget = Math.max(client._seqTracker.getContiguousSeq(ns), retentionFloor, visibilityFloor, deferredServerCursor);
|
|
870
1005
|
if (commitTarget > client._seqTracker.getContiguousSeq(ns)) {
|
|
871
1006
|
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
@@ -919,6 +1054,9 @@ export class MessageDeliveryEngine {
|
|
|
919
1054
|
this.ensurePullOperationCurrent();
|
|
920
1055
|
await this.confirmPlainForwardAck(ns, ackMethod, pendingAckSeq, groupId);
|
|
921
1056
|
}
|
|
1057
|
+
const remaining = typeof response.remaining === 'number' && Number.isSafeInteger(response.remaining) && response.remaining >= 0
|
|
1058
|
+
? response.remaining : null;
|
|
1059
|
+
this.onPullPage(ns, publishedCount, response.has_more === true, messages.length > 0 ? Number(messages[messages.length - 1].seq ?? 0) : undefined, remaining, messages.length, client._seqTracker.getContiguousSeq(ns), messages.length > 0 ? Number(messages[messages.length - 1].seq ?? 0) : undefined);
|
|
922
1060
|
return { rawCount: messages.length, publishedCount };
|
|
923
1061
|
}
|
|
924
1062
|
enqueueOrderedMessage(ns, event, seq, payload, source = 'push') {
|
|
@@ -941,11 +1079,11 @@ export class MessageDeliveryEngine {
|
|
|
941
1079
|
async publishOrderedQueueItem(ns, event, seq, payload, pullResponse = false, source = 'push', batch) {
|
|
942
1080
|
const client = this.runtime.client;
|
|
943
1081
|
if (event === 'group.changed' && this.isGroupEventNamespace(ns)) {
|
|
944
|
-
await this.publishOrderedGroupChanged(payload);
|
|
1082
|
+
await this.publishOrderedGroupChanged(payload, source);
|
|
945
1083
|
return;
|
|
946
1084
|
}
|
|
947
1085
|
if (event === 'message.recalled') {
|
|
948
|
-
await this.publishMessageRecallTombstone(seq, payload);
|
|
1086
|
+
await this.publishMessageRecallTombstone(seq, payload, source);
|
|
949
1087
|
return;
|
|
950
1088
|
}
|
|
951
1089
|
if (pullResponse) {
|
|
@@ -954,7 +1092,7 @@ export class MessageDeliveryEngine {
|
|
|
954
1092
|
}
|
|
955
1093
|
await client._publishAppEvent(event, payload, source, ns, batch);
|
|
956
1094
|
}
|
|
957
|
-
async publishOrderedGroupChanged(payload) {
|
|
1095
|
+
async publishOrderedGroupChanged(payload, source = 'ordered') {
|
|
958
1096
|
const client = this.runtime.client;
|
|
959
1097
|
if (isJsonObject(payload)) {
|
|
960
1098
|
const eventPayload = payload;
|
|
@@ -966,7 +1104,7 @@ export class MessageDeliveryEngine {
|
|
|
966
1104
|
client._cleanupDissolvedGroup?.(groupId);
|
|
967
1105
|
}
|
|
968
1106
|
}
|
|
969
|
-
await client._publishAppEvent('group.changed', payload);
|
|
1107
|
+
await client._publishAppEvent('group.changed', payload, source);
|
|
970
1108
|
}
|
|
971
1109
|
isInstanceScopedMessageEvent(event) {
|
|
972
1110
|
return event === 'message.received'
|
|
@@ -989,18 +1127,27 @@ export class MessageDeliveryEngine {
|
|
|
989
1127
|
}
|
|
990
1128
|
return result;
|
|
991
1129
|
}
|
|
992
|
-
normalizePublishedMessagePayload(event, payload) {
|
|
1130
|
+
normalizePublishedMessagePayload(event, payload, source = 'direct') {
|
|
1131
|
+
let normalized;
|
|
993
1132
|
if (this.isInstanceScopedMessageEvent(event)) {
|
|
994
1133
|
payload = this.stripInlineInternalFields(payload);
|
|
995
1134
|
if (event === 'group.message_created')
|
|
996
1135
|
payload = normalizeGroupMentionMode(payload);
|
|
997
|
-
|
|
1136
|
+
normalized = this.attachAppMessageEnvelope(this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload)));
|
|
998
1137
|
}
|
|
999
|
-
if (this.isGroupScopedEvent(event)) {
|
|
1138
|
+
else if (this.isGroupScopedEvent(event)) {
|
|
1000
1139
|
payload = this.stripInlineInternalFields(payload);
|
|
1001
|
-
|
|
1140
|
+
normalized = this.attachAppGroupEventEnvelope(this.attachCurrentInstanceContext(payload));
|
|
1141
|
+
}
|
|
1142
|
+
else {
|
|
1143
|
+
normalized = payload;
|
|
1002
1144
|
}
|
|
1003
|
-
|
|
1145
|
+
const canonicalSource = canonicalDeliverySource(source);
|
|
1146
|
+
if (canonicalSource === 'direct' || !isJsonObject(normalized))
|
|
1147
|
+
return normalized;
|
|
1148
|
+
if (!this.isInstanceScopedMessageEvent(event) && !this.isGroupScopedEvent(event))
|
|
1149
|
+
return normalized;
|
|
1150
|
+
return { ...normalized, source: canonicalSource };
|
|
1004
1151
|
}
|
|
1005
1152
|
stripInlineInternalFields(payload) {
|
|
1006
1153
|
if (!isJsonObject(payload))
|
|
@@ -1269,7 +1416,7 @@ export class MessageDeliveryEngine {
|
|
|
1269
1416
|
return `p2p|tombstone:${tombstoneId}`;
|
|
1270
1417
|
return `p2p|unknown:${Date.now()}:${Math.random()}`;
|
|
1271
1418
|
}
|
|
1272
|
-
async publishMessageRecallTombstone(seq, message) {
|
|
1419
|
+
async publishMessageRecallTombstone(seq, message, source = 'push') {
|
|
1273
1420
|
const client = this.runtime.client;
|
|
1274
1421
|
const eventPayload = this.recallEventFromMessage(message);
|
|
1275
1422
|
if (!eventPayload)
|
|
@@ -1290,7 +1437,7 @@ export class MessageDeliveryEngine {
|
|
|
1290
1437
|
for (const [oldKey] of drop)
|
|
1291
1438
|
seen.delete(oldKey);
|
|
1292
1439
|
}
|
|
1293
|
-
await client._publishAppEvent('message.recalled', eventPayload);
|
|
1440
|
+
await client._publishAppEvent('message.recalled', eventPayload, source);
|
|
1294
1441
|
client._clientLog.debug(`message.recalled published: seq=${String(seq)} ids=${JSON.stringify(eventPayload.message_ids)}`);
|
|
1295
1442
|
return true;
|
|
1296
1443
|
}
|
|
@@ -1373,7 +1520,7 @@ export class MessageDeliveryEngine {
|
|
|
1373
1520
|
return `${normalizedGroupId}|tombstone:${tombstoneId}`;
|
|
1374
1521
|
return `${normalizedGroupId}|unknown:${Date.now()}:${Math.random()}`;
|
|
1375
1522
|
}
|
|
1376
|
-
async publishGroupRecallTombstone(groupId, seq, message) {
|
|
1523
|
+
async publishGroupRecallTombstone(groupId, seq, message, source = 'push') {
|
|
1377
1524
|
const client = this.runtime.client;
|
|
1378
1525
|
const eventPayload = this.recallEventFromGroupMessage(message);
|
|
1379
1526
|
if (!eventPayload)
|
|
@@ -1396,16 +1543,12 @@ export class MessageDeliveryEngine {
|
|
|
1396
1543
|
for (const [oldKey] of drop)
|
|
1397
1544
|
seen.delete(oldKey);
|
|
1398
1545
|
}
|
|
1399
|
-
await client._publishAppEvent('group.message_recalled', eventPayload);
|
|
1546
|
+
await client._publishAppEvent('group.message_recalled', eventPayload, source);
|
|
1400
1547
|
client._clientLog.debug(`group.message_recalled published: group=${groupId} seq=${String(seq)} ids=${JSON.stringify(eventPayload.message_ids)}`);
|
|
1401
1548
|
return true;
|
|
1402
1549
|
}
|
|
1403
1550
|
async onRawGroupMessageRecalled(data) {
|
|
1404
|
-
//
|
|
1405
|
-
// tombstone 的 notice_seq;必须像普通群消息 push 一样推进 seqTracker + markPublished +
|
|
1406
|
-
// auto-ack,否则该 seq 在本地 contiguous 序列留洞,后续 pull/reconnect 会重复拉到并
|
|
1407
|
-
// 重复处理。publishGroupRecallTombstone 内部再按 (group_id, message_ids) 去重,
|
|
1408
|
-
// 确保应用层只回调一次。
|
|
1551
|
+
// 非 inline push 仅作为实时通知,不推进 seqTracker/cursor/ACK。
|
|
1409
1552
|
const client = this.runtime.client;
|
|
1410
1553
|
if (!isJsonObject(data))
|
|
1411
1554
|
return;
|
|
@@ -1436,41 +1579,13 @@ export class MessageDeliveryEngine {
|
|
|
1436
1579
|
return;
|
|
1437
1580
|
}
|
|
1438
1581
|
const ns = `group:${groupId}`;
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
await this.publishGroupRecallTombstone(groupId, seq, wrapped);
|
|
1444
|
-
return;
|
|
1445
|
-
}
|
|
1446
|
-
client._repairPushContiguousBound(ns, seqNum, true, '_raw.group.message_recalled');
|
|
1447
|
-
}
|
|
1448
|
-
// 该 notice_seq 已由 pull 路径处理过(已发布或挂起待发布)时,去重发布兜底后返回。
|
|
1449
|
-
const pushed = client._pushedSeqs.get(ns);
|
|
1450
|
-
const pending = client._pendingOrderedMsgs.get(ns);
|
|
1451
|
-
if (pushed?.has(seqNum) || pending?.has(seqNum)) {
|
|
1452
|
-
await this.publishGroupRecallTombstone(groupId, seq, wrapped);
|
|
1453
|
-
return;
|
|
1454
|
-
}
|
|
1455
|
-
const contigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
1456
|
-
client._seqTracker.onMessageSeq(ns, seqNum);
|
|
1582
|
+
client._seqTracker.updateMaxSeen(ns, seqNum);
|
|
1583
|
+
if (seqNum > client._seqTracker.getContiguousSeq(ns)
|
|
1584
|
+
&& client._sessionOptions?.background_sync !== false)
|
|
1585
|
+
client._safeAsync(this.fillGroupGap(groupId));
|
|
1457
1586
|
await this.publishGroupRecallTombstone(groupId, seq, wrapped);
|
|
1458
|
-
this.markPublishedSeq(ns, seqNum);
|
|
1459
|
-
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
1460
|
-
if (contig > 0) {
|
|
1461
|
-
const ackSeq = this.clampAckSeq('group.ack_messages', 'msg_seq', ns, contig);
|
|
1462
|
-
client._transport.call('group.ack_messages', {
|
|
1463
|
-
group_id: groupId,
|
|
1464
|
-
msg_seq: ackSeq,
|
|
1465
|
-
device_id: client._deviceId,
|
|
1466
|
-
slot_id: client._slotId,
|
|
1467
|
-
_rpc_background: true,
|
|
1468
|
-
}).catch((e) => { client._clientLog.warn('group recall auto-ack failed: group=' + groupId, e); });
|
|
1469
|
-
}
|
|
1470
|
-
if (contig !== contigBefore)
|
|
1471
|
-
this.persistSeq(ns);
|
|
1472
1587
|
}
|
|
1473
|
-
async publishAppEvent(event, payload, source = '', ns = '', batch) {
|
|
1588
|
+
async publishAppEvent(event, payload, source = 'direct', ns = '', batch) {
|
|
1474
1589
|
const client = this.runtime.client;
|
|
1475
1590
|
const generation = this.deliveryGeneration;
|
|
1476
1591
|
if ((event === 'message.received' || event === 'group.message_created') && isJsonObject(payload)) {
|
|
@@ -1492,14 +1607,21 @@ export class MessageDeliveryEngine {
|
|
|
1492
1607
|
client._clientLog.debug(`agent_md etag inject skipped: ${String(exc)}`);
|
|
1493
1608
|
}
|
|
1494
1609
|
}
|
|
1495
|
-
|
|
1610
|
+
const canonicalSource = canonicalDeliverySource(source);
|
|
1611
|
+
let normalized = this.normalizePublishedMessagePayload(event, payload, source);
|
|
1612
|
+
if (isJsonObject(normalized)
|
|
1613
|
+
&& (this.isInstanceScopedMessageEvent(event) || this.isGroupScopedEvent(event))
|
|
1614
|
+
&& canonicalSource !== 'direct') {
|
|
1615
|
+
normalized = { ...normalized, source: canonicalSource };
|
|
1616
|
+
}
|
|
1617
|
+
client._dispatcher.enqueue(event, normalized);
|
|
1496
1618
|
if (generation !== this.deliveryGeneration)
|
|
1497
1619
|
return;
|
|
1498
|
-
if (
|
|
1499
|
-
&&
|
|
1620
|
+
if (canonicalSource !== 'pull' && canonicalSource !== 'pending_retry'
|
|
1621
|
+
&& canonicalSource !== 'push' && canonicalSource !== 'inline_push')
|
|
1500
1622
|
return;
|
|
1501
|
-
if (
|
|
1502
|
-
const localBatch = batch ?? this.createDeliveryChangeBatch(
|
|
1623
|
+
if (canonicalSource === 'push' || canonicalSource === 'inline_push') {
|
|
1624
|
+
const localBatch = batch ?? this.createDeliveryChangeBatch(canonicalSource);
|
|
1503
1625
|
this.recordDeliveryChange(localBatch.changes, event, payload, ns, generation);
|
|
1504
1626
|
if (!batch)
|
|
1505
1627
|
await this.flushRealtimeDeliveryChanges(localBatch);
|
|
@@ -1542,6 +1664,21 @@ export class MessageDeliveryEngine {
|
|
|
1542
1664
|
}
|
|
1543
1665
|
return true;
|
|
1544
1666
|
}
|
|
1667
|
+
isSelfSentGroupMessage(message) {
|
|
1668
|
+
if (!isJsonObject(message))
|
|
1669
|
+
return false;
|
|
1670
|
+
const client = this.runtime.client;
|
|
1671
|
+
const selfAid = String(client._aid ?? '').trim().toLowerCase();
|
|
1672
|
+
const senderAid = String(message.sender_aid ?? message.from_aid ?? message.from ?? '').trim().toLowerCase();
|
|
1673
|
+
if (!selfAid || senderAid !== selfAid)
|
|
1674
|
+
return false;
|
|
1675
|
+
const senderDevice = Object.prototype.hasOwnProperty.call(message, 'sender_device_id')
|
|
1676
|
+
? message.sender_device_id
|
|
1677
|
+
: message.from_device_id;
|
|
1678
|
+
if (typeof senderDevice !== 'string' || !senderDevice.trim() || !String(client._deviceId ?? '').trim())
|
|
1679
|
+
return false;
|
|
1680
|
+
return senderDevice.trim() === String(client._deviceId).trim();
|
|
1681
|
+
}
|
|
1545
1682
|
strictTargetString(source, key) {
|
|
1546
1683
|
if (!Object.prototype.hasOwnProperty.call(source, key)) {
|
|
1547
1684
|
return { present: false, valid: false, value: '' };
|
|
@@ -1611,13 +1748,12 @@ export class MessageDeliveryEngine {
|
|
|
1611
1748
|
const ns = isJsonObject(data) && client._aid && data.seq !== undefined
|
|
1612
1749
|
? `p2p:${client._aid}`
|
|
1613
1750
|
: '';
|
|
1614
|
-
const seq = isJsonObject(data)
|
|
1615
|
-
if (client._v2Session && ns &&
|
|
1616
|
-
&& this.messageTargetsCurrentInstance(data)) {
|
|
1751
|
+
const seq = isJsonObject(data) ? positiveSafeSequenceHint(data.seq) : 0;
|
|
1752
|
+
if (client._v2Session && ns && seq > 0 && this.messageTargetsCurrentInstance(data)) {
|
|
1617
1753
|
client._seqTracker.updateMaxSeen(ns, seq);
|
|
1618
1754
|
}
|
|
1619
1755
|
const hasInlineObject = isJsonObject(data)
|
|
1620
|
-
&&
|
|
1756
|
+
&& hasNonEmptyInlineMessage(data.inline_message);
|
|
1621
1757
|
const inlineGeneration = hasInlineObject ? this.captureInlineGeneration() : undefined;
|
|
1622
1758
|
const operation = () => this.processAndPublishMessage(data, inlineGeneration);
|
|
1623
1759
|
client._safeAsync(client._v2Session && ns && !hasInlineObject
|
|
@@ -1634,33 +1770,15 @@ export class MessageDeliveryEngine {
|
|
|
1634
1770
|
msg.type = 'message.recalled';
|
|
1635
1771
|
if (!this.messageTargetsCurrentInstance(msg))
|
|
1636
1772
|
return;
|
|
1637
|
-
const seq = msg.seq;
|
|
1638
|
-
if (
|
|
1773
|
+
const seq = positiveSafeSequenceHint(msg.seq);
|
|
1774
|
+
if (client._aid && seq > 0) {
|
|
1639
1775
|
const ns = `p2p:${client._aid}`;
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
const seqNeedsPull = client._seqTracker.onMessageSeq(ns, seq);
|
|
1644
|
-
const published = await this.publishOrderedMessage('message.recalled', ns, seq, msg);
|
|
1645
|
-
const contigAfter = client._seqTracker.getContiguousSeq(ns);
|
|
1646
|
-
if (seqNeedsPull && !published && client._sessionOptions?.background_sync !== false) {
|
|
1776
|
+
client._seqTracker.updateMaxSeen(ns, seq);
|
|
1777
|
+
if (seq > client._seqTracker.getContiguousSeq(ns)
|
|
1778
|
+
&& client._sessionOptions?.background_sync !== false)
|
|
1647
1779
|
client._safeAsync(this.fillP2pGap());
|
|
1648
|
-
}
|
|
1649
|
-
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
1650
|
-
if (contig > 0) {
|
|
1651
|
-
const ackSeq = this.clampAckSeq('message.ack', 'seq', ns, contig);
|
|
1652
|
-
client._transport.call('message.ack', {
|
|
1653
|
-
seq: ackSeq,
|
|
1654
|
-
device_id: client._deviceId,
|
|
1655
|
-
slot_id: client._slotId,
|
|
1656
|
-
_rpc_background: true,
|
|
1657
|
-
}).catch((e) => { client._clientLog.warn(`P2P recall auto-ack failed:${String(e)}`); });
|
|
1658
|
-
}
|
|
1659
|
-
if (contigAfter !== contigBefore)
|
|
1660
|
-
this.persistSeq(ns);
|
|
1661
|
-
return;
|
|
1662
1780
|
}
|
|
1663
|
-
await this.publishMessageRecallTombstone(seq, msg);
|
|
1781
|
+
await this.publishMessageRecallTombstone(msg.seq, msg);
|
|
1664
1782
|
}
|
|
1665
1783
|
async processAndPublishMessage(data, inlineGeneration) {
|
|
1666
1784
|
const client = this.runtime.client;
|
|
@@ -1677,47 +1795,36 @@ export class MessageDeliveryEngine {
|
|
|
1677
1795
|
// 新服务端为兼容旧 SDK 仍可能发送 message.received;V2 会话下必须
|
|
1678
1796
|
// 在旧 seq tracking / ordered publish 之前转入前台 Tail 协调器。
|
|
1679
1797
|
const legacyPushSeq = positiveSafeSequenceHint(msg.seq);
|
|
1680
|
-
const hasInlineField =
|
|
1798
|
+
const hasInlineField = hasNonEmptyInlineMessage(msg.inline_message);
|
|
1681
1799
|
if (client._v2Session && client._aid && (legacyPushSeq > 0 || hasInlineField)) {
|
|
1682
1800
|
await this.processV2P2PNotification(msg, 'v1', inlineGeneration);
|
|
1683
1801
|
return;
|
|
1684
1802
|
}
|
|
1803
|
+
if (client._aid && legacyPushSeq > 0) {
|
|
1804
|
+
const ns = `p2p:${client._aid}`;
|
|
1805
|
+
client._seqTracker.updateMaxSeen(ns, legacyPushSeq);
|
|
1806
|
+
if (client._sessionOptions?.background_sync !== false)
|
|
1807
|
+
client._safeAsync(this.fillP2pGap());
|
|
1808
|
+
return;
|
|
1809
|
+
}
|
|
1685
1810
|
const seq = msg.seq;
|
|
1686
1811
|
const encryptedPush = client._isEncryptedPushMessage(msg);
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
const published = encryptedPush
|
|
1694
|
-
? await client._publishEncryptedPushMessage('message.received', 'message.undecryptable', ns, seq, msg, false)
|
|
1695
|
-
: await this.publishOrderedMessage('message.received', ns, seq, msg);
|
|
1696
|
-
const contigAfter = client._seqTracker.getContiguousSeq(ns);
|
|
1697
|
-
const needPull = seqNeedsPull && !published;
|
|
1698
|
-
if (needPull && client._sessionOptions?.background_sync !== false) {
|
|
1812
|
+
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
1813
|
+
const seqNum = positiveSafeSequenceHint(seq);
|
|
1814
|
+
if (ns && seqNum > 0) {
|
|
1815
|
+
client._seqTracker.updateMaxSeen(ns, seqNum);
|
|
1816
|
+
if (seqNum > client._seqTracker.getContiguousSeq(ns)
|
|
1817
|
+
&& client._sessionOptions?.background_sync !== false)
|
|
1699
1818
|
client._safeAsync(this.fillP2pGap());
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
slot_id: client._slotId,
|
|
1708
|
-
_rpc_background: true,
|
|
1709
|
-
}).catch((e) => { client._clientLog.warn(`P2P auto-ack failed:${String(e)}`); });
|
|
1710
|
-
}
|
|
1711
|
-
if (contigAfter !== contigBefore)
|
|
1712
|
-
this.persistSeq(ns);
|
|
1713
|
-
if (encryptedPush)
|
|
1714
|
-
return;
|
|
1819
|
+
}
|
|
1820
|
+
if (encryptedPush) {
|
|
1821
|
+
await client._publishEncryptedPushMessage('message.received', 'message.undecryptable', ns, seq ?? 0, msg, false);
|
|
1822
|
+
return;
|
|
1823
|
+
}
|
|
1824
|
+
if (ns && seq !== undefined && seq !== null) {
|
|
1825
|
+
await this.publishOrderedMessage('message.received', ns, seq, msg);
|
|
1715
1826
|
}
|
|
1716
1827
|
else {
|
|
1717
|
-
if (encryptedPush) {
|
|
1718
|
-
await client._publishEncryptedPushMessage('message.received', 'message.undecryptable', '', seq ?? 0, msg, false);
|
|
1719
|
-
return;
|
|
1720
|
-
}
|
|
1721
1828
|
await client._publishAppEvent('message.received', msg, 'push');
|
|
1722
1829
|
}
|
|
1723
1830
|
}
|
|
@@ -1734,7 +1841,7 @@ export class MessageDeliveryEngine {
|
|
|
1734
1841
|
_decrypt_error: String(exc),
|
|
1735
1842
|
};
|
|
1736
1843
|
client._attachV2EnvelopeMetadataFromSource(safeEvent, data);
|
|
1737
|
-
await client._publishAppEvent('message.undecryptable', safeEvent);
|
|
1844
|
+
await client._publishAppEvent('message.undecryptable', safeEvent, 'push');
|
|
1738
1845
|
}
|
|
1739
1846
|
}
|
|
1740
1847
|
}
|
|
@@ -1742,15 +1849,14 @@ export class MessageDeliveryEngine {
|
|
|
1742
1849
|
const client = this.runtime.client;
|
|
1743
1850
|
client._clientLog.debug(`_onRawGroupMessageCreated enter: group_id=${data?.group_id ?? '-'} from=${data?.from ?? '-'} seq=${data?.seq ?? '-'}`);
|
|
1744
1851
|
const groupId = isJsonObject(data) && typeof data.group_id === 'string' ? data.group_id.trim() : '';
|
|
1745
|
-
const seq = isJsonObject(data) && typeof data.seq === 'number' ? data.seq : 0;
|
|
1746
1852
|
const kind = isJsonObject(data) ? String(data.kind ?? '').trim() : '';
|
|
1747
|
-
if (client._v2Session && groupId && kind !== 'group.online_unread_hint'
|
|
1748
|
-
&& Number.isSafeInteger(seq) && seq > 0) {
|
|
1749
|
-
client._seqTracker.updateMaxSeen(`group:${groupId}`, seq);
|
|
1750
|
-
}
|
|
1751
1853
|
const ns = groupId ? `group:${groupId}` : '';
|
|
1854
|
+
const seq = isJsonObject(data) ? positiveSafeSequenceHint(data.seq) : 0;
|
|
1855
|
+
if (client._v2Session && ns && kind !== 'group.online_unread_hint' && seq > 0) {
|
|
1856
|
+
client._seqTracker.updateMaxSeen(ns, seq);
|
|
1857
|
+
}
|
|
1752
1858
|
const hasInlineObject = isJsonObject(data)
|
|
1753
|
-
&&
|
|
1859
|
+
&& hasNonEmptyInlineMessage(data.inline_message);
|
|
1754
1860
|
const inlineGeneration = hasInlineObject ? this.captureInlineGeneration() : undefined;
|
|
1755
1861
|
const operation = () => this.processAndPublishGroupMessage(data, inlineGeneration);
|
|
1756
1862
|
client._safeAsync(client._v2Session && ns && !hasInlineObject
|
|
@@ -1775,74 +1881,61 @@ export class MessageDeliveryEngine {
|
|
|
1775
1881
|
// V2 会话中旧 group.message_created 也只是 Head 通知;必须先 Tail,
|
|
1776
1882
|
// 不能让 payload 路径直接推进旧 ordered publish 状态。
|
|
1777
1883
|
const legacyPushSeq = positiveSafeSequenceHint(seq);
|
|
1778
|
-
const hasInlineField =
|
|
1884
|
+
const hasInlineField = hasNonEmptyInlineMessage(msg.inline_message);
|
|
1779
1885
|
if (client._v2Session && groupId && (legacyPushSeq > 0 || hasInlineField)) {
|
|
1780
1886
|
await this.processGroupV2MessageCreated(msg, 'v1', inlineGeneration);
|
|
1781
1887
|
return;
|
|
1782
1888
|
}
|
|
1889
|
+
if (groupId && legacyPushSeq > 0) {
|
|
1890
|
+
const ns = `group:${groupId}`;
|
|
1891
|
+
client._seqTracker.updateMaxSeen(ns, legacyPushSeq);
|
|
1892
|
+
if (client._sessionOptions?.background_sync !== false)
|
|
1893
|
+
client._safeAsync(this.fillGroupGap(groupId));
|
|
1894
|
+
return;
|
|
1895
|
+
}
|
|
1783
1896
|
if (payload === undefined || payload === null
|
|
1784
1897
|
|| (typeof payload === 'object' && Object.keys(payload).length === 0)) {
|
|
1785
|
-
|
|
1898
|
+
if (client._v2Session)
|
|
1899
|
+
await this.autoPullGroupMessages(msg);
|
|
1900
|
+
else if (groupId && seq !== undefined && seq !== null) {
|
|
1901
|
+
const ns = `group:${groupId}`;
|
|
1902
|
+
const seqNum = positiveSafeSequenceHint(seq);
|
|
1903
|
+
if (seqNum > 0) {
|
|
1904
|
+
client._seqTracker.updateMaxSeen(ns, seqNum);
|
|
1905
|
+
if (seqNum > client._seqTracker.getContiguousSeq(ns)
|
|
1906
|
+
&& client._sessionOptions?.background_sync !== false)
|
|
1907
|
+
client._safeAsync(this.fillGroupGap(groupId));
|
|
1908
|
+
}
|
|
1909
|
+
await this.publishOrderedMessage('group.message_created', ns, seq, msg);
|
|
1910
|
+
}
|
|
1911
|
+
else
|
|
1912
|
+
await client._publishAppEvent('group.message_created', msg, 'push');
|
|
1786
1913
|
return;
|
|
1787
1914
|
}
|
|
1788
1915
|
const encryptedPush = client._isEncryptedPushMessage(msg);
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
// 群撤回 tombstone(占位 / 通知):归一化为 group.message_recalled,仍占 seq。
|
|
1796
|
-
if (!encryptedPush && this.recallEventFromGroupMessage(msg)) {
|
|
1797
|
-
await this.publishGroupRecallTombstone(groupId, seq, msg);
|
|
1798
|
-
this.markPublishedSeq(ns, Number(seq));
|
|
1799
|
-
const contigAfter = client._seqTracker.getContiguousSeq(ns);
|
|
1800
|
-
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
1801
|
-
if (contig > 0) {
|
|
1802
|
-
const ackSeq = this.clampAckSeq('group.ack_messages', 'msg_seq', ns, contig);
|
|
1803
|
-
client._transport.call('group.ack_messages', {
|
|
1804
|
-
group_id: groupId,
|
|
1805
|
-
msg_seq: ackSeq,
|
|
1806
|
-
device_id: client._deviceId,
|
|
1807
|
-
slot_id: client._slotId,
|
|
1808
|
-
_rpc_background: true,
|
|
1809
|
-
}).catch((e) => { client._clientLog.warn('group recall auto-ack failed: group=' + groupId, e); });
|
|
1810
|
-
}
|
|
1811
|
-
if (contigAfter !== contigBefore)
|
|
1812
|
-
this.persistSeq(ns);
|
|
1813
|
-
return;
|
|
1814
|
-
}
|
|
1815
|
-
const published = encryptedPush
|
|
1816
|
-
? await client._publishEncryptedPushMessage('group.message_created', 'group.message_undecryptable', ns, seq, msg, true)
|
|
1817
|
-
: await this.publishOrderedMessage('group.message_created', ns, seq, msg);
|
|
1818
|
-
const contigAfter = client._seqTracker.getContiguousSeq(ns);
|
|
1819
|
-
const needPull = seqNeedsPull && !published;
|
|
1820
|
-
if (needPull && client._sessionOptions?.background_sync !== false) {
|
|
1916
|
+
const ns = groupId ? `group:${groupId}` : '';
|
|
1917
|
+
const seqNum = positiveSafeSequenceHint(seq);
|
|
1918
|
+
if (ns && seqNum > 0) {
|
|
1919
|
+
client._seqTracker.updateMaxSeen(ns, seqNum);
|
|
1920
|
+
if (seqNum > client._seqTracker.getContiguousSeq(ns)
|
|
1921
|
+
&& client._sessionOptions?.background_sync !== false)
|
|
1821
1922
|
client._safeAsync(this.fillGroupGap(groupId));
|
|
1822
|
-
}
|
|
1823
|
-
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
1824
|
-
if (contig > 0) {
|
|
1825
|
-
const ackSeq = this.clampAckSeq('group.ack_messages', 'msg_seq', ns, contig);
|
|
1826
|
-
client._transport.call('group.ack_messages', {
|
|
1827
|
-
group_id: groupId,
|
|
1828
|
-
msg_seq: ackSeq,
|
|
1829
|
-
device_id: client._deviceId,
|
|
1830
|
-
slot_id: client._slotId,
|
|
1831
|
-
_rpc_background: true,
|
|
1832
|
-
}).catch((e) => { client._clientLog.warn('group message auto-ack failed: group=' + groupId, e); });
|
|
1833
|
-
}
|
|
1834
|
-
if (contigAfter !== contigBefore)
|
|
1835
|
-
this.persistSeq(ns);
|
|
1836
|
-
if (encryptedPush)
|
|
1837
|
-
return;
|
|
1838
1923
|
}
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
await client._publishAppEvent('group.message_created', msg, 'push');
|
|
1924
|
+
if (this.isSelfSentGroupMessage(msg))
|
|
1925
|
+
return;
|
|
1926
|
+
if (!encryptedPush && this.recallEventFromGroupMessage(msg)) {
|
|
1927
|
+
await this.publishGroupRecallTombstone(groupId, seq, msg);
|
|
1928
|
+
return;
|
|
1845
1929
|
}
|
|
1930
|
+
if (encryptedPush) {
|
|
1931
|
+
await client._publishEncryptedPushMessage('group.message_created', 'group.message_undecryptable', ns, seq ?? 0, msg, true);
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1934
|
+
if (ns && seq !== undefined && seq !== null) {
|
|
1935
|
+
await this.publishOrderedMessage('group.message_created', ns, seq, msg);
|
|
1936
|
+
}
|
|
1937
|
+
else
|
|
1938
|
+
await client._publishAppEvent('group.message_created', msg, 'push');
|
|
1846
1939
|
}
|
|
1847
1940
|
catch (exc) {
|
|
1848
1941
|
client._clientLog.warn(`group push processing failed:${String(exc)}`);
|
|
@@ -1857,7 +1950,7 @@ export class MessageDeliveryEngine {
|
|
|
1857
1950
|
_decrypt_error: String(exc),
|
|
1858
1951
|
};
|
|
1859
1952
|
client._attachV2EnvelopeMetadataFromSource(safeEvent, data);
|
|
1860
|
-
await client._publishAppEvent('group.message_undecryptable', safeEvent);
|
|
1953
|
+
await client._publishAppEvent('group.message_undecryptable', safeEvent, 'push');
|
|
1861
1954
|
}
|
|
1862
1955
|
}
|
|
1863
1956
|
}
|
|
@@ -1865,7 +1958,7 @@ export class MessageDeliveryEngine {
|
|
|
1865
1958
|
const client = this.runtime.client;
|
|
1866
1959
|
const groupId = (notification.group_id ?? '');
|
|
1867
1960
|
if (!groupId) {
|
|
1868
|
-
await client._publishAppEvent('group.message_created', notification);
|
|
1961
|
+
await client._publishAppEvent('group.message_created', notification, 'push');
|
|
1869
1962
|
return;
|
|
1870
1963
|
}
|
|
1871
1964
|
if (client._sessionOptions?.background_sync === false) {
|
|
@@ -1882,7 +1975,7 @@ export class MessageDeliveryEngine {
|
|
|
1882
1975
|
}
|
|
1883
1976
|
catch (exc) {
|
|
1884
1977
|
client._clientLog.warn(`auto pull group message failed:${String(exc)}`);
|
|
1885
|
-
await client._publishAppEvent('group.message_created', notification);
|
|
1978
|
+
await client._publishAppEvent('group.message_created', notification, 'push');
|
|
1886
1979
|
return;
|
|
1887
1980
|
}
|
|
1888
1981
|
}
|
|
@@ -1912,7 +2005,7 @@ export class MessageDeliveryEngine {
|
|
|
1912
2005
|
// 两种情况都不能再把原始通知当作第二条业务消息发布。
|
|
1913
2006
|
client._clientLog.warn(`auto pull group message commit/ack failed:${String(exc)}`);
|
|
1914
2007
|
if (!rawCompleted) {
|
|
1915
|
-
await client._publishAppEvent('group.message_created', notification);
|
|
2008
|
+
await client._publishAppEvent('group.message_created', notification, 'push');
|
|
1916
2009
|
}
|
|
1917
2010
|
}
|
|
1918
2011
|
}
|
|
@@ -2050,6 +2143,8 @@ export class MessageDeliveryEngine {
|
|
|
2050
2143
|
client._gapFillDone.add(dedupKey);
|
|
2051
2144
|
this.runtime.delivery.setGapFillActive(true);
|
|
2052
2145
|
let continuationAfterSeq = 0;
|
|
2146
|
+
let pendingAckSeq = 0;
|
|
2147
|
+
let failed = false;
|
|
2053
2148
|
try {
|
|
2054
2149
|
let nextAfterSeq = afterSeq;
|
|
2055
2150
|
const maxPages = singlePage ? 1 : 100;
|
|
@@ -2071,9 +2166,11 @@ export class MessageDeliveryEngine {
|
|
|
2071
2166
|
return;
|
|
2072
2167
|
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
2073
2168
|
const eventObjects = events.filter((evt) => isJsonObject(evt));
|
|
2074
|
-
|
|
2169
|
+
const contiguousSeq = Number.isSafeInteger(result.contiguous_seq) ? result.contiguous_seq : undefined;
|
|
2170
|
+
if (contiguousSeq === undefined)
|
|
2075
2171
|
client._seqTracker.onPullResult(ns, eventObjects, nextAfterSeq);
|
|
2076
|
-
|
|
2172
|
+
else
|
|
2173
|
+
client._seqTracker.onPullResult(ns, eventObjects, nextAfterSeq, contiguousSeq);
|
|
2077
2174
|
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
2078
2175
|
const retentionFloor = Math.max(0, Number(result.retention_floor_event_seq ?? 0), Number(cursor?.retention_floor_seq ?? 0));
|
|
2079
2176
|
const serverAckFloor = Math.max(0, Number(cursor?.current_seq ?? 0));
|
|
@@ -2089,6 +2186,7 @@ export class MessageDeliveryEngine {
|
|
|
2089
2186
|
}
|
|
2090
2187
|
const eventSeqs = [];
|
|
2091
2188
|
let hasDissolvedEvent = false;
|
|
2189
|
+
let publishedEventCount = 0;
|
|
2092
2190
|
for (const evt of eventObjects) {
|
|
2093
2191
|
const eventSeq = Number(evt.event_seq ?? 0);
|
|
2094
2192
|
if (Number.isFinite(eventSeq) && eventSeq > 0)
|
|
@@ -2110,7 +2208,8 @@ export class MessageDeliveryEngine {
|
|
|
2110
2208
|
}
|
|
2111
2209
|
}
|
|
2112
2210
|
if (Number.isFinite(eventSeq) && eventSeq > 0 && !client._pushedSeqs.get(ns)?.has(eventSeq)) {
|
|
2113
|
-
this.enqueueOrderedMessage(ns, 'group.changed', eventSeq, evt);
|
|
2211
|
+
this.enqueueOrderedMessage(ns, 'group.changed', eventSeq, evt, 'pull');
|
|
2212
|
+
publishedEventCount += 1;
|
|
2114
2213
|
}
|
|
2115
2214
|
}
|
|
2116
2215
|
const ackContig = client._seqTracker.getContiguousSeq(ns);
|
|
@@ -2119,41 +2218,45 @@ export class MessageDeliveryEngine {
|
|
|
2119
2218
|
this.persistSeq(ns);
|
|
2120
2219
|
}
|
|
2121
2220
|
if (eventObjects.length > 0 && ackContig > 0 && ackContig !== pageContigBefore) {
|
|
2122
|
-
|
|
2123
|
-
const ackPromise = client._transport.call('group.ack_events', {
|
|
2124
|
-
group_id: groupId,
|
|
2125
|
-
event_seq: ackContig,
|
|
2126
|
-
device_id: client._deviceId,
|
|
2127
|
-
slot_id: client._slotId,
|
|
2128
|
-
_rpc_background: true,
|
|
2129
|
-
});
|
|
2130
|
-
if (singlePage)
|
|
2131
|
-
await ackPromise;
|
|
2132
|
-
else
|
|
2133
|
-
ackPromise.catch((e) => { client._clientLog.warn('group event auto-ack failed: group=' + groupId, e); });
|
|
2134
|
-
}
|
|
2135
|
-
catch (e) {
|
|
2136
|
-
client._clientLog.warn('group event auto-ack failed: group=' + groupId, e);
|
|
2137
|
-
}
|
|
2221
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackContig);
|
|
2138
2222
|
}
|
|
2223
|
+
this.onPullPage(ns, publishedEventCount, result.has_more === true, eventObjects.length > 0 ? Number(eventObjects[eventObjects.length - 1].event_seq ?? 0) : undefined, typeof result.remaining === 'number' && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null, eventObjects.length, ackContig, eventObjects.length > 0 ? Number(eventObjects[eventObjects.length - 1].event_seq ?? 0) : undefined);
|
|
2139
2224
|
const nextAfter = Math.max(eventSeqs.length > 0 ? Math.max(...eventSeqs) : nextAfterSeq, nextAfterSeq);
|
|
2140
|
-
|
|
2225
|
+
const needsMore = result.has_more === true || client._seqTracker.getMaxSeenSeq(ns) > nextAfter;
|
|
2226
|
+
if (singlePage && needsMore && nextAfter > nextAfterSeq) {
|
|
2141
2227
|
continuationAfterSeq = nextAfter;
|
|
2142
2228
|
}
|
|
2143
|
-
if (eventObjects.length === 0 || nextAfter <= nextAfterSeq ||
|
|
2229
|
+
if (eventObjects.length === 0 || nextAfter <= nextAfterSeq || !needsMore)
|
|
2144
2230
|
break;
|
|
2145
2231
|
nextAfterSeq = nextAfter;
|
|
2146
2232
|
}
|
|
2147
2233
|
if (pageCount >= maxPages) {
|
|
2148
2234
|
client._clientLog.warn(`group event gap fill reached max_pages=${maxPages} group=${groupId} after_seq=${nextAfterSeq}`);
|
|
2149
2235
|
}
|
|
2236
|
+
if (pendingAckSeq > 0) {
|
|
2237
|
+
try {
|
|
2238
|
+
await client._transport.call('group.ack_events', {
|
|
2239
|
+
group_id: groupId,
|
|
2240
|
+
event_seq: pendingAckSeq,
|
|
2241
|
+
device_id: client._deviceId,
|
|
2242
|
+
slot_id: client._slotId,
|
|
2243
|
+
_rpc_background: true,
|
|
2244
|
+
});
|
|
2245
|
+
}
|
|
2246
|
+
catch (e) {
|
|
2247
|
+
client._clientLog.warn('group event auto-ack failed: group=' + groupId, e);
|
|
2248
|
+
}
|
|
2249
|
+
}
|
|
2150
2250
|
}
|
|
2151
2251
|
catch (exc) {
|
|
2252
|
+
failed = true;
|
|
2152
2253
|
client._clientLog.warn(`group event gap-fill failed:${String(exc)}`);
|
|
2153
2254
|
}
|
|
2154
2255
|
finally {
|
|
2155
2256
|
client._gapFillDone.delete(dedupKey);
|
|
2156
2257
|
this.runtime.delivery.setGapFillActive(false);
|
|
2258
|
+
if (!client._rpcPipeline)
|
|
2259
|
+
this.syncStopped(failed ? 'aborted' : 'pull_drained');
|
|
2157
2260
|
if (singlePage && continuationAfterSeq > afterSeq) {
|
|
2158
2261
|
this.enqueueOnlineUnreadEventHint({
|
|
2159
2262
|
group_id: groupId,
|
|
@@ -2182,43 +2285,15 @@ export class MessageDeliveryEngine {
|
|
|
2182
2285
|
this.enqueueOnlineUnreadEventHint(data);
|
|
2183
2286
|
return;
|
|
2184
2287
|
}
|
|
2185
|
-
if (this.isSelfJoinGroupChanged(data)) {
|
|
2186
|
-
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
2187
|
-
const maxSeen = client._seqTracker.getMaxSeenSeq(ns);
|
|
2188
|
-
if (contig === 0 && maxSeen === 0 && eventSeq > 1) {
|
|
2189
|
-
client._clientLog.debug(`group.changed self-join baseline: group=${groupId}, event_seq=${eventSeq}, baseline=${eventSeq - 1}`);
|
|
2190
|
-
client._seqTracker.forceContiguousSeq(ns, eventSeq - 1);
|
|
2191
|
-
}
|
|
2192
|
-
}
|
|
2193
2288
|
const contigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
2289
|
+
client._seqTracker.updateMaxSeen(ns, eventSeq);
|
|
2194
2290
|
const publishedDuplicate = client._pushedSeqs.get(ns)?.has(eventSeq) === true;
|
|
2195
2291
|
if (eventSeq <= contigBefore || publishedDuplicate) {
|
|
2196
2292
|
client._clientLog.debug(`group.changed skipped duplicate/stale: group=${groupId}, event_seq=${eventSeq}, contiguous=${contigBefore}`);
|
|
2197
|
-
if (eventSeq <= contigBefore) {
|
|
2198
|
-
this.fireGroupEventAck(groupId, ns, eventSeq, 'group event covered push ack');
|
|
2199
|
-
}
|
|
2200
|
-
else if (contigBefore > 0) {
|
|
2201
|
-
this.fireGroupEventAck(groupId, ns, contigBefore, 'group event covered push ack');
|
|
2202
|
-
}
|
|
2203
2293
|
return;
|
|
2204
2294
|
}
|
|
2205
2295
|
this.enqueueOrderedMessage(ns, 'group.changed', eventSeq, data);
|
|
2206
|
-
needPull =
|
|
2207
|
-
const ackContig = client._seqTracker.getContiguousSeq(ns);
|
|
2208
|
-
await this.drainOrderedMessages(ns);
|
|
2209
|
-
if (ackContig > 0 && ackContig !== contigBefore) {
|
|
2210
|
-
if (data.action !== 'dissolved')
|
|
2211
|
-
this.persistSeq(ns);
|
|
2212
|
-
client._transport.call('group.ack_events', {
|
|
2213
|
-
group_id: groupId,
|
|
2214
|
-
event_seq: ackContig,
|
|
2215
|
-
device_id: client._deviceId,
|
|
2216
|
-
slot_id: client._slotId,
|
|
2217
|
-
_rpc_background: true,
|
|
2218
|
-
}).catch((e) => {
|
|
2219
|
-
client._clientLog.warn('group event push auto-ack failed: group=' + groupId, e);
|
|
2220
|
-
});
|
|
2221
|
-
}
|
|
2296
|
+
needPull = eventSeq > contigBefore;
|
|
2222
2297
|
if (needPull && groupId && !data._from_gap_fill && client._sessionOptions?.background_sync !== false) {
|
|
2223
2298
|
client._safeAsync(this.fillGroupEventGap(groupId));
|
|
2224
2299
|
}
|
|
@@ -2837,20 +2912,19 @@ export class MessageDeliveryEngine {
|
|
|
2837
2912
|
if (forceTail || order[0] === 'tail')
|
|
2838
2913
|
result = await run(true, false, undefined, false, forceTail);
|
|
2839
2914
|
const state = this.syncState(ns);
|
|
2840
|
-
// Tail 的连续性证明只覆盖其返回的 head。Tail 进行期间到达的新
|
|
2841
|
-
// Push 不能再次进入 realtime planner(那会再做一次 Tail);以已经
|
|
2842
|
-
// 落盘的 ack 为起点,至多补一页普通 Forward 即可恢复该小窗口。
|
|
2843
|
-
const tailMissedDuringPull = tailCompleted
|
|
2844
|
-
&& Number(client._seqTracker.getMaxSeenSeq(ns) || 0) > state.head;
|
|
2845
2915
|
const maxSeen = Number(client._seqTracker.getMaxSeenSeq(ns) || 0);
|
|
2916
|
+
// Tail 期间到达的新 Push 只记录在 pending Pull 上界,不写入 SeqTracker。
|
|
2917
|
+
const pendingUpper = Number(this.pendingP2pPullUpper?.get(ns) ?? 0);
|
|
2918
|
+
const observedUpper = Math.max(maxSeen, pendingUpper);
|
|
2919
|
+
const tailMissedDuringPull = tailCompleted && observedUpper > state.head;
|
|
2846
2920
|
const pendingThroughSeq = tailMissedDuringPull
|
|
2847
|
-
?
|
|
2921
|
+
? observedUpper
|
|
2848
2922
|
: 0;
|
|
2849
|
-
const tailForward = tailCompleted && (state.ack < state.
|
|
2923
|
+
const tailForward = tailCompleted && (state.ack < state.head || tailMissedDuringPull);
|
|
2850
2924
|
const backgroundWindowForward = order[0] === 'forward' && !headForward;
|
|
2851
2925
|
if (headForward || tailForward
|
|
2852
2926
|
|| (client._sessionOptions?.background_sync !== false && backgroundWindowForward)) {
|
|
2853
|
-
const forwardTarget = Math.max(headForward ? Math.max(pushSeq, maxSeen) : 0, (tailForward || backgroundWindowForward) ? state.tail - 1 : 0, tailMissedDuringPull ?
|
|
2927
|
+
const forwardTarget = Math.max(headForward ? Math.max(pushSeq, maxSeen) : 0, (tailForward || backgroundWindowForward) ? state.tail - 1 : 0, tailMissedDuringPull ? observedUpper : 0);
|
|
2854
2928
|
const forwardMaxPages = this.forwardMaxPages(state.ack, forwardTarget, pageLimit);
|
|
2855
2929
|
result = await run(false, !(headForward || tailForward), forwardMaxPages, headForward || tailMissedDuringPull);
|
|
2856
2930
|
if (pendingThroughSeq > 0) {
|
|
@@ -2912,17 +2986,18 @@ export class MessageDeliveryEngine {
|
|
|
2912
2986
|
const state = this.syncState(ns);
|
|
2913
2987
|
// 与 P2P 相同:真实完成 Tail 后,至多用一页 Forward 收敛 Tail
|
|
2914
2988
|
// 期间新到达的序号;绝不重新规划或再执行 Tail。
|
|
2915
|
-
const tailMissedDuringPull = tailCompleted
|
|
2916
|
-
&& Number(client._seqTracker.getMaxSeenSeq(ns) || 0) > state.head;
|
|
2917
2989
|
const maxSeen = Number(client._seqTracker.getMaxSeenSeq(ns) || 0);
|
|
2990
|
+
const pendingUpper = Number(this.pendingGroupPullUpper?.get(ns) ?? 0);
|
|
2991
|
+
const observedUpper = Math.max(maxSeen, pendingUpper);
|
|
2992
|
+
const tailMissedDuringPull = tailCompleted && observedUpper > state.head;
|
|
2918
2993
|
const pendingThroughSeq = tailMissedDuringPull
|
|
2919
|
-
?
|
|
2994
|
+
? observedUpper
|
|
2920
2995
|
: 0;
|
|
2921
|
-
const tailForward = tailCompleted && (state.ack < state.
|
|
2996
|
+
const tailForward = tailCompleted && (state.ack < state.head || tailMissedDuringPull);
|
|
2922
2997
|
const backgroundWindowForward = order[0] === 'forward' && !headForward;
|
|
2923
2998
|
if (headForward || tailForward
|
|
2924
2999
|
|| (client._sessionOptions?.background_sync !== false && backgroundWindowForward)) {
|
|
2925
|
-
const forwardTarget = Math.max(headForward ? Math.max(pushSeq, maxSeen) : 0, (tailForward || backgroundWindowForward) ? state.tail - 1 : 0, tailMissedDuringPull ?
|
|
3000
|
+
const forwardTarget = Math.max(headForward ? Math.max(pushSeq, maxSeen) : 0, (tailForward || backgroundWindowForward) ? state.tail - 1 : 0, tailMissedDuringPull ? observedUpper : 0);
|
|
2926
3001
|
const forwardMaxPages = this.forwardMaxPages(state.ack, forwardTarget, pageLimit);
|
|
2927
3002
|
result = await run(false, !(headForward || tailForward), forwardMaxPages, headForward || tailMissedDuringPull);
|
|
2928
3003
|
if (pendingThroughSeq > 0) {
|
|
@@ -2979,15 +3054,13 @@ export class MessageDeliveryEngine {
|
|
|
2979
3054
|
const groupId = typeof row.group_id === 'string' ? row.group_id.trim() : '';
|
|
2980
3055
|
if (!groupId)
|
|
2981
3056
|
return;
|
|
2982
|
-
const seq = typeof row.seq === 'number' ? row.seq : 0;
|
|
2983
3057
|
const eventKind = String(row.kind ?? '').trim();
|
|
2984
3058
|
const ns = this.resolvePendingGroupInlineAckNamespace(`group:${groupId}`);
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
// 必须在进入 namespace 队列前登记,才能让在途 Tail 看见后来 Push。
|
|
3059
|
+
const seq = positiveSafeSequenceHint(row.seq);
|
|
3060
|
+
if (client._v2Session && eventKind !== 'group.online_unread_hint' && seq > 0) {
|
|
2988
3061
|
client._seqTracker.updateMaxSeen(ns, seq);
|
|
2989
3062
|
}
|
|
2990
|
-
const hasInlineObject =
|
|
3063
|
+
const hasInlineObject = hasNonEmptyInlineMessage(row.inline_message)
|
|
2991
3064
|
&& eventKind !== 'group.online_unread_hint';
|
|
2992
3065
|
const inlineGeneration = hasInlineObject ? this.captureInlineGeneration() : undefined;
|
|
2993
3066
|
const operation = () => this.processGroupV2MessageCreated(data, 'v2', inlineGeneration);
|
|
@@ -3004,7 +3077,6 @@ export class MessageDeliveryEngine {
|
|
|
3004
3077
|
return;
|
|
3005
3078
|
const groupId = typeof data.group_id === 'string' ? data.group_id.trim() : '';
|
|
3006
3079
|
const seq = positiveSafeSequenceHint(data.seq);
|
|
3007
|
-
const strictSeq = typeof data.seq === 'number' && Number.isSafeInteger(data.seq) && data.seq > 0;
|
|
3008
3080
|
if (!groupId)
|
|
3009
3081
|
return;
|
|
3010
3082
|
const eventKind = String(data.kind ?? '').trim();
|
|
@@ -3016,12 +3088,13 @@ export class MessageDeliveryEngine {
|
|
|
3016
3088
|
return;
|
|
3017
3089
|
}
|
|
3018
3090
|
const ns = this.resolvePendingGroupInlineAckNamespace(`group:${groupId}`);
|
|
3019
|
-
const inlinePresent =
|
|
3091
|
+
const inlinePresent = hasNonEmptyInlineMessage(data.inline_message);
|
|
3020
3092
|
if (seq <= 0) {
|
|
3021
3093
|
if (inlinePresent)
|
|
3022
3094
|
await this.recoverInvalidGroupRealtimeHead(groupId, ns, data.seq);
|
|
3023
3095
|
return;
|
|
3024
3096
|
}
|
|
3097
|
+
client._seqTracker.updateMaxSeen(ns, seq);
|
|
3025
3098
|
const inline = this.inlineMessage(data);
|
|
3026
3099
|
const inlineMatches = inline !== null
|
|
3027
3100
|
&& this.inlineGroupBindingMatches(data, inline, groupId, seq, expectedInlineVersion);
|
|
@@ -3040,15 +3113,13 @@ export class MessageDeliveryEngine {
|
|
|
3040
3113
|
const before = this.syncState(ns);
|
|
3041
3114
|
// 无正文的旧服务端 Head 可把 JSON-safe 数字字符串作为恢复 hint;
|
|
3042
3115
|
// inline 绑定仍要求原始 seq 是严格 number,避免类型归一化后消费正文。
|
|
3043
|
-
if (strictSeq || !inlinePresent)
|
|
3044
|
-
client._seqTracker.updateMaxSeen(ns, seq);
|
|
3045
3116
|
if (seq <= before.ack && before.ack >= before.tail - 1) {
|
|
3046
3117
|
const canonicalNs = this.resolvePendingGroupInlineAckNamespace(ns);
|
|
3047
3118
|
const pending = this.pendingGroupInlineAcks?.get(canonicalNs);
|
|
3048
|
-
if (pending && seq >= pending.seq) {
|
|
3119
|
+
if (inlinePresent && pending && seq >= pending.seq) {
|
|
3049
3120
|
this.retryPendingGroupInlineAck(canonicalNs, pending.seq, 'covered inline Group ack', seq);
|
|
3050
3121
|
}
|
|
3051
|
-
else if (
|
|
3122
|
+
else if (inlineMatches) {
|
|
3052
3123
|
this.fireGroupV2Ack(groupId, ns, seq, 'group v2 covered push ack', inlineMatches ? inlineGeneration : null);
|
|
3053
3124
|
}
|
|
3054
3125
|
return;
|
|
@@ -3084,11 +3155,10 @@ export class MessageDeliveryEngine {
|
|
|
3084
3155
|
}
|
|
3085
3156
|
const pushSeq = isJsonObject(data) && typeof data.seq === 'number' ? data.seq : 0;
|
|
3086
3157
|
if (client._v2Session && Number.isSafeInteger(pushSeq) && pushSeq > 0) {
|
|
3087
|
-
// 与 Group 相同,在排队前暴露更高 Head 给当前在途 Tail。
|
|
3088
3158
|
client._seqTracker.updateMaxSeen(ns, pushSeq);
|
|
3089
3159
|
}
|
|
3090
3160
|
const hasInlineObject = isJsonObject(data)
|
|
3091
|
-
&&
|
|
3161
|
+
&& hasNonEmptyInlineMessage(data.inline_message);
|
|
3092
3162
|
const inlineGeneration = hasInlineObject ? this.captureInlineGeneration() : undefined;
|
|
3093
3163
|
const operation = () => this.processV2P2PNotification(data, 'v2', inlineGeneration);
|
|
3094
3164
|
if (hasInlineObject)
|
|
@@ -3103,12 +3173,12 @@ export class MessageDeliveryEngine {
|
|
|
3103
3173
|
if (!client._v2Session)
|
|
3104
3174
|
return;
|
|
3105
3175
|
const pushSeq = isJsonObject(data) ? positiveSafeSequenceHint(data.seq) : 0;
|
|
3106
|
-
const strictSeq = isJsonObject(data)
|
|
3107
|
-
&& typeof data.seq === 'number' && Number.isSafeInteger(data.seq) && data.seq > 0;
|
|
3108
3176
|
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
3109
3177
|
if (!ns)
|
|
3110
3178
|
return;
|
|
3111
|
-
|
|
3179
|
+
if (pushSeq > 0)
|
|
3180
|
+
client._seqTracker.updateMaxSeen(ns, pushSeq);
|
|
3181
|
+
const inlinePresent = isJsonObject(data) && hasNonEmptyInlineMessage(data.inline_message);
|
|
3112
3182
|
if (pushSeq <= 0) {
|
|
3113
3183
|
if (inlinePresent)
|
|
3114
3184
|
await this.recoverInvalidP2PRealtimeHead(ns, isJsonObject(data) ? data.seq : undefined);
|
|
@@ -3128,11 +3198,9 @@ export class MessageDeliveryEngine {
|
|
|
3128
3198
|
if (inlineMatches && !this.isInlineGenerationCurrent(inlineGeneration))
|
|
3129
3199
|
return;
|
|
3130
3200
|
const before = this.syncState(ns);
|
|
3131
|
-
if (strictSeq || !inlinePresent)
|
|
3132
|
-
client._seqTracker.updateMaxSeen(ns, pushSeq);
|
|
3133
3201
|
if (pushSeq <= before.ack && before.ack >= before.tail - 1) {
|
|
3134
3202
|
const pending = this.pendingP2PInlineAcks?.get(ns);
|
|
3135
|
-
if (pending && pushSeq >= pending.seq) {
|
|
3203
|
+
if (inlinePresent && pending && pushSeq >= pending.seq) {
|
|
3136
3204
|
this.retryPendingP2PInlineAck(ns, pending.seq, 'covered inline P2P ack', pushSeq);
|
|
3137
3205
|
}
|
|
3138
3206
|
else if (inlineMatches) {
|
|
@@ -3651,7 +3719,7 @@ export class MessageDeliveryEngine {
|
|
|
3651
3719
|
const seqNum = Number(seq);
|
|
3652
3720
|
if (!Number.isFinite(seqNum) || !Number.isInteger(seqNum) || seqNum <= 0 || !ns) {
|
|
3653
3721
|
if (event === 'message.recalled') {
|
|
3654
|
-
const published = await client._withPullResponseProcessing(ns, () => this.publishMessageRecallTombstone(seq, payload));
|
|
3722
|
+
const published = await client._withPullResponseProcessing(ns, () => this.publishMessageRecallTombstone(seq, payload, source));
|
|
3655
3723
|
this.ensurePullOperationCurrent();
|
|
3656
3724
|
return published;
|
|
3657
3725
|
}
|
|
@@ -3672,7 +3740,7 @@ export class MessageDeliveryEngine {
|
|
|
3672
3740
|
if (queue && queue.size === 0)
|
|
3673
3741
|
client._pendingOrderedMsgs.delete(ns);
|
|
3674
3742
|
if (event === 'message.recalled') {
|
|
3675
|
-
const published = await client._withPullResponseProcessing(ns, () => this.publishMessageRecallTombstone(seqNum, payload));
|
|
3743
|
+
const published = await client._withPullResponseProcessing(ns, () => this.publishMessageRecallTombstone(seqNum, payload, source));
|
|
3676
3744
|
this.ensurePullOperationCurrent();
|
|
3677
3745
|
this.markPublishedSeq(ns, seqNum);
|
|
3678
3746
|
return published;
|