@agentunion/fastaun-browser 0.5.18 → 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 +44 -2
- package/_packed_docs/CHANGELOG.md +44 -2
- package/_packed_docs/INDEX.md +136 -14
- package/_packed_docs/KITE_DOCS_GUIDE.md +42 -16
- package/_packed_docs/agent.md/SCHEMA.md +245 -205
- 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/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 +28 -9
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +22 -27
- 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 +250 -283
- 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 +74 -64
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +25 -5
- package/_packed_docs/sdk/INDEX.md +76 -17
- package/dist/agent-md-schema.d.ts +3 -0
- package/dist/agent-md-schema.d.ts.map +1 -1
- package/dist/agent-md-schema.js +45 -11
- package/dist/agent-md-schema.js.map +1 -1
- package/dist/agent-md.d.ts +45 -2
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +519 -30
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts +12 -46
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +113 -246
- package/dist/aid-store.js.map +1 -1
- package/dist/aid.d.ts.map +1 -1
- package/dist/aid.js +20 -7
- package/dist/aid.js.map +1 -1
- package/dist/auth.d.ts +4 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +343 -23
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +37330 -32005
- package/dist/cert-utils.d.ts +1 -1
- package/dist/cert-utils.d.ts.map +1 -1
- package/dist/cert-utils.js +19 -7
- package/dist/cert-utils.js.map +1 -1
- package/dist/client/delivery.d.ts +15 -1
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +147 -51
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +19 -5
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +1 -0
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +104 -30
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +100 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +1204 -23
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +3 -2
- package/dist/errors.js.map +1 -1
- package/dist/events.d.ts +4 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +41 -1
- package/dist/events.js.map +1 -1
- package/dist/facades.d.ts +82 -2
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +98 -4
- package/dist/facades.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +17 -0
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.d.ts +10 -1
- package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.js +111 -0
- package/dist/keystore/indexeddb-shared.js.map +1 -1
- package/dist/keystore/indexeddb-token-store.d.ts +7 -1
- package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-token-store.js +30 -1
- package/dist/keystore/indexeddb-token-store.js.map +1 -1
- package/dist/org-protocol.d.ts +98 -0
- package/dist/org-protocol.d.ts.map +1 -0
- package/dist/org-protocol.js +1742 -0
- package/dist/org-protocol.js.map +1 -0
- package/dist/ownership-proof.d.ts +93 -5
- package/dist/ownership-proof.d.ts.map +1 -1
- package/dist/ownership-proof.js +239 -53
- package/dist/ownership-proof.js.map +1 -1
- package/dist/ownership-v21.d.ts +177 -0
- package/dist/ownership-v21.d.ts.map +1 -0
- package/dist/ownership-v21.js +609 -0
- package/dist/ownership-v21.js.map +1 -0
- package/dist/ownership-workflow.d.ts +146 -0
- package/dist/ownership-workflow.d.ts.map +1 -0
- package/dist/ownership-workflow.js +1066 -0
- package/dist/ownership-workflow.js.map +1 -0
- package/dist/seq-tracker.d.ts +1 -0
- package/dist/seq-tracker.d.ts.map +1 -1
- package/dist/seq-tracker.js +10 -2
- package/dist/seq-tracker.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +82 -0
- package/dist/tools/cross-sdk-agent.d.ts.map +1 -1
- package/dist/tools/cross-sdk-agent.js +332 -7
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +6 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +88 -2
- package/dist/transport.js.map +1 -1
- package/dist/v2/e2ee/decrypt.d.ts +1 -0
- package/dist/v2/e2ee/decrypt.d.ts.map +1 -1
- 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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { slotIsolationKey } from '../config.js';
|
|
2
2
|
import { normalizeGroupAid } from '../group-id.js';
|
|
3
|
+
import { SeqTracker } from '../seq-tracker.js';
|
|
3
4
|
import { ConnectionState, isJsonObject } from '../types.js';
|
|
4
5
|
import { hasGroupMentionModeField, normalizeGroupMentionMode } from './mention-mode.js';
|
|
5
6
|
const PUSHED_SEQS_LIMIT = 50_000;
|
|
@@ -136,6 +137,7 @@ export class MessageDeliveryEngine {
|
|
|
136
137
|
pendingP2PInlineAcks = null;
|
|
137
138
|
pendingGroupInlineAcks = null;
|
|
138
139
|
pendingGroupInlineAckNamespaces = null;
|
|
140
|
+
pendingTailWindows = new Map();
|
|
139
141
|
volatileContext = '';
|
|
140
142
|
volatilePullCursorValue = '';
|
|
141
143
|
volatileSeenMessages = new Map();
|
|
@@ -164,6 +166,7 @@ export class MessageDeliveryEngine {
|
|
|
164
166
|
this.pendingP2PInlineAcks = null;
|
|
165
167
|
this.pendingGroupInlineAcks = null;
|
|
166
168
|
this.pendingGroupInlineAckNamespaces = null;
|
|
169
|
+
this.pendingTailWindows.clear();
|
|
167
170
|
this.syncingNamespaces.clear();
|
|
168
171
|
this.syncSessions.clear();
|
|
169
172
|
}
|
|
@@ -186,6 +189,35 @@ export class MessageDeliveryEngine {
|
|
|
186
189
|
captureInlineGeneration() {
|
|
187
190
|
return this.inlineGeneration;
|
|
188
191
|
}
|
|
192
|
+
deferTailWindow(ns, proof) {
|
|
193
|
+
const tracker = new SeqTracker();
|
|
194
|
+
tracker.restoreNamespaceSnapshot(ns, this.runtime.client._seqTracker.snapshotNamespace(ns));
|
|
195
|
+
tracker.commitTailWindow(ns, proof);
|
|
196
|
+
this.pendingTailWindows.set(ns, proof);
|
|
197
|
+
}
|
|
198
|
+
prospectiveTailState(ns) {
|
|
199
|
+
const proof = this.pendingTailWindows.get(ns);
|
|
200
|
+
if (!proof)
|
|
201
|
+
return this.syncState(ns);
|
|
202
|
+
const tracker = new SeqTracker();
|
|
203
|
+
tracker.restoreNamespaceSnapshot(ns, this.runtime.client._seqTracker.snapshotNamespace(ns));
|
|
204
|
+
tracker.commitTailWindow(ns, proof);
|
|
205
|
+
return tracker.getSyncState(ns);
|
|
206
|
+
}
|
|
207
|
+
pendingTailDeliveryThrough(ns, scanThrough) {
|
|
208
|
+
const proof = this.pendingTailWindows.get(ns);
|
|
209
|
+
return proof && scanThrough >= proof.windowStartSeq - 1 ? Math.max(scanThrough, proof.headSeq) : scanThrough;
|
|
210
|
+
}
|
|
211
|
+
commitPendingTailWindow(ns, scanThrough) {
|
|
212
|
+
const proof = this.pendingTailWindows.get(ns);
|
|
213
|
+
if (proof && scanThrough >= proof.windowStartSeq - 1)
|
|
214
|
+
this.runtime.client._seqTracker.commitTailWindow(ns, proof);
|
|
215
|
+
}
|
|
216
|
+
completePendingTailWindow(ns) {
|
|
217
|
+
const proof = this.pendingTailWindows.get(ns);
|
|
218
|
+
if (proof && this.runtime.client._seqTracker.getContiguousSeq(ns) >= proof.headSeq)
|
|
219
|
+
this.pendingTailWindows.delete(ns);
|
|
220
|
+
}
|
|
189
221
|
beginRealtimeSync(ns) {
|
|
190
222
|
if (!ns || this.realtimeSyncing?.has(ns))
|
|
191
223
|
return false;
|
|
@@ -295,6 +327,18 @@ export class MessageDeliveryEngine {
|
|
|
295
327
|
return;
|
|
296
328
|
this.schedulePendingPullIfNeeded(ns, 'pull-gate-idle');
|
|
297
329
|
}
|
|
330
|
+
onForegroundPullsDrained() {
|
|
331
|
+
const client = this.runtime.client;
|
|
332
|
+
if (!client._aid || client._sessionOptions?.background_sync === false
|
|
333
|
+
|| client.state !== ConnectionState.READY || client._closing)
|
|
334
|
+
return;
|
|
335
|
+
const ns = `p2p:${client._aid}`;
|
|
336
|
+
const state = this.syncState(ns);
|
|
337
|
+
const maxSeen = client._seqTracker.getMaxSeenSeq(ns);
|
|
338
|
+
if (maxSeen <= state.ack && state.ack >= state.tail - 1)
|
|
339
|
+
return;
|
|
340
|
+
client._safeAsync(this.fillP2pGap());
|
|
341
|
+
}
|
|
298
342
|
onPullWorkSettled() {
|
|
299
343
|
const pipeline = this.runtime.client._rpcPipeline;
|
|
300
344
|
if (pipeline?.hasAnyPullActivity?.() === true)
|
|
@@ -952,9 +996,7 @@ export class MessageDeliveryEngine {
|
|
|
952
996
|
const pageInlineGeneration = this.captureInlineGeneration();
|
|
953
997
|
const deferredServerCursor = Number(coordinator.pendingForwardCursor(ns, pageContigBefore) ?? 0);
|
|
954
998
|
const deferredForwardAck = Number(coordinator.pendingForwardAck(ns, pageContigBefore) ?? 0);
|
|
955
|
-
|
|
956
|
-
? client._seqTracker.snapshotNamespace(ns)
|
|
957
|
-
: null;
|
|
999
|
+
let pageTrackerSnapshot;
|
|
958
1000
|
response._contig_before = pageContigBefore;
|
|
959
1001
|
const cursor = isJsonObject(response.cursor)
|
|
960
1002
|
? response.cursor
|
|
@@ -1005,6 +1047,15 @@ export class MessageDeliveryEngine {
|
|
|
1005
1047
|
const contiguousSeq = Number.isSafeInteger(result.contiguous_seq)
|
|
1006
1048
|
? result.contiguous_seq
|
|
1007
1049
|
: undefined;
|
|
1050
|
+
const scanThrough = Math.max(pageContigBefore, contiguousSeq ?? 0, retentionFloor, visibilityFloor, deferredServerCursor, ...messages.map((message) => positiveSafeSequenceHint(message.seq)));
|
|
1051
|
+
const state = this.syncState(ns);
|
|
1052
|
+
const deliveryThrough = this.pendingTailDeliveryThrough(ns, scanThrough >= state.tail - 1 ? Math.max(scanThrough, state.head) : scanThrough);
|
|
1053
|
+
await this.drainOrderedMessages(ns, undefined, false, false, undefined, 'pull', deliveryThrough);
|
|
1054
|
+
this.ensurePullOperationCurrent(ns);
|
|
1055
|
+
if (!this.isInlineGenerationCurrent(pageInlineGeneration))
|
|
1056
|
+
throw new Error('pull invalidated');
|
|
1057
|
+
pageTrackerSnapshot = client._seqTracker.snapshotNamespace(ns);
|
|
1058
|
+
this.commitPendingTailWindow(ns, scanThrough);
|
|
1008
1059
|
if (contiguousSeq === undefined)
|
|
1009
1060
|
client._seqTracker.onPullResult(ns, messages, afterSeq);
|
|
1010
1061
|
else
|
|
@@ -1014,16 +1065,19 @@ export class MessageDeliveryEngine {
|
|
|
1014
1065
|
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
1015
1066
|
}
|
|
1016
1067
|
if (client._seqTracker.getContiguousSeq(ns) !== pageContigBefore) {
|
|
1017
|
-
this.ensurePullOperationCurrent(ns);
|
|
1018
|
-
await this.drainOrderedMessages(ns, undefined, false, false);
|
|
1019
1068
|
this.ensurePullOperationCurrent(ns);
|
|
1020
1069
|
await client._commitSeqTrackerState(ns);
|
|
1070
|
+
this.ensurePullOperationCurrent(ns);
|
|
1071
|
+
if (!this.isInlineGenerationCurrent(pageInlineGeneration))
|
|
1072
|
+
throw new Error('pull invalidated');
|
|
1021
1073
|
}
|
|
1074
|
+
this.completePendingTailWindow(ns);
|
|
1022
1075
|
committed = true;
|
|
1023
1076
|
}
|
|
1024
1077
|
catch (exc) {
|
|
1025
|
-
if (this.isPullOperationCurrent(ns) &&
|
|
1026
|
-
client._seqTracker.restoreNamespaceSnapshot
|
|
1078
|
+
if (this.isPullOperationCurrent(ns) && this.isInlineGenerationCurrent(pageInlineGeneration)
|
|
1079
|
+
&& pageTrackerSnapshot !== undefined && !committed && typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
|
|
1080
|
+
client._seqTracker._restoreNamespaceSnapshotPreservingPush(ns, pageTrackerSnapshot);
|
|
1027
1081
|
this.dropSeqTrackerPending(ns);
|
|
1028
1082
|
}
|
|
1029
1083
|
throw exc;
|
|
@@ -1103,6 +1157,8 @@ export class MessageDeliveryEngine {
|
|
|
1103
1157
|
}
|
|
1104
1158
|
if (pullResponse) {
|
|
1105
1159
|
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source, ns, batch));
|
|
1160
|
+
if (pullScoped)
|
|
1161
|
+
this.ensurePullOperationCurrent(ns);
|
|
1106
1162
|
return;
|
|
1107
1163
|
}
|
|
1108
1164
|
await client._publishAppEvent(event, payload, source, ns, batch);
|
|
@@ -1111,6 +1167,10 @@ export class MessageDeliveryEngine {
|
|
|
1111
1167
|
}
|
|
1112
1168
|
async publishOrderedGroupChanged(payload, source = 'ordered') {
|
|
1113
1169
|
const client = this.runtime.client;
|
|
1170
|
+
const generation = this.captureInlineGeneration();
|
|
1171
|
+
await client._publishAppEvent('group.changed', payload, source);
|
|
1172
|
+
if (!this.isInlineGenerationCurrent(generation))
|
|
1173
|
+
throw new Error('pull invalidated');
|
|
1114
1174
|
if (isJsonObject(payload)) {
|
|
1115
1175
|
const eventPayload = payload;
|
|
1116
1176
|
client._groupState?.handleGroupChangedV2Membership?.(eventPayload);
|
|
@@ -1121,7 +1181,6 @@ export class MessageDeliveryEngine {
|
|
|
1121
1181
|
client._cleanupDissolvedGroup?.(groupId);
|
|
1122
1182
|
}
|
|
1123
1183
|
}
|
|
1124
|
-
await client._publishAppEvent('group.changed', payload, source);
|
|
1125
1184
|
}
|
|
1126
1185
|
isInstanceScopedMessageEvent(event) {
|
|
1127
1186
|
return event === 'message.received'
|
|
@@ -1559,6 +1618,7 @@ export class MessageDeliveryEngine {
|
|
|
1559
1618
|
}
|
|
1560
1619
|
async publishMessageRecallTombstone(seq, message, source = 'push') {
|
|
1561
1620
|
const client = this.runtime.client;
|
|
1621
|
+
const generation = this.captureInlineGeneration();
|
|
1562
1622
|
const eventPayload = this.recallEventFromMessage(message);
|
|
1563
1623
|
if (!eventPayload)
|
|
1564
1624
|
return false;
|
|
@@ -1572,13 +1632,15 @@ export class MessageDeliveryEngine {
|
|
|
1572
1632
|
client._clientLog.debug(`message.recalled dedup suppressed: seq=${String(seq)} key=${dedupKey}`);
|
|
1573
1633
|
return false;
|
|
1574
1634
|
}
|
|
1635
|
+
await client._publishAppEvent('message.recalled', eventPayload, source);
|
|
1636
|
+
if (!this.isInlineGenerationCurrent(generation))
|
|
1637
|
+
throw new Error('pull invalidated');
|
|
1575
1638
|
seen.set(dedupKey, Date.now());
|
|
1576
1639
|
if (seen.size > MESSAGE_RECALL_SEEN_LIMIT) {
|
|
1577
1640
|
const drop = [...seen.entries()].sort((a, b) => a[1] - b[1]).slice(0, seen.size - MESSAGE_RECALL_SEEN_LIMIT);
|
|
1578
1641
|
for (const [oldKey] of drop)
|
|
1579
1642
|
seen.delete(oldKey);
|
|
1580
1643
|
}
|
|
1581
|
-
await client._publishAppEvent('message.recalled', eventPayload, source);
|
|
1582
1644
|
client._clientLog.debug(`message.recalled published: seq=${String(seq)} ids=${JSON.stringify(eventPayload.message_ids)}`);
|
|
1583
1645
|
return true;
|
|
1584
1646
|
}
|
|
@@ -1663,6 +1725,7 @@ export class MessageDeliveryEngine {
|
|
|
1663
1725
|
}
|
|
1664
1726
|
async publishGroupRecallTombstone(groupId, seq, message, source = 'push') {
|
|
1665
1727
|
const client = this.runtime.client;
|
|
1728
|
+
const generation = this.captureInlineGeneration();
|
|
1666
1729
|
const eventPayload = this.recallEventFromGroupMessage(message);
|
|
1667
1730
|
if (!eventPayload)
|
|
1668
1731
|
return false;
|
|
@@ -1678,13 +1741,15 @@ export class MessageDeliveryEngine {
|
|
|
1678
1741
|
client._clientLog.debug(`group.message_recalled dedup suppressed: group=${groupId} seq=${String(seq)} key=${dedupKey}`);
|
|
1679
1742
|
return false;
|
|
1680
1743
|
}
|
|
1744
|
+
await client._publishAppEvent('group.message_recalled', eventPayload, source);
|
|
1745
|
+
if (!this.isInlineGenerationCurrent(generation))
|
|
1746
|
+
throw new Error('pull invalidated');
|
|
1681
1747
|
seen.set(dedupKey, Date.now());
|
|
1682
1748
|
if (seen.size > GROUP_RECALL_SEEN_LIMIT) {
|
|
1683
1749
|
const drop = [...seen.entries()].sort((a, b) => a[1] - b[1]).slice(0, seen.size - GROUP_RECALL_SEEN_LIMIT);
|
|
1684
1750
|
for (const [oldKey] of drop)
|
|
1685
1751
|
seen.delete(oldKey);
|
|
1686
1752
|
}
|
|
1687
|
-
await client._publishAppEvent('group.message_recalled', eventPayload, source);
|
|
1688
1753
|
client._clientLog.debug(`group.message_recalled published: group=${groupId} seq=${String(seq)} ids=${JSON.stringify(eventPayload.message_ids)}`);
|
|
1689
1754
|
return true;
|
|
1690
1755
|
}
|
|
@@ -1755,7 +1820,13 @@ export class MessageDeliveryEngine {
|
|
|
1755
1820
|
&& canonicalSource !== 'direct') {
|
|
1756
1821
|
normalized = { ...normalized, source: canonicalSource };
|
|
1757
1822
|
}
|
|
1758
|
-
|
|
1823
|
+
if (['message.received', 'group.message_created', 'message.recalled', 'group.message_recalled', 'group.changed'].includes(event)
|
|
1824
|
+
&& typeof client._dispatcher._publishAndWait === 'function') {
|
|
1825
|
+
await client._dispatcher._publishAndWait(event, normalized);
|
|
1826
|
+
}
|
|
1827
|
+
else {
|
|
1828
|
+
client._dispatcher.enqueue(event, normalized);
|
|
1829
|
+
}
|
|
1759
1830
|
if (generation !== this.deliveryGeneration)
|
|
1760
1831
|
return;
|
|
1761
1832
|
if (canonicalSource !== 'pull' && canonicalSource !== 'pending_retry'
|
|
@@ -1997,6 +2068,8 @@ export class MessageDeliveryEngine {
|
|
|
1997
2068
|
}
|
|
1998
2069
|
}
|
|
1999
2070
|
catch (exc) {
|
|
2071
|
+
if (exc?.stringCode === 'persistence_unavailable')
|
|
2072
|
+
throw exc;
|
|
2000
2073
|
client._clientLog.warn(`P2P push processing failed:${String(exc)}`);
|
|
2001
2074
|
if (isJsonObject(data)) {
|
|
2002
2075
|
const src = data;
|
|
@@ -2300,6 +2373,7 @@ export class MessageDeliveryEngine {
|
|
|
2300
2373
|
}
|
|
2301
2374
|
async fillGroupEventGap(groupId, options) {
|
|
2302
2375
|
const client = this.runtime.client;
|
|
2376
|
+
const generation = this.captureInlineGeneration();
|
|
2303
2377
|
if (client._sessionOptions?.background_sync === false)
|
|
2304
2378
|
return;
|
|
2305
2379
|
if (client.state !== ConnectionState.READY || client._closing)
|
|
@@ -2343,23 +2417,12 @@ export class MessageDeliveryEngine {
|
|
|
2343
2417
|
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
2344
2418
|
const eventObjects = events.filter((evt) => isJsonObject(evt));
|
|
2345
2419
|
const contiguousSeq = Number.isSafeInteger(result.contiguous_seq) ? result.contiguous_seq : undefined;
|
|
2346
|
-
if (contiguousSeq === undefined)
|
|
2347
|
-
client._seqTracker.onPullResult(ns, eventObjects, nextAfterSeq);
|
|
2348
|
-
else
|
|
2349
|
-
client._seqTracker.onPullResult(ns, eventObjects, nextAfterSeq, contiguousSeq);
|
|
2350
2420
|
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
2351
2421
|
const retentionFloor = Math.max(0, Number(result.retention_floor_event_seq ?? 0), Number(cursor?.retention_floor_seq ?? 0));
|
|
2352
2422
|
const serverAckFloor = Math.max(0, Number(cursor?.current_seq ?? 0));
|
|
2353
2423
|
const earliestAvailable = Number(result.earliest_available_event_seq ?? 0);
|
|
2354
2424
|
const visibilityFloor = Math.max(0, Number(cursor?.join_seq ?? 0), Number.isFinite(earliestAvailable) && earliestAvailable > 0 ? earliestAvailable - 1 : 0);
|
|
2355
2425
|
const effectiveFloor = Math.max(...[retentionFloor, serverAckFloor, visibilityFloor].filter((value) => Number.isFinite(value)));
|
|
2356
|
-
if (effectiveFloor > 0) {
|
|
2357
|
-
const contigBeforeFloor = client._seqTracker.getContiguousSeq(ns);
|
|
2358
|
-
if (contigBeforeFloor < effectiveFloor) {
|
|
2359
|
-
client._clientLog.info('group.pull_events floor advance: ns=' + ns + ' contiguous=' + contigBeforeFloor + ' -> effective_floor=' + effectiveFloor + ' retention_floor=' + retentionFloor + ' server_ack=' + serverAckFloor + ' visibility_floor=' + visibilityFloor);
|
|
2360
|
-
client._seqTracker.forceContiguousSeq(ns, effectiveFloor);
|
|
2361
|
-
}
|
|
2362
|
-
}
|
|
2363
2426
|
const eventSeqs = [];
|
|
2364
2427
|
let hasDissolvedEvent = false;
|
|
2365
2428
|
let publishedEventCount = 0;
|
|
@@ -2388,8 +2451,20 @@ export class MessageDeliveryEngine {
|
|
|
2388
2451
|
publishedEventCount += 1;
|
|
2389
2452
|
}
|
|
2390
2453
|
}
|
|
2454
|
+
const deliveryThrough = Math.max(pageContigBefore, contiguousSeq ?? 0, effectiveFloor, ...eventSeqs);
|
|
2455
|
+
await this.drainOrderedMessages(ns, undefined, false, false, undefined, 'pull', deliveryThrough);
|
|
2456
|
+
if (!this.isInlineGenerationCurrent(generation))
|
|
2457
|
+
throw new Error('pull invalidated');
|
|
2458
|
+
if (hasDissolvedEvent)
|
|
2459
|
+
break;
|
|
2460
|
+
if (contiguousSeq === undefined)
|
|
2461
|
+
client._seqTracker.onPullResult(ns, eventObjects, nextAfterSeq);
|
|
2462
|
+
else
|
|
2463
|
+
client._seqTracker.onPullResult(ns, eventObjects, nextAfterSeq, contiguousSeq);
|
|
2464
|
+
if (effectiveFloor > client._seqTracker.getContiguousSeq(ns)) {
|
|
2465
|
+
client._seqTracker.forceContiguousSeq(ns, effectiveFloor);
|
|
2466
|
+
}
|
|
2391
2467
|
const ackContig = client._seqTracker.getContiguousSeq(ns);
|
|
2392
|
-
await this.drainOrderedMessages(ns);
|
|
2393
2468
|
if (ackContig !== pageContigBefore && !hasDissolvedEvent) {
|
|
2394
2469
|
this.persistSeq(ns);
|
|
2395
2470
|
}
|
|
@@ -2879,9 +2954,8 @@ export class MessageDeliveryEngine {
|
|
|
2879
2954
|
async commitInlineMessage(ns, seq, event, payload, generation) {
|
|
2880
2955
|
const client = this.runtime.client;
|
|
2881
2956
|
const tracker = client._seqTracker;
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
: null;
|
|
2957
|
+
let snapshot = null;
|
|
2958
|
+
let trackerChanged = false;
|
|
2885
2959
|
try {
|
|
2886
2960
|
const selfSync = (event === 'message.received' && this.isSelfSentP2PMessage(payload))
|
|
2887
2961
|
|| (event === 'group.message_created' && this.isSelfSentGroupMessage(payload));
|
|
@@ -2892,6 +2966,8 @@ export class MessageDeliveryEngine {
|
|
|
2892
2966
|
return false;
|
|
2893
2967
|
if (!published)
|
|
2894
2968
|
return false;
|
|
2969
|
+
snapshot = typeof tracker.snapshotNamespace === 'function' ? tracker.snapshotNamespace(ns) : null;
|
|
2970
|
+
trackerChanged = true;
|
|
2895
2971
|
const needsPull = Boolean(tracker.onMessageSeq(ns, seq));
|
|
2896
2972
|
const committed = this.syncState(ns);
|
|
2897
2973
|
if (needsPull || committed.ack !== seq || committed.tail !== seq || committed.head !== seq) {
|
|
@@ -2905,8 +2981,8 @@ export class MessageDeliveryEngine {
|
|
|
2905
2981
|
return true;
|
|
2906
2982
|
}
|
|
2907
2983
|
catch (exc) {
|
|
2908
|
-
if (this.isInlineGenerationCurrent(generation) && typeof tracker.restoreNamespaceSnapshot === 'function') {
|
|
2909
|
-
tracker.
|
|
2984
|
+
if (trackerChanged && this.isInlineGenerationCurrent(generation) && typeof tracker.restoreNamespaceSnapshot === 'function') {
|
|
2985
|
+
tracker._restoreNamespaceSnapshotPreservingPush(ns, snapshot);
|
|
2910
2986
|
}
|
|
2911
2987
|
client._clientLog.debug(`inline realtime consume rolled back: ns=${ns} seq=${seq} err=${formatDeliveryError(exc)}`);
|
|
2912
2988
|
return false;
|
|
@@ -2995,7 +3071,7 @@ export class MessageDeliveryEngine {
|
|
|
2995
3071
|
}
|
|
2996
3072
|
}
|
|
2997
3073
|
hasOutstandingRealtimeGap(ns) {
|
|
2998
|
-
const state = this.
|
|
3074
|
+
const state = this.prospectiveTailState(ns);
|
|
2999
3075
|
// Tail 已建立可靠后缀窗口;后台续拉只针对尚未接上窗口的真实缺口。
|
|
3000
3076
|
// maxSeen 可能只是 Tail 期间到达的通知,不能单独据此启动全量补洞。
|
|
3001
3077
|
return state.ack < state.tail - 1;
|
|
@@ -3051,8 +3127,8 @@ export class MessageDeliveryEngine {
|
|
|
3051
3127
|
}
|
|
3052
3128
|
async runP2PRealtimeSync(ns, pushSeq, pageLimit = 50, forceTail = false) {
|
|
3053
3129
|
const client = this.runtime.client;
|
|
3054
|
-
const before = this.
|
|
3055
|
-
const order = forceTail ? ['tail'] : typeof client._seqTracker.planRealtimeSync === 'function'
|
|
3130
|
+
const before = this.prospectiveTailState(ns);
|
|
3131
|
+
const order = forceTail ? ['tail'] : this.pendingTailWindows.has(ns) ? ['forward'] : typeof client._seqTracker.planRealtimeSync === 'function'
|
|
3056
3132
|
? client._seqTracker.planRealtimeSync(ns, pushSeq, pageLimit)
|
|
3057
3133
|
: (() => {
|
|
3058
3134
|
const gap = pushSeq - before.ack;
|
|
@@ -3065,7 +3141,7 @@ export class MessageDeliveryEngine {
|
|
|
3065
3141
|
let result = { messages: [], raw_count: 0 };
|
|
3066
3142
|
let tailCompleted = false;
|
|
3067
3143
|
const run = async (tail, background, maxPages, forceForward = false, bypassCoveredHead = false) => {
|
|
3068
|
-
const live = this.
|
|
3144
|
+
const live = this.prospectiveTailState(ns);
|
|
3069
3145
|
if (tail && !bypassCoveredHead && pushSeq <= live.head)
|
|
3070
3146
|
return { messages: [], raw_count: 0 };
|
|
3071
3147
|
if (!tail && !forceForward && live.ack >= live.tail - 1)
|
|
@@ -3089,7 +3165,7 @@ export class MessageDeliveryEngine {
|
|
|
3089
3165
|
};
|
|
3090
3166
|
if (forceTail || order[0] === 'tail')
|
|
3091
3167
|
result = await run(true, false, undefined, false, forceTail);
|
|
3092
|
-
const state = this.
|
|
3168
|
+
const state = this.prospectiveTailState(ns);
|
|
3093
3169
|
const maxSeen = Number(client._seqTracker.getMaxSeenSeq(ns) || 0);
|
|
3094
3170
|
// Tail 期间到达的新 Push 只记录在 pending Pull 上界,不写入 SeqTracker。
|
|
3095
3171
|
const pendingUpper = Number(this.pendingP2pPullUpper?.get(ns) ?? 0);
|
|
@@ -3104,16 +3180,16 @@ export class MessageDeliveryEngine {
|
|
|
3104
3180
|
|| (client._sessionOptions?.background_sync !== false && backgroundWindowForward)) {
|
|
3105
3181
|
const forwardTarget = Math.max(headForward ? Math.max(pushSeq, maxSeen) : 0, (tailForward || backgroundWindowForward) ? state.tail - 1 : 0, tailMissedDuringPull ? observedUpper : 0);
|
|
3106
3182
|
const forwardMaxPages = this.forwardMaxPages(state.ack, forwardTarget, pageLimit);
|
|
3107
|
-
result = await run(false, !(headForward || tailForward), forwardMaxPages, headForward || tailMissedDuringPull);
|
|
3183
|
+
result = await run(false, !(headForward || tailForward), forwardMaxPages, headForward || tailForward || tailMissedDuringPull);
|
|
3108
3184
|
if (pendingThroughSeq > 0) {
|
|
3109
|
-
const committedAck = this.
|
|
3185
|
+
const committedAck = this.prospectiveTailState(ns).ack;
|
|
3110
3186
|
if (committedAck >= pendingThroughSeq)
|
|
3111
3187
|
this.consumePendingPull(ns, pendingThroughSeq);
|
|
3112
3188
|
else if (committedAck <= state.ack)
|
|
3113
3189
|
this.markPendingPullNoProgress(ns, committedAck);
|
|
3114
3190
|
}
|
|
3115
3191
|
}
|
|
3116
|
-
const finalState = this.
|
|
3192
|
+
const finalState = this.prospectiveTailState(ns);
|
|
3117
3193
|
if (tailCompleted && finalState.ack < finalState.tail - 1) {
|
|
3118
3194
|
this.scheduleP2PRealtimeFullGapFill(ns);
|
|
3119
3195
|
}
|
|
@@ -3123,8 +3199,8 @@ export class MessageDeliveryEngine {
|
|
|
3123
3199
|
}
|
|
3124
3200
|
async runGroupRealtimeSync(groupId, ns, pushSeq, pageLimit = 50, forceTail = false) {
|
|
3125
3201
|
const client = this.runtime.client;
|
|
3126
|
-
const before = this.
|
|
3127
|
-
const order = forceTail ? ['tail'] : typeof client._seqTracker.planRealtimeSync === 'function'
|
|
3202
|
+
const before = this.prospectiveTailState(ns);
|
|
3203
|
+
const order = forceTail ? ['tail'] : this.pendingTailWindows.has(ns) ? ['forward'] : typeof client._seqTracker.planRealtimeSync === 'function'
|
|
3128
3204
|
? client._seqTracker.planRealtimeSync(ns, pushSeq, pageLimit)
|
|
3129
3205
|
: (() => {
|
|
3130
3206
|
const gap = pushSeq - before.ack;
|
|
@@ -3137,7 +3213,7 @@ export class MessageDeliveryEngine {
|
|
|
3137
3213
|
let result = { messages: [], raw_count: 0 };
|
|
3138
3214
|
let tailCompleted = false;
|
|
3139
3215
|
const run = async (tail, background, maxPages, forceForward = false, bypassCoveredHead = false) => {
|
|
3140
|
-
const live = this.
|
|
3216
|
+
const live = this.prospectiveTailState(ns);
|
|
3141
3217
|
if (tail && !bypassCoveredHead && pushSeq <= live.head)
|
|
3142
3218
|
return { messages: [], raw_count: 0 };
|
|
3143
3219
|
if (!tail && !forceForward && live.ack >= live.tail - 1)
|
|
@@ -3161,7 +3237,7 @@ export class MessageDeliveryEngine {
|
|
|
3161
3237
|
};
|
|
3162
3238
|
if (forceTail || order[0] === 'tail')
|
|
3163
3239
|
result = await run(true, false, undefined, false, forceTail);
|
|
3164
|
-
const state = this.
|
|
3240
|
+
const state = this.prospectiveTailState(ns);
|
|
3165
3241
|
// 与 P2P 相同:真实完成 Tail 后,至多用一页 Forward 收敛 Tail
|
|
3166
3242
|
// 期间新到达的序号;绝不重新规划或再执行 Tail。
|
|
3167
3243
|
const maxSeen = Number(client._seqTracker.getMaxSeenSeq(ns) || 0);
|
|
@@ -3177,16 +3253,16 @@ export class MessageDeliveryEngine {
|
|
|
3177
3253
|
|| (client._sessionOptions?.background_sync !== false && backgroundWindowForward)) {
|
|
3178
3254
|
const forwardTarget = Math.max(headForward ? Math.max(pushSeq, maxSeen) : 0, (tailForward || backgroundWindowForward) ? state.tail - 1 : 0, tailMissedDuringPull ? observedUpper : 0);
|
|
3179
3255
|
const forwardMaxPages = this.forwardMaxPages(state.ack, forwardTarget, pageLimit);
|
|
3180
|
-
result = await run(false, !(headForward || tailForward), forwardMaxPages, headForward || tailMissedDuringPull);
|
|
3256
|
+
result = await run(false, !(headForward || tailForward), forwardMaxPages, headForward || tailForward || tailMissedDuringPull);
|
|
3181
3257
|
if (pendingThroughSeq > 0) {
|
|
3182
|
-
const committedAck = this.
|
|
3258
|
+
const committedAck = this.prospectiveTailState(ns).ack;
|
|
3183
3259
|
if (committedAck >= pendingThroughSeq)
|
|
3184
3260
|
this.consumePendingPull(ns, pendingThroughSeq);
|
|
3185
3261
|
else if (committedAck <= state.ack)
|
|
3186
3262
|
this.markPendingPullNoProgress(ns, committedAck);
|
|
3187
3263
|
}
|
|
3188
3264
|
}
|
|
3189
|
-
const finalState = this.
|
|
3265
|
+
const finalState = this.prospectiveTailState(ns);
|
|
3190
3266
|
if (tailCompleted && finalState.ack < finalState.tail - 1) {
|
|
3191
3267
|
this.scheduleGroupRealtimeFullGapFill(groupId, ns);
|
|
3192
3268
|
}
|
|
@@ -3803,7 +3879,7 @@ export class MessageDeliveryEngine {
|
|
|
3803
3879
|
}
|
|
3804
3880
|
return params;
|
|
3805
3881
|
}
|
|
3806
|
-
async drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true, batch, source = 'pull') {
|
|
3882
|
+
async drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true, batch, source = 'pull', throughSeq) {
|
|
3807
3883
|
const client = this.runtime.client;
|
|
3808
3884
|
const pullScoped = this.isPullScopedSource(source);
|
|
3809
3885
|
if (pullScoped)
|
|
@@ -3811,7 +3887,8 @@ export class MessageDeliveryEngine {
|
|
|
3811
3887
|
const queue = client._pendingOrderedMsgs.get(ns);
|
|
3812
3888
|
if (!queue || queue.size === 0)
|
|
3813
3889
|
return;
|
|
3814
|
-
const
|
|
3890
|
+
const generation = this.captureInlineGeneration();
|
|
3891
|
+
const contig = throughSeq ?? client._seqTracker.getContiguousSeq(ns);
|
|
3815
3892
|
const ready = [...queue.keys()]
|
|
3816
3893
|
.filter((seq) => seq <= contig && (beforeSeq === undefined || seq < beforeSeq))
|
|
3817
3894
|
.sort((a, b) => a - b);
|
|
@@ -3822,9 +3899,12 @@ export class MessageDeliveryEngine {
|
|
|
3822
3899
|
if (pullScoped)
|
|
3823
3900
|
this.ensurePullOperationCurrent(ns);
|
|
3824
3901
|
const item = queue.get(seq);
|
|
3825
|
-
|
|
3826
|
-
if (!item || client._pushedSeqs.get(ns)?.has(seq))
|
|
3902
|
+
if (!item)
|
|
3827
3903
|
continue;
|
|
3904
|
+
if (client._pushedSeqs.get(ns)?.has(seq)) {
|
|
3905
|
+
queue.delete(seq);
|
|
3906
|
+
continue;
|
|
3907
|
+
}
|
|
3828
3908
|
const itemSource = item.source ?? source;
|
|
3829
3909
|
let itemBatch = batch;
|
|
3830
3910
|
if ((itemSource === 'push' || itemSource === 'inline_push')
|
|
@@ -3836,8 +3916,13 @@ export class MessageDeliveryEngine {
|
|
|
3836
3916
|
}
|
|
3837
3917
|
}
|
|
3838
3918
|
await this.publishOrderedQueueItem(ns, item.event, seq, item.payload, pullResponse, itemSource, itemBatch);
|
|
3919
|
+
if (!this.isInlineGenerationCurrent(generation))
|
|
3920
|
+
throw new Error('pull invalidated');
|
|
3839
3921
|
if (pullScoped)
|
|
3840
3922
|
this.ensurePullOperationCurrent(ns);
|
|
3923
|
+
queue.delete(seq);
|
|
3924
|
+
if (item.event === 'group.changed' && isJsonObject(item.payload) && item.payload.action === 'dissolved')
|
|
3925
|
+
continue;
|
|
3841
3926
|
this.markPublishedSeq(ns, seq);
|
|
3842
3927
|
delivered = true;
|
|
3843
3928
|
}
|
|
@@ -3858,6 +3943,7 @@ export class MessageDeliveryEngine {
|
|
|
3858
3943
|
}
|
|
3859
3944
|
async publishOrderedMessage(event, ns, seq, payload, source = 'push', operationBatch) {
|
|
3860
3945
|
const client = this.runtime.client;
|
|
3946
|
+
const generation = this.captureInlineGeneration();
|
|
3861
3947
|
const ownsBatch = !operationBatch && (source === 'push' || source === 'inline_push');
|
|
3862
3948
|
const batch = operationBatch ?? (ownsBatch ? this.createDeliveryChangeBatch(source) : undefined);
|
|
3863
3949
|
try {
|
|
@@ -3887,10 +3973,12 @@ export class MessageDeliveryEngine {
|
|
|
3887
3973
|
if (client._pushedSeqs.get(ns)?.has(seqNum))
|
|
3888
3974
|
return false;
|
|
3889
3975
|
const queue = client._pendingOrderedMsgs.get(ns);
|
|
3976
|
+
await this.publishOrderedQueueItem(ns, event, seqNum, payload, false, source, batch);
|
|
3977
|
+
if (!this.isInlineGenerationCurrent(generation))
|
|
3978
|
+
throw new Error('pull invalidated');
|
|
3890
3979
|
queue?.delete(seqNum);
|
|
3891
3980
|
if (queue && queue.size === 0)
|
|
3892
3981
|
client._pendingOrderedMsgs.delete(ns);
|
|
3893
|
-
await this.publishOrderedQueueItem(ns, event, seqNum, payload, false, source, batch);
|
|
3894
3982
|
this.markPublishedSeq(ns, seqNum);
|
|
3895
3983
|
await this.drainOrderedMessages(ns, undefined, false, true, batch, source);
|
|
3896
3984
|
if (!client._pendingOrderedMsgs.get(ns))
|
|
@@ -3904,6 +3992,7 @@ export class MessageDeliveryEngine {
|
|
|
3904
3992
|
}
|
|
3905
3993
|
async publishPulledMessage(event, ns, seq, payload, persist = true, source = 'pull', operationBatch) {
|
|
3906
3994
|
const client = this.runtime.client;
|
|
3995
|
+
const generation = this.captureInlineGeneration();
|
|
3907
3996
|
const ownsBatch = !operationBatch && (source === 'push' || source === 'inline_push');
|
|
3908
3997
|
const batch = operationBatch ?? (ownsBatch ? this.createDeliveryChangeBatch(source) : undefined);
|
|
3909
3998
|
const pullScoped = this.isPullScopedSource(source);
|
|
@@ -3938,19 +4027,26 @@ export class MessageDeliveryEngine {
|
|
|
3938
4027
|
await this.drainOrderedMessages(ns, seqNum, false, persist, batch, source);
|
|
3939
4028
|
if (pullScoped)
|
|
3940
4029
|
this.ensurePullOperationCurrent(ns);
|
|
3941
|
-
queue?.delete(seqNum);
|
|
3942
|
-
if (queue && queue.size === 0)
|
|
3943
|
-
client._pendingOrderedMsgs.delete(ns);
|
|
3944
4030
|
if (event === 'message.recalled') {
|
|
3945
4031
|
const published = await client._withPullResponseProcessing(ns, () => this.publishMessageRecallTombstone(seqNum, payload, source));
|
|
4032
|
+
if (!this.isInlineGenerationCurrent(generation))
|
|
4033
|
+
throw new Error('pull invalidated');
|
|
3946
4034
|
if (pullScoped)
|
|
3947
4035
|
this.ensurePullOperationCurrent(ns);
|
|
4036
|
+
queue?.delete(seqNum);
|
|
4037
|
+
if (queue && queue.size === 0)
|
|
4038
|
+
client._pendingOrderedMsgs.delete(ns);
|
|
3948
4039
|
this.markPublishedSeq(ns, seqNum);
|
|
3949
4040
|
return published;
|
|
3950
4041
|
}
|
|
3951
4042
|
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source, ns, batch));
|
|
4043
|
+
if (!this.isInlineGenerationCurrent(generation))
|
|
4044
|
+
throw new Error('pull invalidated');
|
|
3952
4045
|
if (pullScoped)
|
|
3953
4046
|
this.ensurePullOperationCurrent(ns);
|
|
4047
|
+
queue?.delete(seqNum);
|
|
4048
|
+
if (queue && queue.size === 0)
|
|
4049
|
+
client._pendingOrderedMsgs.delete(ns);
|
|
3954
4050
|
this.markPublishedSeq(ns, seqNum);
|
|
3955
4051
|
return true;
|
|
3956
4052
|
}
|