@agentunion/fastaun 0.5.17 → 0.5.19
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 +83 -0
- package/_packed_docs/CHANGELOG.md +83 -0
- package/_packed_docs/INDEX.md +163 -16
- package/_packed_docs/KITE_DOCS_GUIDE.md +52 -20
- package/_packed_docs/agent.md/SCHEMA.md +266 -207
- package/_packed_docs/aun/345/210/206/345/270/203/345/274/217/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +64 -31
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +8 -6
- package/_packed_docs/protocol/01-/350/272/253/344/273/275/344/270/216/345/207/255/350/257/201/345/215/217/350/256/256-auth.md +39 -39
- package/_packed_docs/protocol/07-/351/224/231/350/257/257/347/240/201/344/270/216/347/212/266/346/200/201/346/234/272.md +1 -1
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +28 -9
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +20 -2
- package/_packed_docs/protocol/agent.md/SCHEMA.md +4 -0
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +5 -2
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +246 -241
- package/_packed_docs/sdk/09-group-rpc-manual.md +46 -14
- package/_packed_docs/sdk/09-message-rpc-manual.md +40 -2
- package/_packed_docs/sdk/09-storage-rpc-manual.md +69 -38
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +25 -5
- package/_packed_docs/sdk/INDEX.md +78 -18
- package/dist/agent-md-schema.d.ts +2 -0
- package/dist/agent-md-schema.js +46 -9
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +46 -1
- package/dist/agent-md.js +492 -30
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +9 -1
- package/dist/aid-store.js +139 -18
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.d.ts +17 -1
- package/dist/aid.js +67 -7
- package/dist/aid.js.map +1 -1
- package/dist/auth.d.ts +6 -1
- package/dist/auth.js +428 -15
- package/dist/auth.js.map +1 -1
- package/dist/cert-utils.d.ts +1 -1
- package/dist/cert-utils.js +19 -7
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +4 -1
- package/dist/client/delivery.js +264 -68
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.js +29 -14
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +8 -1
- package/dist/client/v2-e2ee.js +893 -529
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +87 -1
- package/dist/client.js +816 -35
- package/dist/client.js.map +1 -1
- package/dist/errors.js +4 -3
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +5 -1
- package/dist/events.js +47 -6
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +85 -3
- package/dist/facades.js +104 -4
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/aid-db.d.ts +5 -0
- package/dist/keystore/aid-db.js +48 -0
- package/dist/keystore/aid-db.js.map +1 -1
- package/dist/keystore/index.d.ts +8 -0
- package/dist/keystore/local-identity-store.js +10 -3
- package/dist/keystore/local-identity-store.js.map +1 -1
- package/dist/keystore/local-token-store.d.ts +6 -1
- package/dist/keystore/local-token-store.js +24 -1
- package/dist/keystore/local-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +98 -0
- package/dist/org-protocol.js +1689 -0
- package/dist/org-protocol.js.map +1 -0
- package/dist/ownership-proof.d.ts +117 -0
- package/dist/ownership-proof.js +436 -0
- package/dist/ownership-proof.js.map +1 -0
- package/dist/ownership-v21.d.ts +178 -0
- package/dist/ownership-v21.js +635 -0
- package/dist/ownership-v21.js.map +1 -0
- package/dist/ownership-workflow.d.ts +82 -0
- package/dist/ownership-workflow.js +872 -0
- package/dist/ownership-workflow.js.map +1 -0
- package/dist/seq-tracker.d.ts +1 -0
- package/dist/seq-tracker.js +10 -1
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +89 -1
- package/dist/tools/cross-sdk-agent.js +338 -7
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +7 -0
- package/dist/transport.js +108 -4
- package/dist/transport.js.map +1 -1
- package/dist/v2/e2ee/decrypt.d.ts +1 -0
- package/dist/v2/e2ee/decrypt.js +9 -1
- package/dist/v2/e2ee/decrypt.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -8
package/dist/client/delivery.js
CHANGED
|
@@ -175,9 +175,8 @@ export class MessageDeliveryEngine {
|
|
|
175
175
|
const pageInlineGeneration = this.captureInlineAckGeneration();
|
|
176
176
|
const deferredServerCursor = Number(coordinator.pendingForwardCursor(ns, pageContigBefore) ?? 0);
|
|
177
177
|
const deferredForwardAck = Number(coordinator.pendingForwardAck(ns, pageContigBefore) ?? 0);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
: null;
|
|
178
|
+
let pageTrackerSnapshot = null;
|
|
179
|
+
let trackerMutated = false;
|
|
181
180
|
response._contig_before = pageContigBefore;
|
|
182
181
|
const cursor = isJsonObject(response.cursor)
|
|
183
182
|
? response.cursor
|
|
@@ -225,6 +224,18 @@ export class MessageDeliveryEngine {
|
|
|
225
224
|
}
|
|
226
225
|
}
|
|
227
226
|
this.ensurePullOperationCurrent(ns);
|
|
227
|
+
if (pageInlineGeneration !== this.captureInlineAckGeneration())
|
|
228
|
+
throw new Error('pull invalidated');
|
|
229
|
+
const previousWindow = client._seqTracker.getSyncState(ns);
|
|
230
|
+
const scanTarget = Math.max(pageContigBefore, ...messages.map((message) => positiveSafeSequenceHint(message.seq)), positiveSafeSequenceHint(response.contiguous_seq), retentionFloor, visibilityFloor, deferredServerCursor);
|
|
231
|
+
const deliveryTarget = scanTarget >= previousWindow.tail - 1
|
|
232
|
+
? Math.max(scanTarget, previousWindow.head) : scanTarget;
|
|
233
|
+
await this.drainOrderedMessages(ns, undefined, false, false, 'pull', undefined, deliveryTarget);
|
|
234
|
+
this.ensurePullOperationCurrent(ns);
|
|
235
|
+
if (pageInlineGeneration !== this.captureInlineAckGeneration())
|
|
236
|
+
throw new Error('pull invalidated');
|
|
237
|
+
pageTrackerSnapshot = client._seqTracker.snapshotNamespace?.(ns) ?? null;
|
|
238
|
+
trackerMutated = true;
|
|
228
239
|
client._seqTracker.onPullResult(ns, messages, afterSeq, Number.isSafeInteger(result.contiguous_seq)
|
|
229
240
|
? result.contiguous_seq
|
|
230
241
|
: undefined);
|
|
@@ -233,16 +244,19 @@ export class MessageDeliveryEngine {
|
|
|
233
244
|
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
234
245
|
}
|
|
235
246
|
if (client._seqTracker.getContiguousSeq(ns) !== pageContigBefore) {
|
|
236
|
-
this.ensurePullOperationCurrent(ns);
|
|
237
|
-
await this.drainOrderedMessages(ns, undefined, false, false, 'pull');
|
|
238
247
|
this.ensurePullOperationCurrent(ns);
|
|
239
248
|
await client._commitSeqTrackerState(ns);
|
|
249
|
+
this.ensurePullOperationCurrent(ns);
|
|
250
|
+
if (pageInlineGeneration !== this.captureInlineAckGeneration())
|
|
251
|
+
throw new Error('pull invalidated');
|
|
240
252
|
}
|
|
241
253
|
committed = true;
|
|
242
254
|
}
|
|
243
255
|
catch (exc) {
|
|
244
|
-
if (this.
|
|
245
|
-
client._seqTracker.restoreNamespaceSnapshot
|
|
256
|
+
if (trackerMutated && pageInlineGeneration === this.captureInlineAckGeneration() && this.isPullOperationCurrent(ns)
|
|
257
|
+
&& !committed && typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
|
|
258
|
+
(client._seqTracker._restoreNamespaceSnapshotPreservingPush ?? client._seqTracker.restoreNamespaceSnapshot)
|
|
259
|
+
.call(client._seqTracker, ns, pageTrackerSnapshot);
|
|
246
260
|
this.dropSeqTrackerPending(ns);
|
|
247
261
|
}
|
|
248
262
|
throw exc;
|
|
@@ -702,6 +716,18 @@ export class MessageDeliveryEngine {
|
|
|
702
716
|
}
|
|
703
717
|
this.schedulePendingGroupPullIfNeeded(ns, 'pull-gate-idle');
|
|
704
718
|
}
|
|
719
|
+
onForegroundPullsDrained() {
|
|
720
|
+
const client = this.runtime.client;
|
|
721
|
+
if (!client._aid || client._sessionOptions?.background_sync === false
|
|
722
|
+
|| client.state !== ConnectionState.READY || client._closing)
|
|
723
|
+
return;
|
|
724
|
+
const ns = `p2p:${client._aid}`;
|
|
725
|
+
const state = this.syncState(ns);
|
|
726
|
+
const maxSeen = client._seqTracker.getMaxSeenSeq(ns);
|
|
727
|
+
if (maxSeen <= state.ack && state.ack >= state.tail - 1)
|
|
728
|
+
return;
|
|
729
|
+
client._safeAsync(this.fillP2pGap());
|
|
730
|
+
}
|
|
705
731
|
recordPendingGroupPull(ns, seq) {
|
|
706
732
|
if (!ns || !Number.isSafeInteger(seq) || seq <= 0)
|
|
707
733
|
return;
|
|
@@ -792,12 +818,12 @@ export class MessageDeliveryEngine {
|
|
|
792
818
|
return;
|
|
793
819
|
}
|
|
794
820
|
if (pullResponse) {
|
|
795
|
-
const published = client._withPullResponseProcessing(ns, () =>
|
|
821
|
+
const published = client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source, deliveryContext));
|
|
796
822
|
if (isPromiseLike(published))
|
|
797
823
|
await published;
|
|
798
824
|
}
|
|
799
825
|
else {
|
|
800
|
-
const published =
|
|
826
|
+
const published = client._publishAppEvent(event, payload, source, deliveryContext);
|
|
801
827
|
if (isPromiseLike(published))
|
|
802
828
|
await published;
|
|
803
829
|
}
|
|
@@ -806,6 +832,12 @@ export class MessageDeliveryEngine {
|
|
|
806
832
|
}
|
|
807
833
|
async publishOrderedGroupChanged(payload, source = 'ordered') {
|
|
808
834
|
const client = this.runtime.client;
|
|
835
|
+
const generation = this.captureInlineAckGeneration();
|
|
836
|
+
const published = client._publishAppEvent('group.changed', payload, source);
|
|
837
|
+
if (isPromiseLike(published))
|
|
838
|
+
await published;
|
|
839
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
840
|
+
throw new Error('pull invalidated');
|
|
809
841
|
if (isJsonObject(payload)) {
|
|
810
842
|
const eventPayload = payload;
|
|
811
843
|
client._groupState?.handleGroupChangedV2Membership?.(eventPayload);
|
|
@@ -816,9 +848,6 @@ export class MessageDeliveryEngine {
|
|
|
816
848
|
client._cleanupDissolvedGroup?.(groupId);
|
|
817
849
|
}
|
|
818
850
|
}
|
|
819
|
-
const published = client._publishAppEvent('group.changed', payload, source);
|
|
820
|
-
if (isPromiseLike(published))
|
|
821
|
-
await published;
|
|
822
851
|
}
|
|
823
852
|
isInstanceScopedMessageEvent(event) {
|
|
824
853
|
return event === 'message.received'
|
|
@@ -1131,6 +1160,7 @@ export class MessageDeliveryEngine {
|
|
|
1131
1160
|
}
|
|
1132
1161
|
async publishMessageRecallTombstone(seq, message, source = 'push') {
|
|
1133
1162
|
const client = this.runtime.client;
|
|
1163
|
+
const generation = this.captureInlineAckGeneration();
|
|
1134
1164
|
const eventPayload = this.recallEventFromMessage(message);
|
|
1135
1165
|
if (!eventPayload)
|
|
1136
1166
|
return false;
|
|
@@ -1144,13 +1174,17 @@ export class MessageDeliveryEngine {
|
|
|
1144
1174
|
client._clientLog.debug(`message.recalled dedup suppressed: seq=${String(seq)} key=${dedupKey}`);
|
|
1145
1175
|
return false;
|
|
1146
1176
|
}
|
|
1177
|
+
await client._publishAppEvent('message.recalled', eventPayload, source);
|
|
1178
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
1179
|
+
throw new Error('pull invalidated');
|
|
1180
|
+
if (this.isPullScopedSource(source))
|
|
1181
|
+
this.ensurePullOperationCurrent(`p2p:${client._aid ?? ''}`);
|
|
1147
1182
|
seen.set(dedupKey, Date.now());
|
|
1148
1183
|
if (seen.size > MESSAGE_RECALL_SEEN_LIMIT) {
|
|
1149
1184
|
const drop = [...seen.entries()].sort((a, b) => a[1] - b[1]).slice(0, seen.size - MESSAGE_RECALL_SEEN_LIMIT);
|
|
1150
1185
|
for (const [oldKey] of drop)
|
|
1151
1186
|
seen.delete(oldKey);
|
|
1152
1187
|
}
|
|
1153
|
-
await client._publishAppEvent('message.recalled', eventPayload, source);
|
|
1154
1188
|
client._clientLog.debug(`message.recalled published: seq=${String(seq)} ids=${JSON.stringify(eventPayload.message_ids)}`);
|
|
1155
1189
|
return true;
|
|
1156
1190
|
}
|
|
@@ -1235,6 +1269,7 @@ export class MessageDeliveryEngine {
|
|
|
1235
1269
|
}
|
|
1236
1270
|
async publishGroupRecallTombstone(groupId, seq, message, source = 'push') {
|
|
1237
1271
|
const client = this.runtime.client;
|
|
1272
|
+
const generation = this.captureInlineAckGeneration();
|
|
1238
1273
|
const eventPayload = this.recallEventFromGroupMessage(message);
|
|
1239
1274
|
if (!eventPayload)
|
|
1240
1275
|
return false;
|
|
@@ -1250,13 +1285,17 @@ export class MessageDeliveryEngine {
|
|
|
1250
1285
|
client._clientLog.debug(`group.message_recalled dedup suppressed: group=${groupId} seq=${String(seq)} key=${dedupKey}`);
|
|
1251
1286
|
return false;
|
|
1252
1287
|
}
|
|
1288
|
+
await client._publishAppEvent('group.message_recalled', eventPayload, source);
|
|
1289
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
1290
|
+
throw new Error('pull invalidated');
|
|
1291
|
+
if (this.isPullScopedSource(source))
|
|
1292
|
+
this.ensurePullOperationCurrent(`group:${groupId}`);
|
|
1253
1293
|
seen.set(dedupKey, Date.now());
|
|
1254
1294
|
if (seen.size > GROUP_RECALL_SEEN_LIMIT) {
|
|
1255
1295
|
const drop = [...seen.entries()].sort((a, b) => a[1] - b[1]).slice(0, seen.size - GROUP_RECALL_SEEN_LIMIT);
|
|
1256
1296
|
for (const [oldKey] of drop)
|
|
1257
1297
|
seen.delete(oldKey);
|
|
1258
1298
|
}
|
|
1259
|
-
await client._publishAppEvent('group.message_recalled', eventPayload, source);
|
|
1260
1299
|
client._clientLog.debug(`group.message_recalled published: group=${groupId} seq=${String(seq)} ids=${JSON.stringify(eventPayload.message_ids)}`);
|
|
1261
1300
|
return true;
|
|
1262
1301
|
}
|
|
@@ -1325,11 +1364,14 @@ export class MessageDeliveryEngine {
|
|
|
1325
1364
|
const generation = this.deliveryChangedGeneration;
|
|
1326
1365
|
const normalized = this.normalizePublishedMessagePayload(event, payload, source);
|
|
1327
1366
|
const dispatcher = client._dispatcher;
|
|
1328
|
-
const result =
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1367
|
+
const result = ['message.received', 'group.message_created', 'message.recalled', 'group.message_recalled', 'group.changed'].includes(event)
|
|
1368
|
+
&& typeof dispatcher._publishAndWait === 'function'
|
|
1369
|
+
? dispatcher._publishAndWait(event, normalized)
|
|
1370
|
+
: typeof dispatcher.enqueue === 'function'
|
|
1371
|
+
? (dispatcher.enqueue(event, normalized), undefined)
|
|
1372
|
+
: typeof dispatcher.publishSyncAware === 'function'
|
|
1373
|
+
? dispatcher.publishSyncAware(event, normalized)
|
|
1374
|
+
: dispatcher.publish?.(event, normalized);
|
|
1333
1375
|
const note = () => {
|
|
1334
1376
|
if (generation === this.deliveryChangedGeneration) {
|
|
1335
1377
|
this.noteDeliveryChanged(event, payload, source, deliveryContext);
|
|
@@ -1460,6 +1502,24 @@ export class MessageDeliveryEngine {
|
|
|
1460
1502
|
return false;
|
|
1461
1503
|
return senderDevice.trim() === String(client._deviceId).trim();
|
|
1462
1504
|
}
|
|
1505
|
+
isSelfSentP2PMessage(message) {
|
|
1506
|
+
if (!isJsonObject(message))
|
|
1507
|
+
return false;
|
|
1508
|
+
const client = this.runtime.client;
|
|
1509
|
+
if (String(message.direction ?? '').trim() !== 'outbound_sync')
|
|
1510
|
+
return false;
|
|
1511
|
+
const selfAid = String(client._aid ?? '').trim().toLowerCase();
|
|
1512
|
+
const senderAid = String(message.sender_aid ?? message.from_aid ?? message.from ?? '').trim().toLowerCase();
|
|
1513
|
+
if (!selfAid || senderAid !== selfAid)
|
|
1514
|
+
return false;
|
|
1515
|
+
const senderDevice = Object.prototype.hasOwnProperty.call(message, 'sender_device_id')
|
|
1516
|
+
? message.sender_device_id
|
|
1517
|
+
: message.from_device_id;
|
|
1518
|
+
return typeof senderDevice === 'string'
|
|
1519
|
+
&& Boolean(senderDevice.trim())
|
|
1520
|
+
&& Boolean(String(client._deviceId ?? '').trim())
|
|
1521
|
+
&& senderDevice.trim() === String(client._deviceId).trim();
|
|
1522
|
+
}
|
|
1463
1523
|
async runPushProcessSerialized(ns, operation, inlineGeneration) {
|
|
1464
1524
|
const client = this.runtime.client;
|
|
1465
1525
|
if (inlineGeneration === undefined) {
|
|
@@ -1589,7 +1649,7 @@ export class MessageDeliveryEngine {
|
|
|
1589
1649
|
// 在旧 seq tracking / ordered publish 之前转入前台 Tail 协调器。
|
|
1590
1650
|
const inlinePresent = hasNonEmptyInlineMessage(msg.inline_message);
|
|
1591
1651
|
const legacyPushSeq = this.realtimeHeadSeq(msg.seq, inlinePresent);
|
|
1592
|
-
if (client._v2Session && client._aid && (msg.seq !== undefined && msg.seq !== null || inlinePresent)) {
|
|
1652
|
+
if ((client._v2Session || inlinePresent) && client._aid && (msg.seq !== undefined && msg.seq !== null || inlinePresent)) {
|
|
1593
1653
|
if (legacyPushSeq === null) {
|
|
1594
1654
|
if (inlinePresent)
|
|
1595
1655
|
await this.recoverInvalidP2PRealtimeHead(`p2p:${client._aid}`, msg.seq);
|
|
@@ -1606,10 +1666,18 @@ export class MessageDeliveryEngine {
|
|
|
1606
1666
|
const ns = `p2p:${client._aid}`;
|
|
1607
1667
|
if (seq > 0)
|
|
1608
1668
|
client._seqTracker.updateMaxSeen(ns, seq);
|
|
1669
|
+
if (encryptedPush) {
|
|
1670
|
+
if (Number(seq) > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
|
|
1671
|
+
client._clientLog.debug(`P2P push triggering Forward: ns=${ns}, seq=${seq}, contiguous=${client._seqTracker.getContiguousSeq(ns)}`);
|
|
1672
|
+
this.fillP2pGap().catch((exc) => client._clientLog.warn(`background gap fill trigger failed: ${formatDeliveryError(exc)}`));
|
|
1673
|
+
}
|
|
1674
|
+
return;
|
|
1675
|
+
}
|
|
1609
1676
|
if (Number(seq) > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
|
|
1610
1677
|
client._clientLog.debug(`P2P push triggering Forward: ns=${ns}, seq=${seq}, contiguous=${client._seqTracker.getContiguousSeq(ns)}`);
|
|
1611
1678
|
this.fillP2pGap().catch((exc) => client._clientLog.warn(`background gap fill trigger failed: ${formatDeliveryError(exc)}`));
|
|
1612
1679
|
}
|
|
1680
|
+
await this.publishOrderedMessage('message.received', ns, seq, msg, 'push');
|
|
1613
1681
|
return;
|
|
1614
1682
|
}
|
|
1615
1683
|
else {
|
|
@@ -1617,6 +1685,8 @@ export class MessageDeliveryEngine {
|
|
|
1617
1685
|
await client._publishEncryptedPushMessage('message.received', 'message.undecryptable', '', seq ?? 0, msg, false);
|
|
1618
1686
|
return;
|
|
1619
1687
|
}
|
|
1688
|
+
if (this.isSelfSentP2PMessage(msg))
|
|
1689
|
+
return;
|
|
1620
1690
|
await client._publishAppEvent('message.received', msg, 'push');
|
|
1621
1691
|
}
|
|
1622
1692
|
}
|
|
@@ -1671,7 +1741,7 @@ export class MessageDeliveryEngine {
|
|
|
1671
1741
|
// 不能让 payload 路径直接推进旧 ordered publish 状态。
|
|
1672
1742
|
const inlinePresent = hasNonEmptyInlineMessage(msg.inline_message);
|
|
1673
1743
|
const legacyPushSeq = this.realtimeHeadSeq(seq, inlinePresent);
|
|
1674
|
-
if (client._v2Session && groupId && (seq !== undefined && seq !== null || inlinePresent)) {
|
|
1744
|
+
if ((client._v2Session || inlinePresent) && groupId && (seq !== undefined && seq !== null || inlinePresent)) {
|
|
1675
1745
|
if (legacyPushSeq === null) {
|
|
1676
1746
|
if (inlinePresent)
|
|
1677
1747
|
await this.recoverInvalidGroupRealtimeHead(groupId, `group:${groupId}`, seq);
|
|
@@ -1694,10 +1764,20 @@ export class MessageDeliveryEngine {
|
|
|
1694
1764
|
const ns = `group:${groupId}`;
|
|
1695
1765
|
if (seq > 0)
|
|
1696
1766
|
client._seqTracker.updateMaxSeen(ns, seq);
|
|
1767
|
+
if (encryptedPush) {
|
|
1768
|
+
if (Number(seq) > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
|
|
1769
|
+
client._clientLog.debug(`group push triggering Forward: group=${groupId}, seq=${seq}, contiguous=${client._seqTracker.getContiguousSeq(ns)}`);
|
|
1770
|
+
this.fillGroupGap(groupId).catch((exc) => client._clientLog.warn(`background gap fill trigger failed: ${formatDeliveryError(exc)}`));
|
|
1771
|
+
}
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1697
1774
|
if (Number(seq) > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
|
|
1698
1775
|
client._clientLog.debug(`group push triggering Forward: group=${groupId}, seq=${seq}, contiguous=${client._seqTracker.getContiguousSeq(ns)}`);
|
|
1699
1776
|
this.fillGroupGap(groupId).catch((exc) => client._clientLog.warn(`background gap fill trigger failed: ${formatDeliveryError(exc)}`));
|
|
1700
1777
|
}
|
|
1778
|
+
if (!this.isSelfSentGroupMessage(msg)) {
|
|
1779
|
+
await this.publishOrderedMessage('group.message_created', ns, seq, msg, 'group-push');
|
|
1780
|
+
}
|
|
1701
1781
|
return;
|
|
1702
1782
|
}
|
|
1703
1783
|
else {
|
|
@@ -1845,6 +1925,7 @@ export class MessageDeliveryEngine {
|
|
|
1845
1925
|
if (!groupId)
|
|
1846
1926
|
return;
|
|
1847
1927
|
const ns = `group_event:${groupId}`;
|
|
1928
|
+
const generation = this.captureInlineAckGeneration();
|
|
1848
1929
|
const requestedAfterSeq = Number(options.afterSeq ?? 0);
|
|
1849
1930
|
const afterSeq = Number.isSafeInteger(requestedAfterSeq) && requestedAfterSeq > 0
|
|
1850
1931
|
? requestedAfterSeq
|
|
@@ -1867,6 +1948,7 @@ export class MessageDeliveryEngine {
|
|
|
1867
1948
|
let filled = 0;
|
|
1868
1949
|
let continuationAfterSeq = 0;
|
|
1869
1950
|
let failed = false;
|
|
1951
|
+
let dissolved = false;
|
|
1870
1952
|
try {
|
|
1871
1953
|
let nextAfterSeq = afterSeq;
|
|
1872
1954
|
const maxPages = singlePage ? 1 : 100;
|
|
@@ -1896,10 +1978,9 @@ export class MessageDeliveryEngine {
|
|
|
1896
1978
|
if (!Array.isArray(events))
|
|
1897
1979
|
return;
|
|
1898
1980
|
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
1981
|
+
let pageTrackerSnapshot = null;
|
|
1982
|
+
let trackerMutated = false;
|
|
1899
1983
|
const eventObjects = events.filter((evt) => isJsonObject(evt));
|
|
1900
|
-
client._seqTracker.onPullResult(ns, eventObjects, nextAfterSeq, Number.isSafeInteger(result.contiguous_seq)
|
|
1901
|
-
? result.contiguous_seq
|
|
1902
|
-
: undefined);
|
|
1903
1984
|
const cursor = isJsonObject(result.cursor)
|
|
1904
1985
|
? result.cursor
|
|
1905
1986
|
: null;
|
|
@@ -1909,13 +1990,6 @@ export class MessageDeliveryEngine {
|
|
|
1909
1990
|
const earliestAvailable = Number(result.earliest_available_event_seq ?? 0);
|
|
1910
1991
|
const visibilityFloor = Math.max(0, Number.isFinite(Number(cursor?.join_seq)) ? Number(cursor?.join_seq) : 0, Number.isFinite(earliestAvailable) && earliestAvailable > 0 ? earliestAvailable - 1 : 0);
|
|
1911
1992
|
const effectiveFloor = Math.max(retentionFloor, serverAckFloor, visibilityFloor);
|
|
1912
|
-
if (effectiveFloor > 0) {
|
|
1913
|
-
const contigBeforeFloor = client._seqTracker.getContiguousSeq(ns);
|
|
1914
|
-
if (contigBeforeFloor < effectiveFloor) {
|
|
1915
|
-
client._clientLog.info(`group.pull_events floor advance: ns=${ns} contiguous=${contigBeforeFloor} -> effective_floor=${effectiveFloor} retention_floor=${retentionFloor} server_ack=${serverAckFloor} visibility_floor=${visibilityFloor}`);
|
|
1916
|
-
client._seqTracker.forceContiguousSeq(ns, effectiveFloor);
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1919
1993
|
const eventSeqs = [];
|
|
1920
1994
|
let hasDissolvedEvent = false;
|
|
1921
1995
|
let publishedEventCount = 0;
|
|
@@ -1945,11 +2019,40 @@ export class MessageDeliveryEngine {
|
|
|
1945
2019
|
if (et !== 'group.message_created')
|
|
1946
2020
|
filled += 1;
|
|
1947
2021
|
}
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
this.
|
|
2022
|
+
try {
|
|
2023
|
+
const scanTarget = Math.max(pageContigBefore, effectiveFloor, positiveSafeSequenceHint(result.contiguous_seq), ...eventSeqs);
|
|
2024
|
+
await this.drainOrderedMessages(ns, undefined, false, false, 'pull', undefined, scanTarget);
|
|
2025
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
2026
|
+
throw new Error('pull invalidated');
|
|
2027
|
+
this.ensurePullOperationCurrent(ns);
|
|
2028
|
+
if (hasDissolvedEvent) {
|
|
2029
|
+
dissolved = true;
|
|
2030
|
+
break;
|
|
2031
|
+
}
|
|
2032
|
+
pageTrackerSnapshot = client._seqTracker.snapshotNamespace?.(ns) ?? null;
|
|
2033
|
+
trackerMutated = true;
|
|
2034
|
+
client._seqTracker.onPullResult(ns, eventObjects, nextAfterSeq, Number.isSafeInteger(result.contiguous_seq)
|
|
2035
|
+
? result.contiguous_seq : undefined);
|
|
2036
|
+
if (client._seqTracker.getContiguousSeq(ns) < effectiveFloor) {
|
|
2037
|
+
client._seqTracker.forceContiguousSeq(ns, effectiveFloor);
|
|
2038
|
+
}
|
|
2039
|
+
if (client._seqTracker.getContiguousSeq(ns) !== pageContigBefore && !hasDissolvedEvent) {
|
|
2040
|
+
await client._commitSeqTrackerState(ns);
|
|
2041
|
+
this.ensurePullOperationCurrent(ns);
|
|
2042
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
2043
|
+
throw new Error('pull invalidated');
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
catch (exc) {
|
|
2047
|
+
if (trackerMutated && generation === this.captureInlineAckGeneration() && this.isPullOperationCurrent(ns)
|
|
2048
|
+
&& typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
|
|
2049
|
+
(client._seqTracker._restoreNamespaceSnapshotPreservingPush ?? client._seqTracker.restoreNamespaceSnapshot)
|
|
2050
|
+
.call(client._seqTracker, ns, pageTrackerSnapshot);
|
|
2051
|
+
this.dropSeqTrackerPending(ns);
|
|
2052
|
+
}
|
|
2053
|
+
throw exc;
|
|
1952
2054
|
}
|
|
2055
|
+
const ackContig = client._seqTracker.getContiguousSeq(ns);
|
|
1953
2056
|
if (eventObjects.length > 0 && ackContig > 0 && ackContig !== pageContigBefore) {
|
|
1954
2057
|
const ackSeq = this.clampAckSeq('group.ack_events', 'event_seq', ns, ackContig);
|
|
1955
2058
|
try {
|
|
@@ -2004,13 +2107,13 @@ export class MessageDeliveryEngine {
|
|
|
2004
2107
|
if (legacyGateToken !== null && typeof client._releasePullGate === 'function') {
|
|
2005
2108
|
client._releasePullGate(ns, legacyGateToken);
|
|
2006
2109
|
}
|
|
2007
|
-
if (singlePage && continuationAfterSeq > afterSeq) {
|
|
2110
|
+
if (!dissolved && singlePage && continuationAfterSeq > afterSeq) {
|
|
2008
2111
|
this.enqueueOnlineUnreadEventHint({
|
|
2009
2112
|
group_id: groupId,
|
|
2010
2113
|
_online_hint_after_event_seq: continuationAfterSeq,
|
|
2011
2114
|
});
|
|
2012
2115
|
}
|
|
2013
|
-
else if (!singlePage && filled > 0 && client._seqTracker.getContiguousSeq(ns) > afterSeq) {
|
|
2116
|
+
else if (!dissolved && !singlePage && filled > 0 && client._seqTracker.getContiguousSeq(ns) > afterSeq) {
|
|
2014
2117
|
void this.fillGroupEventGap(groupId);
|
|
2015
2118
|
}
|
|
2016
2119
|
if (!client._rpcPipeline)
|
|
@@ -2374,7 +2477,7 @@ export class MessageDeliveryEngine {
|
|
|
2374
2477
|
after_seq: state.ack,
|
|
2375
2478
|
limit: pageLimit,
|
|
2376
2479
|
...(headForward ? { max_pages: Math.max(2, requiredPages) } : order[0] === 'tail' ? { max_pages: requiredPages } : {}),
|
|
2377
|
-
},
|
|
2480
|
+
}, true, headForward || tailMissedDuringPull);
|
|
2378
2481
|
}
|
|
2379
2482
|
if (tailHead !== null && this.syncState(ns).ack < this.syncState(ns).tail - 1) {
|
|
2380
2483
|
this.scheduleRealtimeWindowForward(ns);
|
|
@@ -2451,7 +2554,7 @@ export class MessageDeliveryEngine {
|
|
|
2451
2554
|
after_seq: state.ack,
|
|
2452
2555
|
limit: pageLimit,
|
|
2453
2556
|
...(headForward ? { max_pages: Math.max(2, requiredPages) } : order[0] === 'tail' ? { max_pages: requiredPages } : {}),
|
|
2454
|
-
},
|
|
2557
|
+
}, true, headForward || tailMissedDuringPull);
|
|
2455
2558
|
}
|
|
2456
2559
|
if (tailHead !== null && this.syncState(ns).ack < this.syncState(ns).tail - 1) {
|
|
2457
2560
|
this.scheduleRealtimeWindowForward(ns, groupId);
|
|
@@ -2722,10 +2825,10 @@ export class MessageDeliveryEngine {
|
|
|
2722
2825
|
const pending = client._pendingOrderedMsgs.get(ns);
|
|
2723
2826
|
return !pending || pending.size === 0;
|
|
2724
2827
|
}
|
|
2725
|
-
async decodeInlineP2P(row) {
|
|
2828
|
+
async decodeInlineP2P(row, deferStatus) {
|
|
2726
2829
|
const client = this.runtime.client;
|
|
2727
2830
|
if (this.inlineRowKind(row) === 'v2') {
|
|
2728
|
-
const decrypted = await client._decryptV2PushMessage(row);
|
|
2831
|
+
const decrypted = await client._decryptV2PushMessage(row, deferStatus);
|
|
2729
2832
|
return isJsonObject(decrypted) ? decrypted : null;
|
|
2730
2833
|
}
|
|
2731
2834
|
const legacy = isJsonObject(row.legacy_v1) ? row.legacy_v1 : null;
|
|
@@ -2750,16 +2853,20 @@ export class MessageDeliveryEngine {
|
|
|
2750
2853
|
timestamp: row.t_server,
|
|
2751
2854
|
payload: payload,
|
|
2752
2855
|
encrypted: false,
|
|
2753
|
-
direction: String(row.direction ?? '').trim() || (fromAid && fromAid === client._aid ? 'outbound_sync' : 'inbound'),
|
|
2754
2856
|
};
|
|
2857
|
+
const direction = String(row.direction ?? '').trim();
|
|
2858
|
+
if (direction)
|
|
2859
|
+
result.direction = direction;
|
|
2860
|
+
else if (fromAid && fromAid !== client._aid)
|
|
2861
|
+
result.direction = 'inbound';
|
|
2755
2862
|
if (row.sender_device_id !== undefined)
|
|
2756
2863
|
result.sender_device_id = row.sender_device_id;
|
|
2757
2864
|
return result;
|
|
2758
2865
|
}
|
|
2759
|
-
async decodeInlineGroup(row, groupId, groupAid) {
|
|
2866
|
+
async decodeInlineGroup(row, groupId, groupAid, deferStatus) {
|
|
2760
2867
|
const client = this.runtime.client;
|
|
2761
2868
|
if (this.inlineRowKind(row) === 'v2') {
|
|
2762
|
-
const decrypted = await client._decryptV2PushMessage(row);
|
|
2869
|
+
const decrypted = await client._decryptV2PushMessage(row, deferStatus);
|
|
2763
2870
|
if (!isJsonObject(decrypted))
|
|
2764
2871
|
return null;
|
|
2765
2872
|
const decoded = decrypted;
|
|
@@ -2822,17 +2929,24 @@ export class MessageDeliveryEngine {
|
|
|
2822
2929
|
return false;
|
|
2823
2930
|
const client = this.runtime.client;
|
|
2824
2931
|
const tracker = client._seqTracker;
|
|
2825
|
-
|
|
2932
|
+
let snapshot = null;
|
|
2933
|
+
let trackerMutated = false;
|
|
2826
2934
|
let delivered = false;
|
|
2827
2935
|
try {
|
|
2828
|
-
const
|
|
2829
|
-
|
|
2830
|
-
|
|
2936
|
+
const selfSync = (event === 'message.received' && this.isSelfSentP2PMessage(payload))
|
|
2937
|
+
|| (event === 'group.message_created' && this.isSelfSentGroupMessage(payload));
|
|
2938
|
+
if (!selfSync) {
|
|
2939
|
+
const published = client._publishAppEvent(event, payload, 'inline-push');
|
|
2940
|
+
if (isPromiseLike(published))
|
|
2941
|
+
await published;
|
|
2942
|
+
}
|
|
2831
2943
|
if (!this.inlineGenerationIsCurrent(generation))
|
|
2832
2944
|
return false;
|
|
2833
2945
|
delivered = true;
|
|
2834
2946
|
if (!this.inlineGenerationIsCurrent(generation))
|
|
2835
2947
|
return false;
|
|
2948
|
+
snapshot = typeof tracker.snapshotNamespace === 'function' ? tracker.snapshotNamespace(ns) : null;
|
|
2949
|
+
trackerMutated = true;
|
|
2836
2950
|
const needsPull = Boolean(tracker.onMessageSeq(ns, seq));
|
|
2837
2951
|
if (!this.inlineGenerationIsCurrent(generation))
|
|
2838
2952
|
return false;
|
|
@@ -2855,15 +2969,46 @@ export class MessageDeliveryEngine {
|
|
|
2855
2969
|
return true;
|
|
2856
2970
|
}
|
|
2857
2971
|
catch (exc) {
|
|
2858
|
-
if (this.inlineGenerationIsCurrent(generation)
|
|
2859
|
-
&& typeof tracker.restoreNamespaceSnapshot === 'function')
|
|
2860
|
-
tracker.restoreNamespaceSnapshot(ns, snapshot);
|
|
2972
|
+
if (trackerMutated && this.inlineGenerationIsCurrent(generation)
|
|
2973
|
+
&& typeof tracker.restoreNamespaceSnapshot === 'function') {
|
|
2974
|
+
(tracker._restoreNamespaceSnapshotPreservingPush ?? tracker.restoreNamespaceSnapshot).call(tracker, ns, snapshot);
|
|
2975
|
+
}
|
|
2861
2976
|
if (this.inlineGenerationIsCurrent(generation) && delivered)
|
|
2862
2977
|
this.markPublishedSeq(ns, seq);
|
|
2863
2978
|
client._clientLog.debug(`inline realtime consume rolled back: ns=${ns} seq=${seq} err=${formatDeliveryError(exc)}`);
|
|
2864
2979
|
return false;
|
|
2865
2980
|
}
|
|
2866
2981
|
}
|
|
2982
|
+
async commitInlineTerminalDecryptSeq(ns, seq, generation) {
|
|
2983
|
+
const client = this.runtime.client;
|
|
2984
|
+
const tracker = client._seqTracker;
|
|
2985
|
+
if (!this.inlineGenerationIsCurrent(generation) || !this.inlineStateIsCaughtUp(ns, seq))
|
|
2986
|
+
return false;
|
|
2987
|
+
const snapshot = typeof tracker.snapshotNamespace === 'function' ? tracker.snapshotNamespace(ns) : null;
|
|
2988
|
+
try {
|
|
2989
|
+
const needsPull = Boolean(tracker.onMessageSeq(ns, seq));
|
|
2990
|
+
tracker.commitTailWindow(ns, {
|
|
2991
|
+
windowStartSeq: seq,
|
|
2992
|
+
headSeq: seq,
|
|
2993
|
+
coveredThroughSeq: seq,
|
|
2994
|
+
pageLimit: 1,
|
|
2995
|
+
});
|
|
2996
|
+
const committed = this.syncState(ns);
|
|
2997
|
+
if (needsPull || committed.ack !== seq || committed.tail !== seq || committed.head !== seq) {
|
|
2998
|
+
throw new Error('inline realtime terminal cursor did not collapse after consume');
|
|
2999
|
+
}
|
|
3000
|
+
await client._commitSeqTrackerState(ns);
|
|
3001
|
+
return this.inlineGenerationIsCurrent(generation);
|
|
3002
|
+
}
|
|
3003
|
+
catch (exc) {
|
|
3004
|
+
if (this.inlineGenerationIsCurrent(generation)
|
|
3005
|
+
&& typeof tracker.restoreNamespaceSnapshot === 'function') {
|
|
3006
|
+
(tracker._restoreNamespaceSnapshotPreservingPush ?? tracker.restoreNamespaceSnapshot).call(tracker, ns, snapshot);
|
|
3007
|
+
}
|
|
3008
|
+
client._clientLog.debug(`inline realtime terminal consume rolled back: ns=${ns} seq=${seq} err=${formatDeliveryError(exc)}`);
|
|
3009
|
+
return false;
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
2867
3012
|
async tryConsumeInlineP2P(head, ns, pushSeq, expectedVersion, inlineGeneration) {
|
|
2868
3013
|
const client = this.runtime.client;
|
|
2869
3014
|
if (!isJsonObject(head.inline_message))
|
|
@@ -2883,7 +3028,14 @@ export class MessageDeliveryEngine {
|
|
|
2883
3028
|
return false;
|
|
2884
3029
|
let committed = false;
|
|
2885
3030
|
try {
|
|
2886
|
-
const
|
|
3031
|
+
const decryptStatus = {};
|
|
3032
|
+
const decoded = await this.decodeInlineP2P(row, decryptStatus);
|
|
3033
|
+
if (!decoded && decryptStatus.terminal) {
|
|
3034
|
+
if (!await this.commitInlineTerminalDecryptSeq(ns, pushSeq, generation))
|
|
3035
|
+
return false;
|
|
3036
|
+
committed = this.markPendingP2PInlineAckCommitted(ns, pushSeq);
|
|
3037
|
+
return committed;
|
|
3038
|
+
}
|
|
2887
3039
|
if (!this.inlineGenerationIsCurrent(generation) || !decoded || !this.inlineStateIsCaughtUp(ns, pushSeq)
|
|
2888
3040
|
|| !this.decodedInlineMatches(head, decoded, pushSeq))
|
|
2889
3041
|
return false;
|
|
@@ -2921,7 +3073,14 @@ export class MessageDeliveryEngine {
|
|
|
2921
3073
|
return false;
|
|
2922
3074
|
let committed = false;
|
|
2923
3075
|
try {
|
|
2924
|
-
const
|
|
3076
|
+
const decryptStatus = {};
|
|
3077
|
+
const decoded = await this.decodeInlineGroup(row, groupId, groupAid || groupId, decryptStatus);
|
|
3078
|
+
if (!decoded && decryptStatus.terminal) {
|
|
3079
|
+
if (!await this.commitInlineTerminalDecryptSeq(ns, pushSeq, generation))
|
|
3080
|
+
return false;
|
|
3081
|
+
committed = this.markPendingGroupInlineAckCommitted(ns, pushSeq);
|
|
3082
|
+
return committed;
|
|
3083
|
+
}
|
|
2925
3084
|
if (!this.inlineGenerationIsCurrent(generation) || !decoded || !this.inlineStateIsCaughtUp(ns, pushSeq)
|
|
2926
3085
|
|| !this.decodedInlineMatches(head, decoded, pushSeq, groupId))
|
|
2927
3086
|
return false;
|
|
@@ -3040,13 +3199,14 @@ export class MessageDeliveryEngine {
|
|
|
3040
3199
|
const client = this.runtime.client;
|
|
3041
3200
|
if (inlineGeneration !== undefined && !this.inlineGenerationIsCurrent(inlineGeneration))
|
|
3042
3201
|
return;
|
|
3043
|
-
if (!client._v2Session)
|
|
3044
|
-
return;
|
|
3045
3202
|
const pushData = isJsonObject(data) ? data : null;
|
|
3046
3203
|
const rawPushSeq = pushData?.seq;
|
|
3047
3204
|
const ns = client._aid ? `p2p:${client._aid}` : '';
|
|
3048
3205
|
if (!pushData || !ns)
|
|
3049
3206
|
return;
|
|
3207
|
+
const inlinePresent = hasNonEmptyInlineMessage(pushData.inline_message);
|
|
3208
|
+
if (!client._v2Session && !(expectedVersion === 'v1' && inlinePresent))
|
|
3209
|
+
return;
|
|
3050
3210
|
if (this.isVolatileMessage(pushData)) {
|
|
3051
3211
|
await client._v2E2EE.publishV2VolatileMessages([pushData], 'push', false);
|
|
3052
3212
|
return;
|
|
@@ -3057,7 +3217,6 @@ export class MessageDeliveryEngine {
|
|
|
3057
3217
|
this.enqueueOnlineUnreadP2pHint(pushData);
|
|
3058
3218
|
return;
|
|
3059
3219
|
}
|
|
3060
|
-
const inlinePresent = hasNonEmptyInlineMessage(pushData.inline_message);
|
|
3061
3220
|
const pushSeq = this.realtimeHeadSeq(rawPushSeq, inlinePresent);
|
|
3062
3221
|
if (pushSeq === null) {
|
|
3063
3222
|
if (inlinePresent) {
|
|
@@ -3154,8 +3313,8 @@ export class MessageDeliveryEngine {
|
|
|
3154
3313
|
const client = this.runtime.client;
|
|
3155
3314
|
if (inlineGeneration !== undefined && !this.inlineGenerationIsCurrent(inlineGeneration))
|
|
3156
3315
|
return;
|
|
3157
|
-
if (!isJsonObject(data)
|
|
3158
|
-
client._clientLog.debug(
|
|
3316
|
+
if (!isJsonObject(data)) {
|
|
3317
|
+
client._clientLog.debug('_onRawGroupV2MessageCreated skipped: is_object=false');
|
|
3159
3318
|
return;
|
|
3160
3319
|
}
|
|
3161
3320
|
const d = data;
|
|
@@ -3163,6 +3322,8 @@ export class MessageDeliveryEngine {
|
|
|
3163
3322
|
const groupId = typeof d.group_id === 'string' ? d.group_id.trim() : '';
|
|
3164
3323
|
const rawSeq = d.seq;
|
|
3165
3324
|
const inlinePresent = hasNonEmptyInlineMessage(d.inline_message);
|
|
3325
|
+
if (!client._v2Session && !(expectedVersion === 'v1' && inlinePresent))
|
|
3326
|
+
return;
|
|
3166
3327
|
const seq = this.realtimeHeadSeq(rawSeq, inlinePresent);
|
|
3167
3328
|
if (!groupId) {
|
|
3168
3329
|
client._clientLog.debug(`_onRawGroupV2MessageCreated skipped: group=${groupId || '<empty>'}, seq=${String(d.seq ?? '')}`);
|
|
@@ -3621,8 +3782,9 @@ export class MessageDeliveryEngine {
|
|
|
3621
3782
|
}
|
|
3622
3783
|
return params;
|
|
3623
3784
|
}
|
|
3624
|
-
async drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true, source = 'push', context) {
|
|
3785
|
+
async drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true, source = 'push', context, throughSeq) {
|
|
3625
3786
|
const client = this.runtime.client;
|
|
3787
|
+
const generation = this.captureInlineAckGeneration();
|
|
3626
3788
|
const pullScoped = this.isPullScopedSource(source);
|
|
3627
3789
|
if (pullScoped)
|
|
3628
3790
|
this.ensurePullOperationCurrent(ns);
|
|
@@ -3638,11 +3800,11 @@ export class MessageDeliveryEngine {
|
|
|
3638
3800
|
this.ensurePullOperationCurrent(ns);
|
|
3639
3801
|
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
3640
3802
|
const ready = [...queue.keys()]
|
|
3641
|
-
.filter((seq) => seq <= contig && (beforeSeq === undefined || seq < beforeSeq))
|
|
3803
|
+
.filter((seq) => seq <= Math.max(contig, throughSeq ?? contig) && (beforeSeq === undefined || seq < beforeSeq))
|
|
3642
3804
|
.sort((a, b) => a - b);
|
|
3643
3805
|
let seq = ready[0];
|
|
3644
3806
|
if (seq === undefined) {
|
|
3645
|
-
const nextSeq = contig + 1;
|
|
3807
|
+
const nextSeq = Math.max(contig, throughSeq ?? contig) + 1;
|
|
3646
3808
|
if (beforeSeq !== undefined && nextSeq >= beforeSeq)
|
|
3647
3809
|
break;
|
|
3648
3810
|
if (!queue.has(nextSeq))
|
|
@@ -3652,10 +3814,10 @@ export class MessageDeliveryEngine {
|
|
|
3652
3814
|
if (seq === undefined)
|
|
3653
3815
|
continue;
|
|
3654
3816
|
const item = queue.get(seq);
|
|
3655
|
-
queue.delete(seq);
|
|
3656
3817
|
if (!item)
|
|
3657
3818
|
continue;
|
|
3658
3819
|
if (client._pushedSeqs.get(ns)?.has(seq)) {
|
|
3820
|
+
queue.delete(seq);
|
|
3659
3821
|
client._clientLog.debug(`publish ordered drain skipped duplicate: ns=${ns}, seq=${seq}, event=${item.event}`);
|
|
3660
3822
|
client._markOrderedSeqDelivered(ns, seq);
|
|
3661
3823
|
continue;
|
|
@@ -3673,10 +3835,18 @@ export class MessageDeliveryEngine {
|
|
|
3673
3835
|
}
|
|
3674
3836
|
}
|
|
3675
3837
|
await this.publishOrderedQueueItem(ns, item.event, seq, item.payload, itemSource, pullResponse, itemContext);
|
|
3838
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
3839
|
+
throw new Error('pull invalidated');
|
|
3676
3840
|
if (pullScoped)
|
|
3677
3841
|
this.ensurePullOperationCurrent(ns);
|
|
3842
|
+
queue.delete(seq);
|
|
3843
|
+
if (item.event === 'group.changed' && isJsonObject(item.payload)
|
|
3844
|
+
&& item.payload.action === 'dissolved')
|
|
3845
|
+
break;
|
|
3678
3846
|
this.markPublishedSeq(ns, seq);
|
|
3679
3847
|
client._markOrderedSeqDelivered(ns, seq);
|
|
3848
|
+
if (throughSeq !== undefined)
|
|
3849
|
+
throughSeq = Math.max(throughSeq, seq);
|
|
3680
3850
|
delivered = true;
|
|
3681
3851
|
client._clientLog.debug(`publish ordered drain delivered: ns=${ns}, seq=${seq}, event=${item.event}`);
|
|
3682
3852
|
}
|
|
@@ -3720,6 +3890,7 @@ export class MessageDeliveryEngine {
|
|
|
3720
3890
|
}
|
|
3721
3891
|
async publishOrderedMessageInternal(event, ns, seq, payload, source, context) {
|
|
3722
3892
|
const client = this.runtime.client;
|
|
3893
|
+
const generation = this.captureInlineAckGeneration();
|
|
3723
3894
|
const pullScoped = this.isPullScopedSource(source);
|
|
3724
3895
|
if (pullScoped)
|
|
3725
3896
|
this.ensurePullOperationCurrent(ns);
|
|
@@ -3739,6 +3910,11 @@ export class MessageDeliveryEngine {
|
|
|
3739
3910
|
client._pendingOrderedMsgs.delete(ns);
|
|
3740
3911
|
return false;
|
|
3741
3912
|
}
|
|
3913
|
+
if (event === 'message.received' && this.isSelfSentP2PMessage(payload)) {
|
|
3914
|
+
this.markPublishedSeq(ns, seqNum);
|
|
3915
|
+
client._clientLog.debug(`publish ordered skipped current-device self-sync: ns=${ns}, seq=${seqNum}`);
|
|
3916
|
+
return false;
|
|
3917
|
+
}
|
|
3742
3918
|
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
3743
3919
|
if (seqNum > contig && seqNum !== contig + 1) {
|
|
3744
3920
|
client._clientLog.debug(`publish ordered enqueue(gap): event=${event}, ns=${ns}, seq=${seqNum}, contiguous=${contig}`);
|
|
@@ -3752,13 +3928,20 @@ export class MessageDeliveryEngine {
|
|
|
3752
3928
|
client._clientLog.debug(`publish ordered skipped after-drain duplicate: event=${event}, ns=${ns}, seq=${seqNum}`);
|
|
3753
3929
|
return false;
|
|
3754
3930
|
}
|
|
3931
|
+
if (event === 'message.received' && this.isSelfSentP2PMessage(payload)) {
|
|
3932
|
+
this.markPublishedSeq(ns, seqNum);
|
|
3933
|
+
client._clientLog.debug(`publish pulled skipped current-device self-sync: ns=${ns}, seq=${seqNum}`);
|
|
3934
|
+
return false;
|
|
3935
|
+
}
|
|
3755
3936
|
const queue = client._pendingOrderedMsgs.get(ns);
|
|
3756
|
-
queue?.delete(seqNum);
|
|
3757
|
-
if (queue && queue.size === 0)
|
|
3758
|
-
client._pendingOrderedMsgs.delete(ns);
|
|
3759
3937
|
await this.publishOrderedQueueItem(ns, event, seqNum, payload, source, false, context);
|
|
3938
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
3939
|
+
throw new Error('pull invalidated');
|
|
3760
3940
|
if (pullScoped)
|
|
3761
3941
|
this.ensurePullOperationCurrent(ns);
|
|
3942
|
+
queue?.delete(seqNum);
|
|
3943
|
+
if (queue && queue.size === 0)
|
|
3944
|
+
client._pendingOrderedMsgs.delete(ns);
|
|
3762
3945
|
this.markPublishedSeq(ns, seqNum);
|
|
3763
3946
|
client._markOrderedSeqDelivered(ns, seqNum);
|
|
3764
3947
|
client._clientLog.debug(`publish ordered delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
|
|
@@ -3772,6 +3955,7 @@ export class MessageDeliveryEngine {
|
|
|
3772
3955
|
}
|
|
3773
3956
|
async publishPulledMessage(event, ns, seq, payload, persist = true, source = 'pull') {
|
|
3774
3957
|
const client = this.runtime.client;
|
|
3958
|
+
const generation = this.captureInlineAckGeneration();
|
|
3775
3959
|
const pullScoped = this.isPullScopedSource(source);
|
|
3776
3960
|
if (pullScoped)
|
|
3777
3961
|
this.ensurePullOperationCurrent(ns);
|
|
@@ -3799,16 +3983,23 @@ export class MessageDeliveryEngine {
|
|
|
3799
3983
|
client._pendingOrderedMsgs.delete(ns);
|
|
3800
3984
|
return false;
|
|
3801
3985
|
}
|
|
3986
|
+
if (event === 'message.received' && this.isSelfSentP2PMessage(payload)) {
|
|
3987
|
+
this.markPublishedSeq(ns, seqNum);
|
|
3988
|
+
client._clientLog.debug('publish pulled skipped current-device self-sync');
|
|
3989
|
+
return source === 'inline_push';
|
|
3990
|
+
}
|
|
3802
3991
|
await this.drainOrderedMessages(ns, seqNum, true, persist, source);
|
|
3803
3992
|
if (pullScoped)
|
|
3804
3993
|
this.ensurePullOperationCurrent(ns);
|
|
3805
|
-
queue?.delete(seqNum);
|
|
3806
|
-
if (queue && queue.size === 0)
|
|
3807
|
-
client._pendingOrderedMsgs.delete(ns);
|
|
3808
3994
|
if (event === 'message.recalled') {
|
|
3809
3995
|
const recallPublished = await this.publishMessageRecallTombstone(seqNum, payload, source);
|
|
3996
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
3997
|
+
throw new Error('pull invalidated');
|
|
3810
3998
|
if (pullScoped)
|
|
3811
3999
|
this.ensurePullOperationCurrent(ns);
|
|
4000
|
+
queue?.delete(seqNum);
|
|
4001
|
+
if (queue && queue.size === 0)
|
|
4002
|
+
client._pendingOrderedMsgs.delete(ns);
|
|
3812
4003
|
this.markPublishedSeq(ns, seqNum);
|
|
3813
4004
|
client._clientLog.debug(`publish pulled delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
|
|
3814
4005
|
await this.drainOrderedMessages(ns, undefined, true, persist, source);
|
|
@@ -3819,8 +4010,13 @@ export class MessageDeliveryEngine {
|
|
|
3819
4010
|
const published = client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source));
|
|
3820
4011
|
if (isPromiseLike(published))
|
|
3821
4012
|
await published;
|
|
4013
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
4014
|
+
throw new Error('pull invalidated');
|
|
3822
4015
|
if (pullScoped)
|
|
3823
4016
|
this.ensurePullOperationCurrent(ns);
|
|
4017
|
+
queue?.delete(seqNum);
|
|
4018
|
+
if (queue && queue.size === 0)
|
|
4019
|
+
client._pendingOrderedMsgs.delete(ns);
|
|
3824
4020
|
this.markPublishedSeq(ns, seqNum);
|
|
3825
4021
|
client._clientLog.debug(`publish pulled delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
|
|
3826
4022
|
await this.drainOrderedMessages(ns, undefined, true, persist, source);
|