@agentunion/fastaun 0.5.18 → 0.5.20
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 +172 -79
- package/_packed_docs/CHANGELOG.md +172 -79
- package/_packed_docs/INDEX.md +244 -116
- package/_packed_docs/KITE_DOCS_GUIDE.md +98 -69
- package/_packed_docs/agent.md/SCHEMA.md +245 -205
- package/_packed_docs/agent.md//350/277/234/347/250/213agent.md/347/274/223/345/255/230/344/270/216etag/351/200/217/344/274/240/346/226/271/346/241/210.md +11 -11
- 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 +42 -15
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +4 -4
- 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 +45 -134
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +1072 -1072
- 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 +5 -20
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +687 -668
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +19 -28
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/211.md +443 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.0.md +161 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +658 -0
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2113.0.md +233 -0
- package/_packed_docs/protocol/agent.md/SCHEMA.md +4 -0
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +1 -1
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +68 -61
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +1 -1
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +279 -285
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +3 -3
- package/_packed_docs/sdk/09-group-rpc-manual.md +271 -235
- package/_packed_docs/sdk/09-message-rpc-manual.md +47 -6
- package/_packed_docs/sdk/09-storage-rpc-manual.md +70 -68
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +63 -34
- package/_packed_docs/sdk/INDEX.md +105 -34
- package/_packed_docs/sdk/Ownership-SDK/345/256/236/347/216/260/346/214/207/345/215/227.md +169 -0
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/227.md +349 -0
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +922 -0
- package/_packed_docs/sdk/Ownership-SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +270 -0
- package/_packed_docs/sdk/Ownership2.1-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +300 -0
- package/_packed_docs/sdk/Ownership2.1-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +382 -0
- package/_packed_docs/sdk/Ownership2.1-TDD/345/210/206/351/230/266/346/256/265/345/256/236/346/226/275/350/256/241/345/210/222.md +708 -0
- package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +476 -0
- package/_packed_docs/sdk/Ownership/347/256/241/347/220/206/344/270/223/351/242/230.md +559 -0
- package/_packed_docs/sdk/ProtectedHeaders/347/224/250/346/263/225/344/270/223/351/242/230.md +299 -0
- package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +173 -0
- package/_packed_docs/sdk//345/207/255/350/257/201/344/270/216/347/273/204/347/273/207/346/234/215/345/212/241SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +211 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/345/256/236/347/216/260/347/247/273/346/244/215/346/214/207/345/215/227.md +331 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273SDK/351/233/206/346/210/220/346/214/207/345/215/227.md +207 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/344/273/243/347/240/201/345/256/241/346/237/245/346/212/245/345/221/212.md +252 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/345/256/236/346/226/275/350/256/241/345/210/222.md +156 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/346/226/271/346/241/210.md +273 -0
- package/_packed_docs//350/267/250/350/256/276/345/244/207/345/267/262/350/257/273/346/234/252/350/257/273/350/257/246/347/273/206/350/256/276/350/256/241.md +211 -0
- package/dist/agent-md-schema.d.ts +15 -1
- package/dist/agent-md-schema.js +87 -24
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +54 -2
- package/dist/agent-md.js +637 -53
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +38 -59
- package/dist/aid-store.js +300 -265
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.js +30 -11
- package/dist/aid.js.map +1 -1
- package/dist/auth.d.ts +6 -1
- package/dist/auth.js +500 -15
- package/dist/auth.js.map +1 -1
- package/dist/cert-utils.d.ts +44 -2
- package/dist/cert-utils.js +149 -49
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +11 -1
- package/dist/client/delivery.js +321 -62
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +1 -0
- package/dist/client/rpc-pipeline.js +142 -82
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +1 -0
- package/dist/client/v2-e2ee.js +166 -96
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +84 -1
- package/dist/client.js +780 -30
- 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 +9 -3
- package/dist/events.js +69 -11
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +83 -3
- package/dist/facades.js +100 -6
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +9 -4
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/keystore/aid-db.d.ts +5 -0
- package/dist/keystore/aid-db.js +57 -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 +1753 -0
- package/dist/org-protocol.js.map +1 -0
- package/dist/ownership-proof.d.ts +82 -5
- package/dist/ownership-proof.js +327 -31
- package/dist/ownership-proof.js.map +1 -1
- package/dist/ownership-v21.d.ts +196 -0
- package/dist/ownership-v21.js +712 -0
- package/dist/ownership-v21.js.map +1 -0
- package/dist/ownership-workflow.d.ts +87 -0
- package/dist/ownership-workflow.js +1182 -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 +90 -1
- package/dist/tools/cross-sdk-agent.js +441 -7
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +8 -0
- package/dist/transport.js +118 -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 +1 -1
package/dist/client/delivery.js
CHANGED
|
@@ -9,6 +9,7 @@ const MESSAGE_RECALL_SEEN_LIMIT = 10_000;
|
|
|
9
9
|
const GROUP_RECALL_SEEN_LIMIT = 10_000;
|
|
10
10
|
const VOLATILE_SEEN_LIMIT = 10_000;
|
|
11
11
|
const SEQ_TRACKER_PERSIST_FLUSH_DELAY_MS = 200;
|
|
12
|
+
const BACKGROUND_PULL_RETRY_DELAY_MS = 250;
|
|
12
13
|
const DELIVERY_CHANGED_MESSAGE_EVENTS = new Set([
|
|
13
14
|
'message.received',
|
|
14
15
|
'group.message_created',
|
|
@@ -80,6 +81,11 @@ export class MessageDeliveryEngine {
|
|
|
80
81
|
pendingGroupPullUpper = null;
|
|
81
82
|
onlineUnreadHintTargets = null;
|
|
82
83
|
onlineUnreadHintTasks = null;
|
|
84
|
+
backgroundPullNeeds = new Map();
|
|
85
|
+
backgroundPullTasks = new Set();
|
|
86
|
+
backgroundPullCheckQueued = false;
|
|
87
|
+
backgroundPullTimer = null;
|
|
88
|
+
backgroundPullTimerAt = 0;
|
|
83
89
|
pendingP2PInlineAcks = null;
|
|
84
90
|
pendingGroupInlineAcks = null;
|
|
85
91
|
pendingGroupInlineAckNamespaces = null;
|
|
@@ -175,9 +181,8 @@ export class MessageDeliveryEngine {
|
|
|
175
181
|
const pageInlineGeneration = this.captureInlineAckGeneration();
|
|
176
182
|
const deferredServerCursor = Number(coordinator.pendingForwardCursor(ns, pageContigBefore) ?? 0);
|
|
177
183
|
const deferredForwardAck = Number(coordinator.pendingForwardAck(ns, pageContigBefore) ?? 0);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
: null;
|
|
184
|
+
let pageTrackerSnapshot = null;
|
|
185
|
+
let trackerMutated = false;
|
|
181
186
|
response._contig_before = pageContigBefore;
|
|
182
187
|
const cursor = isJsonObject(response.cursor)
|
|
183
188
|
? response.cursor
|
|
@@ -225,6 +230,18 @@ export class MessageDeliveryEngine {
|
|
|
225
230
|
}
|
|
226
231
|
}
|
|
227
232
|
this.ensurePullOperationCurrent(ns);
|
|
233
|
+
if (pageInlineGeneration !== this.captureInlineAckGeneration())
|
|
234
|
+
throw new Error('pull invalidated');
|
|
235
|
+
const previousWindow = client._seqTracker.getSyncState(ns);
|
|
236
|
+
const scanTarget = Math.max(pageContigBefore, ...messages.map((message) => positiveSafeSequenceHint(message.seq)), positiveSafeSequenceHint(response.contiguous_seq), retentionFloor, visibilityFloor, deferredServerCursor);
|
|
237
|
+
const deliveryTarget = scanTarget >= previousWindow.tail - 1
|
|
238
|
+
? Math.max(scanTarget, previousWindow.head) : scanTarget;
|
|
239
|
+
await this.drainOrderedMessages(ns, undefined, false, false, 'pull', undefined, deliveryTarget);
|
|
240
|
+
this.ensurePullOperationCurrent(ns);
|
|
241
|
+
if (pageInlineGeneration !== this.captureInlineAckGeneration())
|
|
242
|
+
throw new Error('pull invalidated');
|
|
243
|
+
pageTrackerSnapshot = client._seqTracker.snapshotNamespace?.(ns) ?? null;
|
|
244
|
+
trackerMutated = true;
|
|
228
245
|
client._seqTracker.onPullResult(ns, messages, afterSeq, Number.isSafeInteger(result.contiguous_seq)
|
|
229
246
|
? result.contiguous_seq
|
|
230
247
|
: undefined);
|
|
@@ -233,16 +250,19 @@ export class MessageDeliveryEngine {
|
|
|
233
250
|
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
234
251
|
}
|
|
235
252
|
if (client._seqTracker.getContiguousSeq(ns) !== pageContigBefore) {
|
|
236
|
-
this.ensurePullOperationCurrent(ns);
|
|
237
|
-
await this.drainOrderedMessages(ns, undefined, false, false, 'pull');
|
|
238
253
|
this.ensurePullOperationCurrent(ns);
|
|
239
254
|
await client._commitSeqTrackerState(ns);
|
|
255
|
+
this.ensurePullOperationCurrent(ns);
|
|
256
|
+
if (pageInlineGeneration !== this.captureInlineAckGeneration())
|
|
257
|
+
throw new Error('pull invalidated');
|
|
240
258
|
}
|
|
241
259
|
committed = true;
|
|
242
260
|
}
|
|
243
261
|
catch (exc) {
|
|
244
|
-
if (this.
|
|
245
|
-
client._seqTracker.restoreNamespaceSnapshot
|
|
262
|
+
if (trackerMutated && pageInlineGeneration === this.captureInlineAckGeneration() && this.isPullOperationCurrent(ns)
|
|
263
|
+
&& !committed && typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
|
|
264
|
+
(client._seqTracker._restoreNamespaceSnapshotPreservingPush ?? client._seqTracker.restoreNamespaceSnapshot)
|
|
265
|
+
.call(client._seqTracker, ns, pageTrackerSnapshot);
|
|
246
266
|
this.dropSeqTrackerPending(ns);
|
|
247
267
|
}
|
|
248
268
|
throw exc;
|
|
@@ -408,6 +428,13 @@ export class MessageDeliveryEngine {
|
|
|
408
428
|
this.deliveryChangedGeneration += 1;
|
|
409
429
|
this.pendingDeliveryChanges = null;
|
|
410
430
|
this.inlineAckGeneration += 1;
|
|
431
|
+
if (this.backgroundPullTimer)
|
|
432
|
+
clearTimeout(this.backgroundPullTimer);
|
|
433
|
+
this.backgroundPullTimer = null;
|
|
434
|
+
this.backgroundPullTimerAt = 0;
|
|
435
|
+
this.backgroundPullNeeds.clear();
|
|
436
|
+
this.backgroundPullTasks.clear();
|
|
437
|
+
this.backgroundPullCheckQueued = false;
|
|
411
438
|
void this.runtime.client._rpcPipeline?.invalidatePulls?.();
|
|
412
439
|
this.pendingP2PInlineAcks = null;
|
|
413
440
|
this.pendingGroupInlineAcks = null;
|
|
@@ -679,6 +706,7 @@ export class MessageDeliveryEngine {
|
|
|
679
706
|
}
|
|
680
707
|
endRealtimeSync(ns) {
|
|
681
708
|
this.realtimeSyncing?.delete(ns);
|
|
709
|
+
this.requestBackgroundPullRecovery();
|
|
682
710
|
this.runtime.client._rpcPipeline?.requestPullDrainedCheck?.();
|
|
683
711
|
}
|
|
684
712
|
hasPendingPull(ns) {
|
|
@@ -689,6 +717,7 @@ export class MessageDeliveryEngine {
|
|
|
689
717
|
}
|
|
690
718
|
hasAnyPendingPull() {
|
|
691
719
|
return [...(this.pendingGroupPullUpper?.values() ?? [])].some((seq) => seq > 0)
|
|
720
|
+
|| this.backgroundPullNeeds.size > 0
|
|
692
721
|
|| Boolean(this.onlineUnreadHintTasks?.size)
|
|
693
722
|
|| Boolean(this.realtimeSyncing?.size)
|
|
694
723
|
|| Boolean(this.runtime.client._v2E2EE?.hasPendingSenderIKWork?.());
|
|
@@ -696,12 +725,136 @@ export class MessageDeliveryEngine {
|
|
|
696
725
|
onPullGateIdle(ns) {
|
|
697
726
|
if (!ns)
|
|
698
727
|
return;
|
|
728
|
+
this.requestBackgroundPullRecovery();
|
|
699
729
|
if (ns.startsWith('p2p:')) {
|
|
700
730
|
this.runtime.client._schedulePendingP2pPullIfNeeded?.(ns, 'pull-gate-idle');
|
|
701
731
|
return;
|
|
702
732
|
}
|
|
703
733
|
this.schedulePendingGroupPullIfNeeded(ns, 'pull-gate-idle');
|
|
704
734
|
}
|
|
735
|
+
recordBackgroundPullNeed(ns, throughSeq = 0, probe = false) {
|
|
736
|
+
const client = this.runtime.client;
|
|
737
|
+
if (client._sessionOptions?.background_sync === false || !client._aid
|
|
738
|
+
|| !(ns === `p2p:${client._aid}` || ns.startsWith('group:') || ns.startsWith('group_event:')))
|
|
739
|
+
return;
|
|
740
|
+
const need = this.backgroundPullNeeds.get(ns) ?? {
|
|
741
|
+
throughSeq: 0, hasMore: false, probe, lastAfter: -1, retryAt: 0,
|
|
742
|
+
};
|
|
743
|
+
need.throughSeq = Math.max(need.throughSeq, throughSeq);
|
|
744
|
+
need.probe ||= probe;
|
|
745
|
+
this.backgroundPullNeeds.set(ns, need);
|
|
746
|
+
}
|
|
747
|
+
observeBackgroundPullResponse(ns, afterSeq, result, limit = 50) {
|
|
748
|
+
if (!isJsonObject(result))
|
|
749
|
+
return;
|
|
750
|
+
const response = result;
|
|
751
|
+
const rows = ns.startsWith('group_event:') ? response.events : response.messages;
|
|
752
|
+
if (!Array.isArray(rows))
|
|
753
|
+
return;
|
|
754
|
+
const cursor = isJsonObject(response.cursor)
|
|
755
|
+
? response.cursor : null;
|
|
756
|
+
const throughSeq = Math.max(positiveSafeSequenceHint(cursor?.latest_seq), positiveSafeSequenceHint(response.latest_seq), positiveSafeSequenceHint(response.latest_message_seq), positiveSafeSequenceHint(response.latest_event_seq), ...rows.map((row) => row && typeof row === 'object' && row.storage !== 'volatile'
|
|
757
|
+
? positiveSafeSequenceHint(row.seq ?? row.event_seq) : 0));
|
|
758
|
+
this.recordBackgroundPullNeed(ns, throughSeq);
|
|
759
|
+
const need = this.backgroundPullNeeds.get(ns);
|
|
760
|
+
if (!need || afterSeq < need.lastAfter)
|
|
761
|
+
return;
|
|
762
|
+
need.lastAfter = afterSeq;
|
|
763
|
+
need.hasMore = response.has_more === true || positiveSafeSequenceHint(response.remaining) > 0
|
|
764
|
+
|| (!('has_more' in response) && limit > 0 && rows.length >= limit);
|
|
765
|
+
need.probe = false;
|
|
766
|
+
}
|
|
767
|
+
finishBackgroundPull(ns, generation, afterSeq, failed) {
|
|
768
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
769
|
+
return;
|
|
770
|
+
const need = this.backgroundPullNeeds.get(ns);
|
|
771
|
+
if (need) {
|
|
772
|
+
if (failed)
|
|
773
|
+
need.probe = true;
|
|
774
|
+
else
|
|
775
|
+
need.probe = false;
|
|
776
|
+
need.retryAt = failed || this.syncState(ns).ack <= afterSeq
|
|
777
|
+
? Date.now() + BACKGROUND_PULL_RETRY_DELAY_MS : 0;
|
|
778
|
+
}
|
|
779
|
+
this.requestBackgroundPullRecovery();
|
|
780
|
+
}
|
|
781
|
+
requestBackgroundPullRecovery() {
|
|
782
|
+
if (this.backgroundPullCheckQueued || this.backgroundPullNeeds.size === 0)
|
|
783
|
+
return;
|
|
784
|
+
this.backgroundPullCheckQueued = true;
|
|
785
|
+
const generation = this.captureInlineAckGeneration();
|
|
786
|
+
queueMicrotask(() => {
|
|
787
|
+
this.backgroundPullCheckQueued = false;
|
|
788
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
789
|
+
return;
|
|
790
|
+
const client = this.runtime.client;
|
|
791
|
+
if (client._sessionOptions?.background_sync === false || client.state !== ConnectionState.READY || client._closing)
|
|
792
|
+
return;
|
|
793
|
+
let retryAt = Infinity;
|
|
794
|
+
for (const [ns, need] of this.backgroundPullNeeds) {
|
|
795
|
+
if (this.backgroundPullTasks.has(ns) || this.realtimeSyncing?.has(ns) || this.onlineUnreadHintTasks?.has(ns))
|
|
796
|
+
continue;
|
|
797
|
+
const eventScope = ns.startsWith('group_event:');
|
|
798
|
+
const filling = [...(client._gapFillDone?.keys?.() ?? [])].some((key) => eventScope
|
|
799
|
+
? String(key) === `group_event_pull:${ns}`
|
|
800
|
+
: ns.startsWith('p2p:') ? String(key).startsWith('p2p:') : String(key).startsWith(`group_msg:${ns.slice(6)}:`));
|
|
801
|
+
if (filling || (!eventScope && client._rpcPipeline?.hasPullActivity?.(ns, false))
|
|
802
|
+
|| (eventScope && ['pulling', 'processing', 'pulled'].includes(client._rpcPipeline?.getPullLifecycle?.(ns))))
|
|
803
|
+
continue;
|
|
804
|
+
if (this.syncState(ns).ack >= need.throughSeq && !need.hasMore && !need.probe) {
|
|
805
|
+
this.backgroundPullNeeds.delete(ns);
|
|
806
|
+
continue;
|
|
807
|
+
}
|
|
808
|
+
if (need.retryAt > Date.now()) {
|
|
809
|
+
retryAt = Math.min(retryAt, need.retryAt);
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
this.backgroundPullTasks.add(ns);
|
|
813
|
+
const task = Promise.resolve().then(() => {
|
|
814
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
815
|
+
return;
|
|
816
|
+
return eventScope ? this.fillGroupEventGap(ns.slice('group_event:'.length))
|
|
817
|
+
: ns.startsWith('group:') ? this.fillGroupGap(ns.slice(6)) : this.fillP2pGap();
|
|
818
|
+
}).finally(() => {
|
|
819
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
820
|
+
return;
|
|
821
|
+
this.backgroundPullTasks.delete(ns);
|
|
822
|
+
this.requestBackgroundPullRecovery();
|
|
823
|
+
});
|
|
824
|
+
client._safeAsync(task);
|
|
825
|
+
}
|
|
826
|
+
if (this.backgroundPullNeeds.size === 0) {
|
|
827
|
+
if (this.backgroundPullTimer)
|
|
828
|
+
clearTimeout(this.backgroundPullTimer);
|
|
829
|
+
this.backgroundPullTimer = null;
|
|
830
|
+
this.backgroundPullTimerAt = 0;
|
|
831
|
+
client._rpcPipeline?.requestPullDrainedCheck?.();
|
|
832
|
+
}
|
|
833
|
+
if (Number.isFinite(retryAt) && (!this.backgroundPullTimer || retryAt < this.backgroundPullTimerAt)) {
|
|
834
|
+
if (this.backgroundPullTimer)
|
|
835
|
+
clearTimeout(this.backgroundPullTimer);
|
|
836
|
+
this.backgroundPullTimerAt = retryAt;
|
|
837
|
+
this.backgroundPullTimer = setTimeout(() => {
|
|
838
|
+
this.backgroundPullTimer = null;
|
|
839
|
+
this.backgroundPullTimerAt = 0;
|
|
840
|
+
this.requestBackgroundPullRecovery();
|
|
841
|
+
}, Math.max(0, retryAt - Date.now()));
|
|
842
|
+
this.backgroundPullTimer.unref?.();
|
|
843
|
+
}
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
onForegroundPullsDrained() {
|
|
847
|
+
const client = this.runtime.client;
|
|
848
|
+
if (!client._aid || client._sessionOptions?.background_sync === false
|
|
849
|
+
|| client.state !== ConnectionState.READY || client._closing)
|
|
850
|
+
return;
|
|
851
|
+
const ns = `p2p:${client._aid}`;
|
|
852
|
+
const state = this.syncState(ns);
|
|
853
|
+
const maxSeen = client._seqTracker.getMaxSeenSeq(ns);
|
|
854
|
+
if (maxSeen <= state.ack && state.ack >= state.tail - 1)
|
|
855
|
+
return;
|
|
856
|
+
client._safeAsync(this.fillP2pGap());
|
|
857
|
+
}
|
|
705
858
|
recordPendingGroupPull(ns, seq) {
|
|
706
859
|
if (!ns || !Number.isSafeInteger(seq) || seq <= 0)
|
|
707
860
|
return;
|
|
@@ -792,12 +945,12 @@ export class MessageDeliveryEngine {
|
|
|
792
945
|
return;
|
|
793
946
|
}
|
|
794
947
|
if (pullResponse) {
|
|
795
|
-
const published = client._withPullResponseProcessing(ns, () =>
|
|
948
|
+
const published = client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source, deliveryContext));
|
|
796
949
|
if (isPromiseLike(published))
|
|
797
950
|
await published;
|
|
798
951
|
}
|
|
799
952
|
else {
|
|
800
|
-
const published =
|
|
953
|
+
const published = client._publishAppEvent(event, payload, source, deliveryContext);
|
|
801
954
|
if (isPromiseLike(published))
|
|
802
955
|
await published;
|
|
803
956
|
}
|
|
@@ -806,6 +959,12 @@ export class MessageDeliveryEngine {
|
|
|
806
959
|
}
|
|
807
960
|
async publishOrderedGroupChanged(payload, source = 'ordered') {
|
|
808
961
|
const client = this.runtime.client;
|
|
962
|
+
const generation = this.captureInlineAckGeneration();
|
|
963
|
+
const published = client._publishAppEvent('group.changed', payload, source);
|
|
964
|
+
if (isPromiseLike(published))
|
|
965
|
+
await published;
|
|
966
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
967
|
+
throw new Error('pull invalidated');
|
|
809
968
|
if (isJsonObject(payload)) {
|
|
810
969
|
const eventPayload = payload;
|
|
811
970
|
client._groupState?.handleGroupChangedV2Membership?.(eventPayload);
|
|
@@ -816,9 +975,6 @@ export class MessageDeliveryEngine {
|
|
|
816
975
|
client._cleanupDissolvedGroup?.(groupId);
|
|
817
976
|
}
|
|
818
977
|
}
|
|
819
|
-
const published = client._publishAppEvent('group.changed', payload, source);
|
|
820
|
-
if (isPromiseLike(published))
|
|
821
|
-
await published;
|
|
822
978
|
}
|
|
823
979
|
isInstanceScopedMessageEvent(event) {
|
|
824
980
|
return event === 'message.received'
|
|
@@ -1131,6 +1287,7 @@ export class MessageDeliveryEngine {
|
|
|
1131
1287
|
}
|
|
1132
1288
|
async publishMessageRecallTombstone(seq, message, source = 'push') {
|
|
1133
1289
|
const client = this.runtime.client;
|
|
1290
|
+
const generation = this.captureInlineAckGeneration();
|
|
1134
1291
|
const eventPayload = this.recallEventFromMessage(message);
|
|
1135
1292
|
if (!eventPayload)
|
|
1136
1293
|
return false;
|
|
@@ -1144,13 +1301,17 @@ export class MessageDeliveryEngine {
|
|
|
1144
1301
|
client._clientLog.debug(`message.recalled dedup suppressed: seq=${String(seq)} key=${dedupKey}`);
|
|
1145
1302
|
return false;
|
|
1146
1303
|
}
|
|
1304
|
+
await client._publishAppEvent('message.recalled', eventPayload, source);
|
|
1305
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
1306
|
+
throw new Error('pull invalidated');
|
|
1307
|
+
if (this.isPullScopedSource(source))
|
|
1308
|
+
this.ensurePullOperationCurrent(`p2p:${client._aid ?? ''}`);
|
|
1147
1309
|
seen.set(dedupKey, Date.now());
|
|
1148
1310
|
if (seen.size > MESSAGE_RECALL_SEEN_LIMIT) {
|
|
1149
1311
|
const drop = [...seen.entries()].sort((a, b) => a[1] - b[1]).slice(0, seen.size - MESSAGE_RECALL_SEEN_LIMIT);
|
|
1150
1312
|
for (const [oldKey] of drop)
|
|
1151
1313
|
seen.delete(oldKey);
|
|
1152
1314
|
}
|
|
1153
|
-
await client._publishAppEvent('message.recalled', eventPayload, source);
|
|
1154
1315
|
client._clientLog.debug(`message.recalled published: seq=${String(seq)} ids=${JSON.stringify(eventPayload.message_ids)}`);
|
|
1155
1316
|
return true;
|
|
1156
1317
|
}
|
|
@@ -1235,6 +1396,7 @@ export class MessageDeliveryEngine {
|
|
|
1235
1396
|
}
|
|
1236
1397
|
async publishGroupRecallTombstone(groupId, seq, message, source = 'push') {
|
|
1237
1398
|
const client = this.runtime.client;
|
|
1399
|
+
const generation = this.captureInlineAckGeneration();
|
|
1238
1400
|
const eventPayload = this.recallEventFromGroupMessage(message);
|
|
1239
1401
|
if (!eventPayload)
|
|
1240
1402
|
return false;
|
|
@@ -1250,13 +1412,17 @@ export class MessageDeliveryEngine {
|
|
|
1250
1412
|
client._clientLog.debug(`group.message_recalled dedup suppressed: group=${groupId} seq=${String(seq)} key=${dedupKey}`);
|
|
1251
1413
|
return false;
|
|
1252
1414
|
}
|
|
1415
|
+
await client._publishAppEvent('group.message_recalled', eventPayload, source);
|
|
1416
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
1417
|
+
throw new Error('pull invalidated');
|
|
1418
|
+
if (this.isPullScopedSource(source))
|
|
1419
|
+
this.ensurePullOperationCurrent(`group:${groupId}`);
|
|
1253
1420
|
seen.set(dedupKey, Date.now());
|
|
1254
1421
|
if (seen.size > GROUP_RECALL_SEEN_LIMIT) {
|
|
1255
1422
|
const drop = [...seen.entries()].sort((a, b) => a[1] - b[1]).slice(0, seen.size - GROUP_RECALL_SEEN_LIMIT);
|
|
1256
1423
|
for (const [oldKey] of drop)
|
|
1257
1424
|
seen.delete(oldKey);
|
|
1258
1425
|
}
|
|
1259
|
-
await client._publishAppEvent('group.message_recalled', eventPayload, source);
|
|
1260
1426
|
client._clientLog.debug(`group.message_recalled published: group=${groupId} seq=${String(seq)} ids=${JSON.stringify(eventPayload.message_ids)}`);
|
|
1261
1427
|
return true;
|
|
1262
1428
|
}
|
|
@@ -1301,6 +1467,7 @@ export class MessageDeliveryEngine {
|
|
|
1301
1467
|
if (seqNum > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
|
|
1302
1468
|
this.fillGroupGap(groupId).catch((exc) => client._clientLog.warn(`background recall gap fill failed: ${formatDeliveryError(exc)}`));
|
|
1303
1469
|
}
|
|
1470
|
+
await this.publishGroupRecallTombstone(groupId, seq, wrapped);
|
|
1304
1471
|
}
|
|
1305
1472
|
publishAppEvent(event, payload, source = 'direct', deliveryContext) {
|
|
1306
1473
|
const client = this.runtime.client;
|
|
@@ -1325,11 +1492,14 @@ export class MessageDeliveryEngine {
|
|
|
1325
1492
|
const generation = this.deliveryChangedGeneration;
|
|
1326
1493
|
const normalized = this.normalizePublishedMessagePayload(event, payload, source);
|
|
1327
1494
|
const dispatcher = client._dispatcher;
|
|
1328
|
-
const result =
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1495
|
+
const result = ['message.received', 'group.message_created', 'message.recalled', 'group.message_recalled', 'group.changed'].includes(event)
|
|
1496
|
+
&& typeof dispatcher._publishAndWait === 'function'
|
|
1497
|
+
? dispatcher._publishAndWait(event, normalized)
|
|
1498
|
+
: typeof dispatcher.enqueue === 'function'
|
|
1499
|
+
? (dispatcher.enqueue(event, normalized), undefined)
|
|
1500
|
+
: typeof dispatcher.publishSyncAware === 'function'
|
|
1501
|
+
? dispatcher.publishSyncAware(event, normalized)
|
|
1502
|
+
: dispatcher.publish?.(event, normalized);
|
|
1333
1503
|
const note = () => {
|
|
1334
1504
|
if (generation === this.deliveryChangedGeneration) {
|
|
1335
1505
|
this.noteDeliveryChanged(event, payload, source, deliveryContext);
|
|
@@ -1583,7 +1753,6 @@ export class MessageDeliveryEngine {
|
|
|
1583
1753
|
if (Number(seq) > client._seqTracker.getContiguousSeq(ns) && client._sessionOptions?.background_sync !== false) {
|
|
1584
1754
|
this.fillP2pGap().catch((exc) => client._clientLog.warn(`background recall gap fill failed: ${formatDeliveryError(exc)}`));
|
|
1585
1755
|
}
|
|
1586
|
-
return;
|
|
1587
1756
|
}
|
|
1588
1757
|
await this.publishMessageRecallTombstone(seq, msg);
|
|
1589
1758
|
}
|
|
@@ -1784,12 +1953,15 @@ export class MessageDeliveryEngine {
|
|
|
1784
1953
|
return;
|
|
1785
1954
|
const ns = `p2p:${client._aid}`;
|
|
1786
1955
|
const afterSeq = client._seqTracker.getContiguousSeq(ns);
|
|
1956
|
+
const generation = this.captureInlineAckGeneration();
|
|
1787
1957
|
const dedupKey = `p2p:${afterSeq}`;
|
|
1788
1958
|
if (client._gapFillDone.has(dedupKey))
|
|
1789
1959
|
return;
|
|
1790
1960
|
client._gapFillDone.set(dedupKey, Date.now());
|
|
1961
|
+
this.recordBackgroundPullNeed(ns, 0, true);
|
|
1791
1962
|
client._clientLog.debug(`P2P message gap fill start: after_seq=${afterSeq}`);
|
|
1792
1963
|
let filled = 0;
|
|
1964
|
+
let failed = false;
|
|
1793
1965
|
try {
|
|
1794
1966
|
// Gap Fill 本身就是 Forward Pull,必须沿用协调器的完整事务:
|
|
1795
1967
|
// 原始页提交成功后由协调器负责 Piggyback/终端 ACK。
|
|
@@ -1802,11 +1974,15 @@ export class MessageDeliveryEngine {
|
|
|
1802
1974
|
client._clientLog.debug(`P2P message gap fill done: after_seq=${afterSeq}, filled=${filled}`);
|
|
1803
1975
|
}
|
|
1804
1976
|
catch (exc) {
|
|
1977
|
+
failed = true;
|
|
1805
1978
|
client._clientLog.warn(`P2P message gap fill failed: ${formatDeliveryError(exc)}`);
|
|
1806
1979
|
}
|
|
1807
1980
|
finally {
|
|
1981
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
1982
|
+
return;
|
|
1808
1983
|
client._gapFillDone.delete(dedupKey);
|
|
1809
1984
|
client._schedulePendingP2pPullIfNeeded?.(ns, 'gap-fill-complete');
|
|
1985
|
+
this.finishBackgroundPull(ns, generation, afterSeq, failed);
|
|
1810
1986
|
}
|
|
1811
1987
|
}
|
|
1812
1988
|
async fillGroupGap(groupId) {
|
|
@@ -1818,12 +1994,15 @@ export class MessageDeliveryEngine {
|
|
|
1818
1994
|
return;
|
|
1819
1995
|
const ns = `group:${groupId}`;
|
|
1820
1996
|
const afterSeq = client._seqTracker.getContiguousSeq(ns);
|
|
1997
|
+
const generation = this.captureInlineAckGeneration();
|
|
1821
1998
|
const dedupKey = `group_msg:${groupId}:${afterSeq}`;
|
|
1822
1999
|
if (client._gapFillDone.has(dedupKey))
|
|
1823
2000
|
return;
|
|
1824
2001
|
client._gapFillDone.set(dedupKey, Date.now());
|
|
2002
|
+
this.recordBackgroundPullNeed(ns, 0, true);
|
|
1825
2003
|
client._clientLog.debug(`group message gap fill start: group=${groupId}, after_seq=${afterSeq}`);
|
|
1826
2004
|
let filled = 0;
|
|
2005
|
+
let failed = false;
|
|
1827
2006
|
try {
|
|
1828
2007
|
const operation = () => client._pullGroupV2(groupId, afterSeq, 50, { gateLocked: true });
|
|
1829
2008
|
const messages = client._rpcPipeline
|
|
@@ -1834,14 +2013,15 @@ export class MessageDeliveryEngine {
|
|
|
1834
2013
|
client._clientLog.debug(`group message gap fill done: group=${groupId}, after_seq=${afterSeq}, filled=${filled}`);
|
|
1835
2014
|
}
|
|
1836
2015
|
catch (exc) {
|
|
2016
|
+
failed = true;
|
|
1837
2017
|
client._clientLog.warn(`group message gap fill failed: ${formatDeliveryError(exc)}`);
|
|
1838
2018
|
}
|
|
1839
2019
|
finally {
|
|
2020
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
2021
|
+
return;
|
|
1840
2022
|
client._gapFillDone.delete(dedupKey);
|
|
1841
|
-
if (filled > 0 && client._seqTracker.getContiguousSeq(ns) > afterSeq) {
|
|
1842
|
-
void this.fillGroupGap(groupId);
|
|
1843
|
-
}
|
|
1844
2023
|
this.schedulePendingGroupPullIfNeeded(ns, 'gap-fill-complete');
|
|
2024
|
+
this.finishBackgroundPull(ns, generation, afterSeq, failed);
|
|
1845
2025
|
}
|
|
1846
2026
|
}
|
|
1847
2027
|
scheduleRealtimeWindowForward(ns, groupId = '') {
|
|
@@ -1883,6 +2063,7 @@ export class MessageDeliveryEngine {
|
|
|
1883
2063
|
if (!groupId)
|
|
1884
2064
|
return;
|
|
1885
2065
|
const ns = `group_event:${groupId}`;
|
|
2066
|
+
const generation = this.captureInlineAckGeneration();
|
|
1886
2067
|
const requestedAfterSeq = Number(options.afterSeq ?? 0);
|
|
1887
2068
|
const afterSeq = Number.isSafeInteger(requestedAfterSeq) && requestedAfterSeq > 0
|
|
1888
2069
|
? requestedAfterSeq
|
|
@@ -1901,10 +2082,12 @@ export class MessageDeliveryEngine {
|
|
|
1901
2082
|
}
|
|
1902
2083
|
}
|
|
1903
2084
|
client._gapFillDone.set(dedupKey, Date.now());
|
|
2085
|
+
this.recordBackgroundPullNeed(ns, client._seqTracker.getMaxSeenSeq(ns), true);
|
|
1904
2086
|
this.syncStarted(ns);
|
|
1905
2087
|
let filled = 0;
|
|
1906
2088
|
let continuationAfterSeq = 0;
|
|
1907
2089
|
let failed = false;
|
|
2090
|
+
let dissolved = false;
|
|
1908
2091
|
try {
|
|
1909
2092
|
let nextAfterSeq = afterSeq;
|
|
1910
2093
|
const maxPages = singlePage ? 1 : 100;
|
|
@@ -1930,14 +2113,16 @@ export class MessageDeliveryEngine {
|
|
|
1930
2113
|
}
|
|
1931
2114
|
if (!isJsonObject(result))
|
|
1932
2115
|
return;
|
|
2116
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
2117
|
+
return;
|
|
2118
|
+
this.observeBackgroundPullResponse(ns, nextAfterSeq, result);
|
|
1933
2119
|
const events = result.events;
|
|
1934
2120
|
if (!Array.isArray(events))
|
|
1935
2121
|
return;
|
|
1936
2122
|
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
2123
|
+
let pageTrackerSnapshot = null;
|
|
2124
|
+
let trackerMutated = false;
|
|
1937
2125
|
const eventObjects = events.filter((evt) => isJsonObject(evt));
|
|
1938
|
-
client._seqTracker.onPullResult(ns, eventObjects, nextAfterSeq, Number.isSafeInteger(result.contiguous_seq)
|
|
1939
|
-
? result.contiguous_seq
|
|
1940
|
-
: undefined);
|
|
1941
2126
|
const cursor = isJsonObject(result.cursor)
|
|
1942
2127
|
? result.cursor
|
|
1943
2128
|
: null;
|
|
@@ -1947,13 +2132,6 @@ export class MessageDeliveryEngine {
|
|
|
1947
2132
|
const earliestAvailable = Number(result.earliest_available_event_seq ?? 0);
|
|
1948
2133
|
const visibilityFloor = Math.max(0, Number.isFinite(Number(cursor?.join_seq)) ? Number(cursor?.join_seq) : 0, Number.isFinite(earliestAvailable) && earliestAvailable > 0 ? earliestAvailable - 1 : 0);
|
|
1949
2134
|
const effectiveFloor = Math.max(retentionFloor, serverAckFloor, visibilityFloor);
|
|
1950
|
-
if (effectiveFloor > 0) {
|
|
1951
|
-
const contigBeforeFloor = client._seqTracker.getContiguousSeq(ns);
|
|
1952
|
-
if (contigBeforeFloor < effectiveFloor) {
|
|
1953
|
-
client._clientLog.info(`group.pull_events floor advance: ns=${ns} contiguous=${contigBeforeFloor} -> effective_floor=${effectiveFloor} retention_floor=${retentionFloor} server_ack=${serverAckFloor} visibility_floor=${visibilityFloor}`);
|
|
1954
|
-
client._seqTracker.forceContiguousSeq(ns, effectiveFloor);
|
|
1955
|
-
}
|
|
1956
|
-
}
|
|
1957
2135
|
const eventSeqs = [];
|
|
1958
2136
|
let hasDissolvedEvent = false;
|
|
1959
2137
|
let publishedEventCount = 0;
|
|
@@ -1983,11 +2161,40 @@ export class MessageDeliveryEngine {
|
|
|
1983
2161
|
if (et !== 'group.message_created')
|
|
1984
2162
|
filled += 1;
|
|
1985
2163
|
}
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
this.
|
|
2164
|
+
try {
|
|
2165
|
+
const scanTarget = Math.max(pageContigBefore, effectiveFloor, positiveSafeSequenceHint(result.contiguous_seq), ...eventSeqs);
|
|
2166
|
+
await this.drainOrderedMessages(ns, undefined, false, false, 'pull', undefined, scanTarget);
|
|
2167
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
2168
|
+
throw new Error('pull invalidated');
|
|
2169
|
+
this.ensurePullOperationCurrent(ns);
|
|
2170
|
+
if (hasDissolvedEvent) {
|
|
2171
|
+
dissolved = true;
|
|
2172
|
+
break;
|
|
2173
|
+
}
|
|
2174
|
+
pageTrackerSnapshot = client._seqTracker.snapshotNamespace?.(ns) ?? null;
|
|
2175
|
+
trackerMutated = true;
|
|
2176
|
+
client._seqTracker.onPullResult(ns, eventObjects, nextAfterSeq, Number.isSafeInteger(result.contiguous_seq)
|
|
2177
|
+
? result.contiguous_seq : undefined);
|
|
2178
|
+
if (client._seqTracker.getContiguousSeq(ns) < effectiveFloor) {
|
|
2179
|
+
client._seqTracker.forceContiguousSeq(ns, effectiveFloor);
|
|
2180
|
+
}
|
|
2181
|
+
if (client._seqTracker.getContiguousSeq(ns) !== pageContigBefore && !hasDissolvedEvent) {
|
|
2182
|
+
await client._commitSeqTrackerState(ns);
|
|
2183
|
+
this.ensurePullOperationCurrent(ns);
|
|
2184
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
2185
|
+
throw new Error('pull invalidated');
|
|
2186
|
+
}
|
|
1990
2187
|
}
|
|
2188
|
+
catch (exc) {
|
|
2189
|
+
if (trackerMutated && generation === this.captureInlineAckGeneration() && this.isPullOperationCurrent(ns)
|
|
2190
|
+
&& typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
|
|
2191
|
+
(client._seqTracker._restoreNamespaceSnapshotPreservingPush ?? client._seqTracker.restoreNamespaceSnapshot)
|
|
2192
|
+
.call(client._seqTracker, ns, pageTrackerSnapshot);
|
|
2193
|
+
this.dropSeqTrackerPending(ns);
|
|
2194
|
+
}
|
|
2195
|
+
throw exc;
|
|
2196
|
+
}
|
|
2197
|
+
const ackContig = client._seqTracker.getContiguousSeq(ns);
|
|
1991
2198
|
if (eventObjects.length > 0 && ackContig > 0 && ackContig !== pageContigBefore) {
|
|
1992
2199
|
const ackSeq = this.clampAckSeq('group.ack_events', 'event_seq', ns, ackContig);
|
|
1993
2200
|
try {
|
|
@@ -2020,6 +2227,7 @@ export class MessageDeliveryEngine {
|
|
|
2020
2227
|
const nextAfter = Math.max(eventSeqs.length > 0 ? Math.max(...eventSeqs) : nextAfterSeq, nextAfterSeq);
|
|
2021
2228
|
const pageAdvanced = eventObjects.length > 0 && nextAfter > nextAfterSeq;
|
|
2022
2229
|
const needsMore = result.has_more === true
|
|
2230
|
+
|| positiveSafeSequenceHint(result.remaining) > 0
|
|
2023
2231
|
|| client._seqTracker.getMaxSeenSeq(ns) > nextAfter;
|
|
2024
2232
|
if (singlePage && pageAdvanced && needsMore) {
|
|
2025
2233
|
continuationAfterSeq = nextAfter;
|
|
@@ -2038,19 +2246,22 @@ export class MessageDeliveryEngine {
|
|
|
2038
2246
|
client._clientLog.warn(`group event gap fill failed: ${formatDeliveryError(exc)}`);
|
|
2039
2247
|
}
|
|
2040
2248
|
finally {
|
|
2041
|
-
|
|
2249
|
+
if (generation === this.captureInlineAckGeneration())
|
|
2250
|
+
client._gapFillDone.delete(dedupKey);
|
|
2042
2251
|
if (legacyGateToken !== null && typeof client._releasePullGate === 'function') {
|
|
2043
2252
|
client._releasePullGate(ns, legacyGateToken);
|
|
2044
2253
|
}
|
|
2045
|
-
if (
|
|
2254
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
2255
|
+
return;
|
|
2256
|
+
if (dissolved)
|
|
2257
|
+
this.backgroundPullNeeds.delete(ns);
|
|
2258
|
+
if (!dissolved && singlePage && continuationAfterSeq > afterSeq) {
|
|
2046
2259
|
this.enqueueOnlineUnreadEventHint({
|
|
2047
2260
|
group_id: groupId,
|
|
2048
2261
|
_online_hint_after_event_seq: continuationAfterSeq,
|
|
2049
2262
|
});
|
|
2050
2263
|
}
|
|
2051
|
-
|
|
2052
|
-
void this.fillGroupEventGap(groupId);
|
|
2053
|
-
}
|
|
2264
|
+
this.finishBackgroundPull(ns, generation, afterSeq, failed);
|
|
2054
2265
|
if (!client._rpcPipeline)
|
|
2055
2266
|
this.syncStopped(failed ? 'aborted' : 'pull_drained');
|
|
2056
2267
|
}
|
|
@@ -2060,6 +2271,7 @@ export class MessageDeliveryEngine {
|
|
|
2060
2271
|
const groupId = String(data.group_id ?? '').trim();
|
|
2061
2272
|
if (!groupId || client._sessionOptions?.background_sync === false)
|
|
2062
2273
|
return;
|
|
2274
|
+
this.recordBackgroundPullNeed(`group_event:${groupId}`, positiveSafeSequenceHint(data.event_seq), true);
|
|
2063
2275
|
const rawAfter = Number(data._online_hint_after_event_seq ?? 0);
|
|
2064
2276
|
const afterSeq = Number.isSafeInteger(rawAfter) && rawAfter > 0 ? rawAfter : 0;
|
|
2065
2277
|
client._safeAsync(this.fillGroupEventGap(groupId, {
|
|
@@ -2864,7 +3076,8 @@ export class MessageDeliveryEngine {
|
|
|
2864
3076
|
return false;
|
|
2865
3077
|
const client = this.runtime.client;
|
|
2866
3078
|
const tracker = client._seqTracker;
|
|
2867
|
-
|
|
3079
|
+
let snapshot = null;
|
|
3080
|
+
let trackerMutated = false;
|
|
2868
3081
|
let delivered = false;
|
|
2869
3082
|
try {
|
|
2870
3083
|
const selfSync = (event === 'message.received' && this.isSelfSentP2PMessage(payload))
|
|
@@ -2879,6 +3092,8 @@ export class MessageDeliveryEngine {
|
|
|
2879
3092
|
delivered = true;
|
|
2880
3093
|
if (!this.inlineGenerationIsCurrent(generation))
|
|
2881
3094
|
return false;
|
|
3095
|
+
snapshot = typeof tracker.snapshotNamespace === 'function' ? tracker.snapshotNamespace(ns) : null;
|
|
3096
|
+
trackerMutated = true;
|
|
2882
3097
|
const needsPull = Boolean(tracker.onMessageSeq(ns, seq));
|
|
2883
3098
|
if (!this.inlineGenerationIsCurrent(generation))
|
|
2884
3099
|
return false;
|
|
@@ -2901,9 +3116,10 @@ export class MessageDeliveryEngine {
|
|
|
2901
3116
|
return true;
|
|
2902
3117
|
}
|
|
2903
3118
|
catch (exc) {
|
|
2904
|
-
if (this.inlineGenerationIsCurrent(generation)
|
|
2905
|
-
&& typeof tracker.restoreNamespaceSnapshot === 'function')
|
|
2906
|
-
tracker.restoreNamespaceSnapshot(ns, snapshot);
|
|
3119
|
+
if (trackerMutated && this.inlineGenerationIsCurrent(generation)
|
|
3120
|
+
&& typeof tracker.restoreNamespaceSnapshot === 'function') {
|
|
3121
|
+
(tracker._restoreNamespaceSnapshotPreservingPush ?? tracker.restoreNamespaceSnapshot).call(tracker, ns, snapshot);
|
|
3122
|
+
}
|
|
2907
3123
|
if (this.inlineGenerationIsCurrent(generation) && delivered)
|
|
2908
3124
|
this.markPublishedSeq(ns, seq);
|
|
2909
3125
|
client._clientLog.debug(`inline realtime consume rolled back: ns=${ns} seq=${seq} err=${formatDeliveryError(exc)}`);
|
|
@@ -2929,11 +3145,13 @@ export class MessageDeliveryEngine {
|
|
|
2929
3145
|
throw new Error('inline realtime terminal cursor did not collapse after consume');
|
|
2930
3146
|
}
|
|
2931
3147
|
await client._commitSeqTrackerState(ns);
|
|
2932
|
-
return
|
|
3148
|
+
return this.inlineGenerationIsCurrent(generation);
|
|
2933
3149
|
}
|
|
2934
3150
|
catch (exc) {
|
|
2935
|
-
if (
|
|
2936
|
-
tracker.restoreNamespaceSnapshot
|
|
3151
|
+
if (this.inlineGenerationIsCurrent(generation)
|
|
3152
|
+
&& typeof tracker.restoreNamespaceSnapshot === 'function') {
|
|
3153
|
+
(tracker._restoreNamespaceSnapshotPreservingPush ?? tracker.restoreNamespaceSnapshot).call(tracker, ns, snapshot);
|
|
3154
|
+
}
|
|
2937
3155
|
client._clientLog.debug(`inline realtime terminal consume rolled back: ns=${ns} seq=${seq} err=${formatDeliveryError(exc)}`);
|
|
2938
3156
|
return false;
|
|
2939
3157
|
}
|
|
@@ -3126,6 +3344,7 @@ export class MessageDeliveryEngine {
|
|
|
3126
3344
|
}
|
|
3127
3345
|
async onV2PushNotification(data, expectedVersion, inlineGeneration) {
|
|
3128
3346
|
const client = this.runtime.client;
|
|
3347
|
+
const syncGeneration = this.captureInlineAckGeneration();
|
|
3129
3348
|
if (inlineGeneration !== undefined && !this.inlineGenerationIsCurrent(inlineGeneration))
|
|
3130
3349
|
return;
|
|
3131
3350
|
const pushData = isJsonObject(data) ? data : null;
|
|
@@ -3180,6 +3399,7 @@ export class MessageDeliveryEngine {
|
|
|
3180
3399
|
this.retryPendingP2PInlineAck(ns, pushSeq, 'covered inline push');
|
|
3181
3400
|
return;
|
|
3182
3401
|
}
|
|
3402
|
+
this.recordBackgroundPullNeed(ns, pushSeq);
|
|
3183
3403
|
const pullActive = client._rpcPipeline?.hasPullActivity?.(ns) === true;
|
|
3184
3404
|
if (pullActive || !this.beginRealtimeSync(ns)) {
|
|
3185
3405
|
client._recordPendingP2pPull(ns, pushSeq);
|
|
@@ -3196,7 +3416,9 @@ export class MessageDeliveryEngine {
|
|
|
3196
3416
|
client._clientLog.warn(`V2 P2P push auto-sync failed: ${formatDeliveryError(exc)}`);
|
|
3197
3417
|
}
|
|
3198
3418
|
finally {
|
|
3199
|
-
if (
|
|
3419
|
+
if (syncGeneration === this.captureInlineAckGeneration()
|
|
3420
|
+
&& (inlineGeneration === undefined || this.inlineGenerationIsCurrent(inlineGeneration))) {
|
|
3421
|
+
this.finishBackgroundPull(ns, syncGeneration, this.syncState(ns).ack, false);
|
|
3200
3422
|
this.endRealtimeSync(ns);
|
|
3201
3423
|
client._schedulePendingP2pPullIfNeeded?.(ns, 'push-auto-pull-complete');
|
|
3202
3424
|
}
|
|
@@ -3208,6 +3430,9 @@ export class MessageDeliveryEngine {
|
|
|
3208
3430
|
if (!groupId)
|
|
3209
3431
|
return;
|
|
3210
3432
|
const ns = `group:${groupId}`;
|
|
3433
|
+
const generation = this.captureInlineAckGeneration();
|
|
3434
|
+
const afterSeq = this.syncState(ns).ack;
|
|
3435
|
+
this.recordBackgroundPullNeed(ns, positiveSafeSequenceHint(data.seq));
|
|
3211
3436
|
const targets = this.onlineUnreadHintTargets ?? new Map();
|
|
3212
3437
|
const tasks = this.onlineUnreadHintTasks ?? new Map();
|
|
3213
3438
|
this.onlineUnreadHintTargets = targets;
|
|
@@ -3215,10 +3440,13 @@ export class MessageDeliveryEngine {
|
|
|
3215
3440
|
targets.set(ns, Math.max(targets.get(ns) ?? 0, positiveSafeSequenceHint(data.seq)));
|
|
3216
3441
|
if (tasks.has(ns))
|
|
3217
3442
|
return;
|
|
3443
|
+
let attemptAfterSeq = afterSeq;
|
|
3218
3444
|
const task = Promise.resolve().then(async () => {
|
|
3445
|
+
let failed = false;
|
|
3219
3446
|
try {
|
|
3220
3447
|
while (true) {
|
|
3221
3448
|
const ackBefore = this.syncState(ns).ack;
|
|
3449
|
+
attemptAfterSeq = ackBefore;
|
|
3222
3450
|
await this.runGroupForwardRecovery(groupId, ns, 50, true, targets.get(ns) ?? 0);
|
|
3223
3451
|
const ackAfter = this.syncState(ns).ack;
|
|
3224
3452
|
if (ackAfter >= (targets.get(ns) ?? 0) || ackAfter <= ackBefore)
|
|
@@ -3226,12 +3454,14 @@ export class MessageDeliveryEngine {
|
|
|
3226
3454
|
}
|
|
3227
3455
|
}
|
|
3228
3456
|
catch (exc) {
|
|
3457
|
+
failed = true;
|
|
3229
3458
|
client._clientLog.debug(`online unread hint background Forward failed: ns=${ns} err=${formatDeliveryError(exc)}`);
|
|
3230
3459
|
}
|
|
3231
3460
|
finally {
|
|
3232
3461
|
targets.delete(ns);
|
|
3233
3462
|
if (tasks.get(ns) === task)
|
|
3234
3463
|
tasks.delete(ns);
|
|
3464
|
+
this.finishBackgroundPull(ns, generation, attemptAfterSeq, failed);
|
|
3235
3465
|
client._rpcPipeline?.requestPullDrainedCheck?.();
|
|
3236
3466
|
}
|
|
3237
3467
|
});
|
|
@@ -3240,6 +3470,7 @@ export class MessageDeliveryEngine {
|
|
|
3240
3470
|
}
|
|
3241
3471
|
async onRawGroupV2MessageCreated(data, expectedVersion, inlineGeneration) {
|
|
3242
3472
|
const client = this.runtime.client;
|
|
3473
|
+
const syncGeneration = this.captureInlineAckGeneration();
|
|
3243
3474
|
if (inlineGeneration !== undefined && !this.inlineGenerationIsCurrent(inlineGeneration))
|
|
3244
3475
|
return;
|
|
3245
3476
|
if (!isJsonObject(data)) {
|
|
@@ -3302,6 +3533,7 @@ export class MessageDeliveryEngine {
|
|
|
3302
3533
|
}
|
|
3303
3534
|
return;
|
|
3304
3535
|
}
|
|
3536
|
+
this.recordBackgroundPullNeed(ns, seq);
|
|
3305
3537
|
const pullActive = client._rpcPipeline?.hasPullActivity?.(ns) === true;
|
|
3306
3538
|
if (pullActive || !this.beginRealtimeSync(ns)) {
|
|
3307
3539
|
this.recordPendingGroupPull(ns, seq);
|
|
@@ -3317,7 +3549,9 @@ export class MessageDeliveryEngine {
|
|
|
3317
3549
|
return;
|
|
3318
3550
|
client._clientLog.warn(`V2 group push auto-sync failed: group=${groupId} err=${formatDeliveryError(exc)}`);
|
|
3319
3551
|
}).finally(() => {
|
|
3320
|
-
if (
|
|
3552
|
+
if (syncGeneration === this.captureInlineAckGeneration()
|
|
3553
|
+
&& (inlineGeneration === undefined || this.inlineGenerationIsCurrent(inlineGeneration))) {
|
|
3554
|
+
this.finishBackgroundPull(ns, syncGeneration, this.syncState(ns).ack, false);
|
|
3321
3555
|
this.endRealtimeSync(ns);
|
|
3322
3556
|
this.schedulePendingGroupPullIfNeeded(ns, 'push-auto-pull-complete');
|
|
3323
3557
|
}
|
|
@@ -3379,9 +3613,14 @@ export class MessageDeliveryEngine {
|
|
|
3379
3613
|
if (!client._aid || !client._v2Session)
|
|
3380
3614
|
return;
|
|
3381
3615
|
const ns = `p2p:${client._aid}`;
|
|
3616
|
+
const generation = this.captureInlineAckGeneration();
|
|
3617
|
+
const afterSeq = this.syncState(ns).ack;
|
|
3618
|
+
this.recordBackgroundPullNeed(ns, positiveSafeSequenceHint(data.seq), true);
|
|
3619
|
+
let failed = false;
|
|
3382
3620
|
client._safeAsync(this.runP2PForwardRecovery(ns, 50, true).catch((exc) => {
|
|
3621
|
+
failed = true;
|
|
3383
3622
|
client._clientLog.debug(`P2P online unread hint background Forward failed: ns=${ns} err=${formatDeliveryError(exc)}`);
|
|
3384
|
-
}));
|
|
3623
|
+
}).finally(() => this.finishBackgroundPull(ns, generation, afterSeq, failed)));
|
|
3385
3624
|
}
|
|
3386
3625
|
async saveSeqTrackerState() {
|
|
3387
3626
|
const client = this.runtime.client;
|
|
@@ -3711,8 +3950,9 @@ export class MessageDeliveryEngine {
|
|
|
3711
3950
|
}
|
|
3712
3951
|
return params;
|
|
3713
3952
|
}
|
|
3714
|
-
async drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true, source = 'push', context) {
|
|
3953
|
+
async drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true, source = 'push', context, throughSeq) {
|
|
3715
3954
|
const client = this.runtime.client;
|
|
3955
|
+
const generation = this.captureInlineAckGeneration();
|
|
3716
3956
|
const pullScoped = this.isPullScopedSource(source);
|
|
3717
3957
|
if (pullScoped)
|
|
3718
3958
|
this.ensurePullOperationCurrent(ns);
|
|
@@ -3728,11 +3968,11 @@ export class MessageDeliveryEngine {
|
|
|
3728
3968
|
this.ensurePullOperationCurrent(ns);
|
|
3729
3969
|
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
3730
3970
|
const ready = [...queue.keys()]
|
|
3731
|
-
.filter((seq) => seq <= contig && (beforeSeq === undefined || seq < beforeSeq))
|
|
3971
|
+
.filter((seq) => seq <= Math.max(contig, throughSeq ?? contig) && (beforeSeq === undefined || seq < beforeSeq))
|
|
3732
3972
|
.sort((a, b) => a - b);
|
|
3733
3973
|
let seq = ready[0];
|
|
3734
3974
|
if (seq === undefined) {
|
|
3735
|
-
const nextSeq = contig + 1;
|
|
3975
|
+
const nextSeq = Math.max(contig, throughSeq ?? contig) + 1;
|
|
3736
3976
|
if (beforeSeq !== undefined && nextSeq >= beforeSeq)
|
|
3737
3977
|
break;
|
|
3738
3978
|
if (!queue.has(nextSeq))
|
|
@@ -3742,10 +3982,10 @@ export class MessageDeliveryEngine {
|
|
|
3742
3982
|
if (seq === undefined)
|
|
3743
3983
|
continue;
|
|
3744
3984
|
const item = queue.get(seq);
|
|
3745
|
-
queue.delete(seq);
|
|
3746
3985
|
if (!item)
|
|
3747
3986
|
continue;
|
|
3748
3987
|
if (client._pushedSeqs.get(ns)?.has(seq)) {
|
|
3988
|
+
queue.delete(seq);
|
|
3749
3989
|
client._clientLog.debug(`publish ordered drain skipped duplicate: ns=${ns}, seq=${seq}, event=${item.event}`);
|
|
3750
3990
|
client._markOrderedSeqDelivered(ns, seq);
|
|
3751
3991
|
continue;
|
|
@@ -3763,10 +4003,18 @@ export class MessageDeliveryEngine {
|
|
|
3763
4003
|
}
|
|
3764
4004
|
}
|
|
3765
4005
|
await this.publishOrderedQueueItem(ns, item.event, seq, item.payload, itemSource, pullResponse, itemContext);
|
|
4006
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
4007
|
+
throw new Error('pull invalidated');
|
|
3766
4008
|
if (pullScoped)
|
|
3767
4009
|
this.ensurePullOperationCurrent(ns);
|
|
4010
|
+
queue.delete(seq);
|
|
4011
|
+
if (item.event === 'group.changed' && isJsonObject(item.payload)
|
|
4012
|
+
&& item.payload.action === 'dissolved')
|
|
4013
|
+
break;
|
|
3768
4014
|
this.markPublishedSeq(ns, seq);
|
|
3769
4015
|
client._markOrderedSeqDelivered(ns, seq);
|
|
4016
|
+
if (throughSeq !== undefined)
|
|
4017
|
+
throughSeq = Math.max(throughSeq, seq);
|
|
3770
4018
|
delivered = true;
|
|
3771
4019
|
client._clientLog.debug(`publish ordered drain delivered: ns=${ns}, seq=${seq}, event=${item.event}`);
|
|
3772
4020
|
}
|
|
@@ -3810,6 +4058,7 @@ export class MessageDeliveryEngine {
|
|
|
3810
4058
|
}
|
|
3811
4059
|
async publishOrderedMessageInternal(event, ns, seq, payload, source, context) {
|
|
3812
4060
|
const client = this.runtime.client;
|
|
4061
|
+
const generation = this.captureInlineAckGeneration();
|
|
3813
4062
|
const pullScoped = this.isPullScopedSource(source);
|
|
3814
4063
|
if (pullScoped)
|
|
3815
4064
|
this.ensurePullOperationCurrent(ns);
|
|
@@ -3853,12 +4102,14 @@ export class MessageDeliveryEngine {
|
|
|
3853
4102
|
return false;
|
|
3854
4103
|
}
|
|
3855
4104
|
const queue = client._pendingOrderedMsgs.get(ns);
|
|
3856
|
-
queue?.delete(seqNum);
|
|
3857
|
-
if (queue && queue.size === 0)
|
|
3858
|
-
client._pendingOrderedMsgs.delete(ns);
|
|
3859
4105
|
await this.publishOrderedQueueItem(ns, event, seqNum, payload, source, false, context);
|
|
4106
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
4107
|
+
throw new Error('pull invalidated');
|
|
3860
4108
|
if (pullScoped)
|
|
3861
4109
|
this.ensurePullOperationCurrent(ns);
|
|
4110
|
+
queue?.delete(seqNum);
|
|
4111
|
+
if (queue && queue.size === 0)
|
|
4112
|
+
client._pendingOrderedMsgs.delete(ns);
|
|
3862
4113
|
this.markPublishedSeq(ns, seqNum);
|
|
3863
4114
|
client._markOrderedSeqDelivered(ns, seqNum);
|
|
3864
4115
|
client._clientLog.debug(`publish ordered delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
|
|
@@ -3872,6 +4123,7 @@ export class MessageDeliveryEngine {
|
|
|
3872
4123
|
}
|
|
3873
4124
|
async publishPulledMessage(event, ns, seq, payload, persist = true, source = 'pull') {
|
|
3874
4125
|
const client = this.runtime.client;
|
|
4126
|
+
const generation = this.captureInlineAckGeneration();
|
|
3875
4127
|
const pullScoped = this.isPullScopedSource(source);
|
|
3876
4128
|
if (pullScoped)
|
|
3877
4129
|
this.ensurePullOperationCurrent(ns);
|
|
@@ -3907,13 +4159,15 @@ export class MessageDeliveryEngine {
|
|
|
3907
4159
|
await this.drainOrderedMessages(ns, seqNum, true, persist, source);
|
|
3908
4160
|
if (pullScoped)
|
|
3909
4161
|
this.ensurePullOperationCurrent(ns);
|
|
3910
|
-
queue?.delete(seqNum);
|
|
3911
|
-
if (queue && queue.size === 0)
|
|
3912
|
-
client._pendingOrderedMsgs.delete(ns);
|
|
3913
4162
|
if (event === 'message.recalled') {
|
|
3914
4163
|
const recallPublished = await this.publishMessageRecallTombstone(seqNum, payload, source);
|
|
4164
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
4165
|
+
throw new Error('pull invalidated');
|
|
3915
4166
|
if (pullScoped)
|
|
3916
4167
|
this.ensurePullOperationCurrent(ns);
|
|
4168
|
+
queue?.delete(seqNum);
|
|
4169
|
+
if (queue && queue.size === 0)
|
|
4170
|
+
client._pendingOrderedMsgs.delete(ns);
|
|
3917
4171
|
this.markPublishedSeq(ns, seqNum);
|
|
3918
4172
|
client._clientLog.debug(`publish pulled delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
|
|
3919
4173
|
await this.drainOrderedMessages(ns, undefined, true, persist, source);
|
|
@@ -3924,8 +4178,13 @@ export class MessageDeliveryEngine {
|
|
|
3924
4178
|
const published = client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source));
|
|
3925
4179
|
if (isPromiseLike(published))
|
|
3926
4180
|
await published;
|
|
4181
|
+
if (generation !== this.captureInlineAckGeneration())
|
|
4182
|
+
throw new Error('pull invalidated');
|
|
3927
4183
|
if (pullScoped)
|
|
3928
4184
|
this.ensurePullOperationCurrent(ns);
|
|
4185
|
+
queue?.delete(seqNum);
|
|
4186
|
+
if (queue && queue.size === 0)
|
|
4187
|
+
client._pendingOrderedMsgs.delete(ns);
|
|
3929
4188
|
this.markPublishedSeq(ns, seqNum);
|
|
3930
4189
|
client._clientLog.debug(`publish pulled delivered: event=${event}, ns=${ns}, seq=${seqNum}`);
|
|
3931
4190
|
await this.drainOrderedMessages(ns, undefined, true, persist, source);
|