@agentunion/fastaun-browser 0.4.13 → 0.5.1
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 +536 -386
- package/_packed_docs/CHANGELOG-validators.md +144 -0
- package/_packed_docs/CHANGELOG.md +536 -386
- package/_packed_docs/INDEX.md +159 -84
- package/_packed_docs/KITE_DOCS_GUIDE.md +48 -31
- package/_packed_docs/agent.md/SCHEMA.md +2 -0
- package/_packed_docs/agent.md/examples/codeagent-claudecode.md +2 -0
- package/_packed_docs/agent.md/examples/human-developer.md +2 -0
- package/_packed_docs/agent.md/examples/openclaw-lobster.md +2 -0
- package/_packed_docs/agent.md/examples/signed-openclaw-lobster.md +2 -0
- 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 +2 -0
- package/_packed_docs/cli/AUN-CLI/350/256/276/350/256/241/346/226/207/346/241/243.md +266 -686
- package/_packed_docs/cli/CLI/346/211/213/345/206/214.md +331 -0
- package/_packed_docs/design/2026-05-22-aun-rpc-trace-enhancement.md +2 -0
- package/_packed_docs/design/AUNClient/346/213/206/345/210/206/351/207/215/346/236/204/346/211/247/350/241/214/346/226/271/346/241/210.md +2 -0
- package/_packed_docs/design/E2EE_V2/347/256/200/345/214/226/344/270/2721DH/345/212/240Per-AID_Wrap/346/226/271/346/241/210.md +2 -0
- package/_packed_docs/design//350/267/250/350/257/255/350/250/200/345/256/271/345/231/250E2E/346/265/213/350/257/225/346/226/271/346/241/210.md +2 -0
- package/_packed_docs/protocol/00-/346/200/273/350/247/210/344/270/216/345/210/206/345/261/202.md +2 -0
- package/_packed_docs/protocol/00A-/350/256/276/350/256/241/345/216/237/345/210/231-/344/270/272Agent/350/200/214/347/224/237.md +2 -0
- 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 +2 -0
- package/_packed_docs/protocol/02-/350/257/201/344/271/246/344/270/216/344/277/241/344/273/273/344/275/223/347/263/273.md +2 -0
- package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +2 -0
- package/_packed_docs/protocol/04-Peer-/345/255/220/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol/05-Relay-/345/255/220/345/215/217/350/256/256.md +2 -0
- 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 +2 -0
- package/_packed_docs/protocol/08-AUN-E2EE-Group.md +296 -900
- package/_packed_docs/protocol/08-AUN-E2EE.md +2 -0
- package/_packed_docs/protocol/09-/345/256/211/345/205/250/350/200/203/350/231/221.md +2 -0
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +66 -173
- package/_packed_docs/protocol/11-Storage-/345/255/220/345/215/217/350/256/256.md +55 -0
- package/_packed_docs/protocol/12-Stream-/345/255/220/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol/13-Agent/350/241/214/344/270/272/350/247/204/350/214/203.md +2 -0
- package/_packed_docs/protocol/14-/344/272/244/344/272/222/346/234/272/345/210/266-/345/223/215/345/272/224/346/250/241/345/274/217/344/270/216/350/207/252/344/270/273/346/250/241/345/274/217.md +2 -0
- package/_packed_docs/protocol/15-/347/246/273/347/272/277/346/216/250/351/200/201/351/200/232/347/237/245/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol/16-/347/263/273/347/273/237/347/233/256/345/275/225/344/277/235/346/212/244/346/226/271/346/241/210.md +177 -0
- package/_packed_docs/protocol/README.md +4 -1
- package/_packed_docs/protocol/agent.md/SCHEMA.md +2 -0
- package/_packed_docs/protocol/agent.md/examples/codeagent-claudecode.md +2 -0
- package/_packed_docs/protocol/agent.md/examples/human-developer.md +2 -0
- package/_packed_docs/protocol/agent.md/examples/openclaw-lobster.md +2 -0
- package/_packed_docs/protocol/aun-docs-guide.md +2 -0
- package/_packed_docs/protocol/index.md +10 -3
- package/_packed_docs/protocol//350/215/211/346/241/210-agent.md/347/255/276/345/220/215/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol//350/215/211/346/241/210-/346/213/222/347/273/235/344/277/241/345/217/267/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225A-/346/234/257/350/257/255/350/241/250.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225B-/346/211/251/345/261/225/346/200/247/346/214/207/345/215/227.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225C-/347/247/201/351/222/245/347/256/241/347/220/206/344/270/216/350/272/253/344/273/275/346/201/242/345/244/215.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225D-Root_CA_/346/262/273/347/220/206/346/234/272/345/210/266.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225E-Root_CA_/345/207/206/345/205/245/346/265/201/347/250/213.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225F-Issuer_CA_/347/224/263/350/257/267/346/265/201/347/250/213.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225G-AID_/345/255/244/345/204/277/351/242/204/351/230/262/344/270/216/346/225/221/346/217/264/346/234/272/345/210/266.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225H-Identity/346/234/215/345/212/241/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225I-/350/267/250/345/237/237/346/266/210/346/201/257/350/267/257/347/224/261/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225J-/345/256/242/346/210/267/347/253/257/346/216/245/345/205/245/347/244/272/344/276/213.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225K-Agent_Web/345/217/221/347/216/260/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225L-E2EE/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225M-JWT/350/256/244/350/257/201/345/256/236/347/216/260/346/214/207/345/215/227.md +2 -0
- package/_packed_docs/protocol//351/231/204/345/275/225N-/345/210/206/345/270/203/345/274/217Trace/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +2 -0
- package/_packed_docs/sdk/02-WebSocket/345/215/217/350/256/256.md +2 -0
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +2 -0
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +2 -0
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +6 -252
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +219 -177
- package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +4 -1
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +2 -0
- package/_packed_docs/sdk/09-collab-rpc-manual.md +581 -0
- package/_packed_docs/sdk/09-custody-api-manual.md +2 -0
- package/_packed_docs/sdk/09-group-rpc-manual.md +301 -634
- package/_packed_docs/sdk/09-message-rpc-manual.md +2 -0
- package/_packed_docs/sdk/09-meta-rpc-manual.md +2 -0
- package/_packed_docs/sdk/09-payload-reference.md +2 -0
- package/_packed_docs/sdk/09-proxy-rpc-manual.md +2 -0
- package/_packed_docs/sdk/09-storage-rpc-manual.md +984 -274
- package/_packed_docs/sdk/09-stream-rpc-manual.md +2 -0
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +36 -23
- package/_packed_docs/sdk/E2EE_V2/346/266/210/346/201/257/351/200/232/344/277/241/346/227/266/345/272/217/345/233/276.md +2 -0
- package/_packed_docs/sdk/INDEX.md +47 -30
- package/_packed_docs/sdk/Notify/351/200/232/347/237/245/346/226/271/346/241/210.md +2 -0
- package/_packed_docs/sdk/README.md +17 -15
- package/dist/aid-store.d.ts +25 -0
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +89 -8
- package/dist/aid-store.js.map +1 -1
- package/dist/bundle.js +19615 -16870
- package/dist/client/delivery.d.ts +15 -2
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +303 -54
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.d.ts.map +1 -1
- package/dist/client/group-state.js +36 -14
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.js +2 -2
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +2 -0
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +235 -82
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +14 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +300 -121
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +36 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +460 -57
- package/dist/client.js.map +1 -1
- package/dist/collab/client.d.ts +47 -0
- package/dist/collab/client.d.ts.map +1 -0
- package/dist/collab/client.js +136 -0
- package/dist/collab/client.js.map +1 -0
- package/dist/collab/errors.d.ts +16 -0
- package/dist/collab/errors.d.ts.map +1 -0
- package/dist/collab/errors.js +60 -0
- package/dist/collab/errors.js.map +1 -0
- package/dist/collab/index.d.ts +4 -0
- package/dist/collab/index.d.ts.map +1 -0
- package/dist/collab/index.js +4 -0
- package/dist/collab/index.js.map +1 -0
- package/dist/collab/types.d.ts +111 -0
- package/dist/collab/types.d.ts.map +1 -0
- package/dist/collab/types.js +2 -0
- package/dist/collab/types.js.map +1 -0
- package/dist/crypto.d.ts +6 -0
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +6 -0
- package/dist/crypto.js.map +1 -1
- package/dist/errors.d.ts +0 -20
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +9 -37
- package/dist/errors.js.map +1 -1
- package/dist/facades.d.ts +94 -0
- package/dist/facades.d.ts.map +1 -0
- package/dist/facades.js +389 -0
- package/dist/facades.js.map +1 -0
- package/dist/group-fs.d.ts +120 -0
- package/dist/group-fs.d.ts.map +1 -0
- package/dist/group-fs.js +539 -0
- package/dist/group-fs.js.map +1 -0
- package/dist/group-id.d.ts +9 -12
- package/dist/group-id.d.ts.map +1 -1
- package/dist/group-id.js +41 -63
- package/dist/group-id.js.map +1 -1
- package/dist/group-resources.d.ts +98 -0
- package/dist/group-resources.d.ts.map +1 -0
- package/dist/group-resources.js +635 -0
- package/dist/group-resources.js.map +1 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +2 -54
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb-identity-store.d.ts +3 -0
- package/dist/keystore/indexeddb-identity-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-identity-store.js +65 -0
- package/dist/keystore/indexeddb-identity-store.js.map +1 -1
- package/dist/keystore/indexeddb-shared.d.ts +3 -17
- package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.js +4 -47
- package/dist/keystore/indexeddb-shared.js.map +1 -1
- package/dist/keystore/indexeddb-token-store.d.ts +1 -64
- package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-token-store.js +45 -774
- package/dist/keystore/indexeddb-token-store.js.map +1 -1
- package/dist/logger.d.ts +2 -0
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +4 -0
- package/dist/logger.js.map +1 -1
- package/dist/register-flow.d.ts +1 -0
- package/dist/register-flow.d.ts.map +1 -1
- package/dist/register-flow.js +3 -0
- package/dist/register-flow.js.map +1 -1
- package/dist/service-proxy.d.ts +1 -0
- package/dist/service-proxy.d.ts.map +1 -1
- package/dist/service-proxy.js +15 -3
- package/dist/service-proxy.js.map +1 -1
- package/dist/storage/errors.d.ts +28 -0
- package/dist/storage/errors.d.ts.map +1 -0
- package/dist/storage/errors.js +65 -0
- package/dist/storage/errors.js.map +1 -0
- package/dist/storage/index.d.ts +2 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/lowlevel.d.ts +322 -0
- package/dist/storage/lowlevel.d.ts.map +1 -0
- package/dist/storage/lowlevel.js +471 -0
- package/dist/storage/lowlevel.js.map +1 -0
- package/dist/storage/types.d.ts +59 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +113 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/storage/vfs.d.ts +166 -0
- package/dist/storage/vfs.d.ts.map +1 -0
- package/dist/storage/vfs.js +457 -0
- package/dist/storage/vfs.js.map +1 -0
- package/dist/tools/cross-sdk-agent.d.ts +3 -0
- package/dist/tools/cross-sdk-agent.d.ts.map +1 -0
- package/dist/tools/cross-sdk-agent.js +1376 -0
- package/dist/tools/cross-sdk-agent.js.map +1 -0
- package/dist/transport.d.ts +5 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +125 -12
- package/dist/transport.js.map +1 -1
- package/dist/types.d.ts +39 -56
- package/dist/types.d.ts.map +1 -1
- package/dist/v2/session/session.d.ts +2 -0
- package/dist/v2/session/session.d.ts.map +1 -1
- package/dist/v2/session/session.js +58 -22
- package/dist/v2/session/session.js.map +1 -1
- package/dist/v2/state/commitment.d.ts +1 -1
- package/dist/v2/state/commitment.d.ts.map +1 -1
- package/dist/v2/state/commitment.js +5 -3
- package/dist/v2/state/commitment.js.map +1 -1
- package/dist/validators.d.ts +35 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +127 -0
- package/dist/validators.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +44 -44
package/dist/client/delivery.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import { slotIsolationKey } from '../config.js';
|
|
2
2
|
import { normalizeGroupId } from '../group-id.js';
|
|
3
|
-
import { isJsonObject } from '../types.js';
|
|
3
|
+
import { ConnectionState, isJsonObject } from '../types.js';
|
|
4
4
|
const PUSHED_SEQS_LIMIT = 50_000;
|
|
5
5
|
const PENDING_ORDERED_LIMIT = 50_000;
|
|
6
6
|
const GROUP_RECALL_SEEN_LIMIT = 10_000;
|
|
7
|
+
const SEQ_TRACKER_PERSIST_FLUSH_DELAY_MS = 200;
|
|
8
|
+
const P2P_GAP_FILL_RETRY_LIMIT = 12;
|
|
9
|
+
const P2P_GAP_FILL_RETRY_BASE_MS = 1000;
|
|
10
|
+
const P2P_GAP_FILL_RETRY_MAX_MS = 5000;
|
|
7
11
|
const APP_MESSAGE_ENVELOPE_KEYS = [
|
|
8
12
|
'module_id', 'message_type', 'type', 'kind', 'version',
|
|
9
13
|
'from', 'from_aid', 'sender_aid', 'to', 'to_aid', 'group_id',
|
|
@@ -24,6 +28,33 @@ const APP_GROUP_EVENT_ENVELOPE_KEYS = [
|
|
|
24
28
|
function formatDeliveryError(error) {
|
|
25
29
|
return error instanceof Error ? error : String(error);
|
|
26
30
|
}
|
|
31
|
+
function isTransientGapFillError(error) {
|
|
32
|
+
const text = (error instanceof Error ? error.message : String(error)).toLowerCase();
|
|
33
|
+
return text.includes('no_service')
|
|
34
|
+
|| text.includes('service plane unavailable')
|
|
35
|
+
|| text.includes('transport not connected')
|
|
36
|
+
|| text.includes('transport closed')
|
|
37
|
+
|| text.includes('websocket closed')
|
|
38
|
+
|| text.includes('websocket is not open')
|
|
39
|
+
|| text.includes('connection closed')
|
|
40
|
+
|| text.includes('socket closed');
|
|
41
|
+
}
|
|
42
|
+
function p2pAppEventFromPlainPullMessage(message) {
|
|
43
|
+
const payload = message.payload;
|
|
44
|
+
const payloadType = isJsonObject(payload)
|
|
45
|
+
? String(payload.type ?? '').trim()
|
|
46
|
+
: '';
|
|
47
|
+
if (payloadType === 'message.recalled' || payloadType === 'message.recall' || message.type === 'message.recalled') {
|
|
48
|
+
return {
|
|
49
|
+
event: 'message.recalled',
|
|
50
|
+
payload: {
|
|
51
|
+
...message,
|
|
52
|
+
...(isJsonObject(payload) ? payload : {}),
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return { event: 'message.received', payload: message };
|
|
57
|
+
}
|
|
27
58
|
export class MessageDeliveryEngine {
|
|
28
59
|
runtime;
|
|
29
60
|
constructor(runtime) {
|
|
@@ -52,6 +83,34 @@ export class MessageDeliveryEngine {
|
|
|
52
83
|
client._pushedSeqs.set(ns, new Set(keep));
|
|
53
84
|
}
|
|
54
85
|
}
|
|
86
|
+
fireGroupEventAck(groupId, ns, eventSeq, label) {
|
|
87
|
+
const client = this.runtime.client;
|
|
88
|
+
const ackSeq = this.clampAckSeq('group.ack_events', 'event_seq', ns, eventSeq);
|
|
89
|
+
if (ackSeq <= 0)
|
|
90
|
+
return;
|
|
91
|
+
void client.call('group.ack_events', {
|
|
92
|
+
group_id: groupId,
|
|
93
|
+
event_seq: ackSeq,
|
|
94
|
+
device_id: client._deviceId,
|
|
95
|
+
slot_id: client._slotId,
|
|
96
|
+
_rpc_background: true,
|
|
97
|
+
}).catch((e) => {
|
|
98
|
+
client._clientLog.warn(`${label} failed: group=${groupId}`, e);
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
fireGroupV2Ack(groupId, ns, seq, label) {
|
|
102
|
+
const client = this.runtime.client;
|
|
103
|
+
const ackSeq = this.clampAckSeq('group.v2.ack', 'up_to_seq', ns, seq);
|
|
104
|
+
if (ackSeq <= 0)
|
|
105
|
+
return;
|
|
106
|
+
void client._callRawV2Rpc('group.v2.ack', {
|
|
107
|
+
group_id: groupId,
|
|
108
|
+
up_to_seq: ackSeq,
|
|
109
|
+
_rpc_background: true,
|
|
110
|
+
}).catch((e) => {
|
|
111
|
+
client._clientLog.debug(`${label} failed: group=${groupId} seq=${ackSeq} err=${String(e)}`);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
55
114
|
enqueueOrderedMessage(ns, event, seq, payload) {
|
|
56
115
|
const client = this.runtime.client;
|
|
57
116
|
let queue = client._pendingOrderedMsgs.get(ns);
|
|
@@ -83,7 +142,8 @@ export class MessageDeliveryEngine {
|
|
|
83
142
|
const eventPayload = payload;
|
|
84
143
|
client._groupState?.handleGroupChangedV2Membership?.(eventPayload);
|
|
85
144
|
if (eventPayload.action === 'dissolved') {
|
|
86
|
-
const
|
|
145
|
+
const rawGroupId = eventPayload.group_aid ?? eventPayload.group_id ?? '';
|
|
146
|
+
const groupId = normalizeGroupId(String(rawGroupId)) || String(rawGroupId).trim();
|
|
87
147
|
if (groupId)
|
|
88
148
|
client._cleanupDissolvedGroup?.(groupId);
|
|
89
149
|
}
|
|
@@ -478,10 +538,11 @@ export class MessageDeliveryEngine {
|
|
|
478
538
|
msg_seq: ackSeq,
|
|
479
539
|
device_id: client._deviceId,
|
|
480
540
|
slot_id: client._slotId,
|
|
541
|
+
_rpc_background: true,
|
|
481
542
|
}).catch((e) => { client._clientLog.warn('group recall auto-ack failed: group=' + groupId, e); });
|
|
482
543
|
}
|
|
483
544
|
if (contig !== contigBefore)
|
|
484
|
-
this.
|
|
545
|
+
this.persistSeq(ns);
|
|
485
546
|
}
|
|
486
547
|
async publishAppEvent(event, payload) {
|
|
487
548
|
const client = this.runtime.client;
|
|
@@ -564,10 +625,11 @@ export class MessageDeliveryEngine {
|
|
|
564
625
|
seq: ackSeq,
|
|
565
626
|
device_id: client._deviceId,
|
|
566
627
|
slot_id: client._slotId,
|
|
628
|
+
_rpc_background: true,
|
|
567
629
|
}).catch((e) => { client._clientLog.warn(`P2P auto-ack failed:${String(e)}`); });
|
|
568
630
|
}
|
|
569
631
|
if (contigAfter !== contigBefore)
|
|
570
|
-
this.
|
|
632
|
+
this.persistSeq(ns);
|
|
571
633
|
if (encryptedPush)
|
|
572
634
|
return;
|
|
573
635
|
}
|
|
@@ -641,10 +703,11 @@ export class MessageDeliveryEngine {
|
|
|
641
703
|
msg_seq: ackSeq,
|
|
642
704
|
device_id: client._deviceId,
|
|
643
705
|
slot_id: client._slotId,
|
|
706
|
+
_rpc_background: true,
|
|
644
707
|
}).catch((e) => { client._clientLog.warn('group recall auto-ack failed: group=' + groupId, e); });
|
|
645
708
|
}
|
|
646
709
|
if (contigAfter !== contigBefore)
|
|
647
|
-
this.
|
|
710
|
+
this.persistSeq(ns);
|
|
648
711
|
return;
|
|
649
712
|
}
|
|
650
713
|
const published = encryptedPush
|
|
@@ -663,10 +726,11 @@ export class MessageDeliveryEngine {
|
|
|
663
726
|
msg_seq: ackSeq,
|
|
664
727
|
device_id: client._deviceId,
|
|
665
728
|
slot_id: client._slotId,
|
|
729
|
+
_rpc_background: true,
|
|
666
730
|
}).catch((e) => { client._clientLog.warn('group message auto-ack failed: group=' + groupId, e); });
|
|
667
731
|
}
|
|
668
732
|
if (contigAfter !== contigBefore)
|
|
669
|
-
this.
|
|
733
|
+
this.persistSeq(ns);
|
|
670
734
|
if (encryptedPush)
|
|
671
735
|
return;
|
|
672
736
|
}
|
|
@@ -706,7 +770,7 @@ export class MessageDeliveryEngine {
|
|
|
706
770
|
const afterSeq = client._seqTracker.getContiguousSeq(ns);
|
|
707
771
|
try {
|
|
708
772
|
if (client._v2Session) {
|
|
709
|
-
await client._pullGroupV2Internal({ group_id: groupId, after_seq: afterSeq, limit: 50 });
|
|
773
|
+
await client._withBackgroundRpc(() => client._pullGroupV2Internal({ group_id: groupId, after_seq: afterSeq, limit: 50 }));
|
|
710
774
|
return;
|
|
711
775
|
}
|
|
712
776
|
const result = await client.call('group.pull', {
|
|
@@ -714,6 +778,7 @@ export class MessageDeliveryEngine {
|
|
|
714
778
|
after_message_seq: afterSeq,
|
|
715
779
|
device_id: client._deviceId,
|
|
716
780
|
limit: 50,
|
|
781
|
+
_rpc_background: true,
|
|
717
782
|
});
|
|
718
783
|
if (isJsonObject(result)) {
|
|
719
784
|
const messages = result.messages;
|
|
@@ -752,9 +817,26 @@ export class MessageDeliveryEngine {
|
|
|
752
817
|
}
|
|
753
818
|
await client._publishAppEvent('group.message_created', notification);
|
|
754
819
|
}
|
|
755
|
-
|
|
820
|
+
scheduleP2pGapRetry(ns, retryAttempt) {
|
|
821
|
+
const client = this.runtime.client;
|
|
822
|
+
if (retryAttempt > P2P_GAP_FILL_RETRY_LIMIT)
|
|
823
|
+
return;
|
|
824
|
+
const retryKey = `p2p_pull_retry:${ns}`;
|
|
825
|
+
if (client._gapFillDone.has(retryKey))
|
|
826
|
+
return;
|
|
827
|
+
const delayMs = Math.min(P2P_GAP_FILL_RETRY_BASE_MS * retryAttempt, P2P_GAP_FILL_RETRY_MAX_MS);
|
|
828
|
+
client._gapFillDone.add(retryKey);
|
|
829
|
+
client._clientLog.debug(`P2P message gap-fill retry scheduled: ns=${ns} attempt=${retryAttempt} delay_ms=${delayMs}`);
|
|
830
|
+
globalThis.setTimeout(() => {
|
|
831
|
+
client._gapFillDone.delete(retryKey);
|
|
832
|
+
if (client.state !== ConnectionState.READY || client._closing)
|
|
833
|
+
return;
|
|
834
|
+
client._safeAsync(this.fillP2pGap(retryAttempt));
|
|
835
|
+
}, delayMs);
|
|
836
|
+
}
|
|
837
|
+
async fillP2pGap(retryAttempt = 0) {
|
|
756
838
|
const client = this.runtime.client;
|
|
757
|
-
if (client.
|
|
839
|
+
if (client.state !== ConnectionState.READY || client._closing)
|
|
758
840
|
return;
|
|
759
841
|
if (!client._aid)
|
|
760
842
|
return;
|
|
@@ -767,24 +849,64 @@ export class MessageDeliveryEngine {
|
|
|
767
849
|
this.runtime.delivery.setGapFillActive(true);
|
|
768
850
|
let filled = 0;
|
|
769
851
|
try {
|
|
770
|
-
const messages = await client._pullV2(afterSeq, 50);
|
|
852
|
+
const messages = await client._withBackgroundRpc(() => client._pullV2(afterSeq, 50));
|
|
771
853
|
filled = messages.length;
|
|
772
854
|
this.prunePushedSeqs(ns);
|
|
773
855
|
}
|
|
774
856
|
catch (exc) {
|
|
775
|
-
|
|
857
|
+
const transient = isTransientGapFillError(exc);
|
|
858
|
+
if (transient) {
|
|
859
|
+
try {
|
|
860
|
+
filled = await this.fillP2pGapWithPlainPull(ns, afterSeq);
|
|
861
|
+
this.prunePushedSeqs(ns);
|
|
862
|
+
client._clientLog.warn(`P2P message gap-fill V2 unavailable, used plain pull fallback:${String(formatDeliveryError(exc))}`);
|
|
863
|
+
}
|
|
864
|
+
catch (fallbackExc) {
|
|
865
|
+
client._clientLog.warn(`P2P message gap-fill plain fallback failed:${String(formatDeliveryError(fallbackExc))}`);
|
|
866
|
+
this.scheduleP2pGapRetry(ns, retryAttempt + 1);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
client._clientLog.warn(`P2P message gap-fill failed:${String(formatDeliveryError(exc))}`);
|
|
871
|
+
}
|
|
776
872
|
}
|
|
777
873
|
finally {
|
|
778
874
|
client._gapFillDone.delete(dedupKey);
|
|
779
875
|
this.runtime.delivery.setGapFillActive(false);
|
|
780
|
-
|
|
781
|
-
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
async fillP2pGapWithPlainPull(ns, afterSeq) {
|
|
879
|
+
const client = this.runtime.client;
|
|
880
|
+
const params = {
|
|
881
|
+
after_seq: afterSeq,
|
|
882
|
+
limit: 50,
|
|
883
|
+
device_id: client._deviceId,
|
|
884
|
+
slot_id: client._slotId,
|
|
885
|
+
_rpc_background: true,
|
|
886
|
+
};
|
|
887
|
+
const raw = await client._transport.call('message.pull', params, undefined, undefined, true);
|
|
888
|
+
const result = typeof client._rpcPipeline?.postprocessResult === 'function'
|
|
889
|
+
? await client._rpcPipeline.postprocessResult('message.pull', params, raw)
|
|
890
|
+
: raw;
|
|
891
|
+
const messages = (isJsonObject(result) && Array.isArray(result.messages))
|
|
892
|
+
? result.messages
|
|
893
|
+
: [];
|
|
894
|
+
let published = 0;
|
|
895
|
+
for (const item of messages) {
|
|
896
|
+
if (!isJsonObject(item))
|
|
897
|
+
continue;
|
|
898
|
+
const msg = item;
|
|
899
|
+
const seq = Number(msg.seq ?? 0);
|
|
900
|
+
const appEvent = p2pAppEventFromPlainPullMessage(msg);
|
|
901
|
+
if (await this.publishPulledMessage(appEvent.event, ns, seq, appEvent.payload)) {
|
|
902
|
+
published += 1;
|
|
782
903
|
}
|
|
783
904
|
}
|
|
905
|
+
return published;
|
|
784
906
|
}
|
|
785
907
|
async fillGroupGap(groupId) {
|
|
786
908
|
const client = this.runtime.client;
|
|
787
|
-
if (client.
|
|
909
|
+
if (client.state !== ConnectionState.READY || client._closing)
|
|
788
910
|
return;
|
|
789
911
|
groupId = normalizeGroupId(groupId) || String(groupId ?? '').trim();
|
|
790
912
|
if (!groupId)
|
|
@@ -798,7 +920,7 @@ export class MessageDeliveryEngine {
|
|
|
798
920
|
this.runtime.delivery.setGapFillActive(true);
|
|
799
921
|
let filled = 0;
|
|
800
922
|
try {
|
|
801
|
-
const messages = await client._pullGroupV2(groupId, afterSeq, 50);
|
|
923
|
+
const messages = await client._withBackgroundRpc(() => client._pullGroupV2(groupId, afterSeq, 50));
|
|
802
924
|
filled = messages.length;
|
|
803
925
|
this.prunePushedSeqs(ns);
|
|
804
926
|
}
|
|
@@ -815,7 +937,10 @@ export class MessageDeliveryEngine {
|
|
|
815
937
|
}
|
|
816
938
|
async fillGroupEventGap(groupId) {
|
|
817
939
|
const client = this.runtime.client;
|
|
818
|
-
if (client.
|
|
940
|
+
if (client.state !== ConnectionState.READY || client._closing)
|
|
941
|
+
return;
|
|
942
|
+
groupId = normalizeGroupId(groupId) || String(groupId ?? '').trim();
|
|
943
|
+
if (!groupId)
|
|
819
944
|
return;
|
|
820
945
|
const ns = `group_event:${groupId}`;
|
|
821
946
|
const afterSeq = client._seqTracker.getContiguousSeq(ns);
|
|
@@ -835,6 +960,7 @@ export class MessageDeliveryEngine {
|
|
|
835
960
|
after_event_seq: nextAfterSeq,
|
|
836
961
|
device_id: client._deviceId,
|
|
837
962
|
limit: 50,
|
|
963
|
+
_rpc_background: true,
|
|
838
964
|
});
|
|
839
965
|
if (!isJsonObject(result))
|
|
840
966
|
return;
|
|
@@ -884,7 +1010,7 @@ export class MessageDeliveryEngine {
|
|
|
884
1010
|
const ackContig = client._seqTracker.getContiguousSeq(ns);
|
|
885
1011
|
await this.drainOrderedMessages(ns);
|
|
886
1012
|
if (ackContig !== pageContigBefore && !hasDissolvedEvent) {
|
|
887
|
-
this.
|
|
1013
|
+
this.persistSeq(ns);
|
|
888
1014
|
}
|
|
889
1015
|
if (eventObjects.length > 0 && ackContig > 0 && ackContig !== pageContigBefore) {
|
|
890
1016
|
const ackSeq = this.clampAckSeq('group.ack_events', 'event_seq', ns, ackContig);
|
|
@@ -893,6 +1019,7 @@ export class MessageDeliveryEngine {
|
|
|
893
1019
|
event_seq: ackSeq,
|
|
894
1020
|
device_id: client._deviceId,
|
|
895
1021
|
slot_id: client._slotId,
|
|
1022
|
+
_rpc_background: true,
|
|
896
1023
|
}).catch((e) => { client._clientLog.warn('group event auto-ack failed: group=' + groupId, e); });
|
|
897
1024
|
}
|
|
898
1025
|
const nextAfter = Math.max(eventSeqs.length > 0 ? Math.max(...eventSeqs) : nextAfterSeq, nextAfterSeq);
|
|
@@ -914,6 +1041,7 @@ export class MessageDeliveryEngine {
|
|
|
914
1041
|
}
|
|
915
1042
|
async handleGroupChangedEventSeq(data, groupId) {
|
|
916
1043
|
const client = this.runtime.client;
|
|
1044
|
+
groupId = normalizeGroupId(groupId) || String(groupId ?? '').trim();
|
|
917
1045
|
let needPull = false;
|
|
918
1046
|
const rawEventSeq = data.event_seq;
|
|
919
1047
|
const eventSeq = Number(rawEventSeq);
|
|
@@ -931,8 +1059,15 @@ export class MessageDeliveryEngine {
|
|
|
931
1059
|
}
|
|
932
1060
|
}
|
|
933
1061
|
const contigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
934
|
-
|
|
1062
|
+
const publishedDuplicate = client._pushedSeqs.get(ns)?.has(eventSeq) === true;
|
|
1063
|
+
if (eventSeq <= contigBefore || publishedDuplicate) {
|
|
935
1064
|
client._clientLog.debug(`group.changed skipped duplicate/stale: group=${groupId}, event_seq=${eventSeq}, contiguous=${contigBefore}`);
|
|
1065
|
+
if (eventSeq <= contigBefore) {
|
|
1066
|
+
this.fireGroupEventAck(groupId, ns, eventSeq, 'group event covered push ack');
|
|
1067
|
+
}
|
|
1068
|
+
else if (contigBefore > 0) {
|
|
1069
|
+
this.fireGroupEventAck(groupId, ns, contigBefore, 'group event covered push ack');
|
|
1070
|
+
}
|
|
936
1071
|
return;
|
|
937
1072
|
}
|
|
938
1073
|
this.enqueueOrderedMessage(ns, 'group.changed', eventSeq, data);
|
|
@@ -941,13 +1076,14 @@ export class MessageDeliveryEngine {
|
|
|
941
1076
|
await this.drainOrderedMessages(ns);
|
|
942
1077
|
if (ackContig > 0 && ackContig !== contigBefore) {
|
|
943
1078
|
if (data.action !== 'dissolved')
|
|
944
|
-
this.
|
|
1079
|
+
this.persistSeq(ns);
|
|
945
1080
|
const ackSeq = this.clampAckSeq('group.ack_events', 'event_seq', ns, ackContig);
|
|
946
1081
|
client._transport.call('group.ack_events', {
|
|
947
1082
|
group_id: groupId,
|
|
948
1083
|
event_seq: ackSeq,
|
|
949
1084
|
device_id: client._deviceId,
|
|
950
1085
|
slot_id: client._slotId,
|
|
1086
|
+
_rpc_background: true,
|
|
951
1087
|
}).catch((e) => {
|
|
952
1088
|
client._clientLog.warn('group event push auto-ack failed: group=' + groupId, e);
|
|
953
1089
|
});
|
|
@@ -990,7 +1126,7 @@ export class MessageDeliveryEngine {
|
|
|
990
1126
|
this.runtime.delivery.setOnlineUnreadHintDrainActive(true);
|
|
991
1127
|
try {
|
|
992
1128
|
while (client._onlineUnreadHintQueue.size > 0) {
|
|
993
|
-
if (client.state !==
|
|
1129
|
+
if (client.state !== ConnectionState.READY)
|
|
994
1130
|
return;
|
|
995
1131
|
if (client._sessionOptions?.background_sync === false)
|
|
996
1132
|
return;
|
|
@@ -1048,8 +1184,9 @@ export class MessageDeliveryEngine {
|
|
|
1048
1184
|
const ns = `group:${groupId}`;
|
|
1049
1185
|
client._seqTracker.updateMaxSeen(ns, seq);
|
|
1050
1186
|
const contigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
1051
|
-
if (contigBefore === seq) {
|
|
1187
|
+
if (contigBefore === seq || (eventKind === 'group.online_unread_hint' && contigBefore > seq)) {
|
|
1052
1188
|
client._clientLog.debug(`_onRawGroupV2MessageCreated duplicate push already covered: group=${groupId} seq=${seq}`);
|
|
1189
|
+
this.fireGroupV2Ack(groupId, ns, seq, 'group v2 covered push ack');
|
|
1053
1190
|
return;
|
|
1054
1191
|
}
|
|
1055
1192
|
const afterSeq = client._repairPushContiguousBound(ns, seq, false, '_raw.group.v2.message_created');
|
|
@@ -1061,7 +1198,7 @@ export class MessageDeliveryEngine {
|
|
|
1061
1198
|
client._gapFillDone.add(dedupKey);
|
|
1062
1199
|
try {
|
|
1063
1200
|
client._clientLog.debug(`_onRawGroupV2MessageCreated -> group.v2.pull group=${groupId} after_seq=${afterSeq}`);
|
|
1064
|
-
const messages = await client._pullGroupV2(groupId, afterSeq, 50);
|
|
1201
|
+
const messages = await client._withBackgroundRpc(() => client._pullGroupV2(groupId, afterSeq, 50));
|
|
1065
1202
|
client._clientLog.debug(`_onRawGroupV2MessageCreated pulled ${messages.length} msgs for group=${groupId}`);
|
|
1066
1203
|
}
|
|
1067
1204
|
finally {
|
|
@@ -1103,12 +1240,12 @@ export class MessageDeliveryEngine {
|
|
|
1103
1240
|
const published = await client._publishOrderedMessage('message.received', ns, pushSeq, decrypted);
|
|
1104
1241
|
const newContig = client._seqTracker.getContiguousSeq(ns);
|
|
1105
1242
|
if (newContig !== contigBefore) {
|
|
1106
|
-
|
|
1243
|
+
this.persistSeq(ns);
|
|
1107
1244
|
}
|
|
1108
1245
|
if (newContig > 0 && newContig !== contigBefore) {
|
|
1109
1246
|
const ackSeq = this.clampAckSeq('message.v2.ack', 'up_to_seq', ns, newContig);
|
|
1110
1247
|
try {
|
|
1111
|
-
await client._callRawV2Rpc('message.v2.ack', { up_to_seq: ackSeq });
|
|
1248
|
+
await client._callRawV2Rpc('message.v2.ack', { up_to_seq: ackSeq, _rpc_background: true });
|
|
1112
1249
|
}
|
|
1113
1250
|
catch (e) {
|
|
1114
1251
|
client._clientLog.debug(`V2 P2P push-ack failed: ${e}`);
|
|
@@ -1138,7 +1275,7 @@ export class MessageDeliveryEngine {
|
|
|
1138
1275
|
try {
|
|
1139
1276
|
do {
|
|
1140
1277
|
this.runtime.delivery.setV2PullPending(false);
|
|
1141
|
-
await client._pullV2();
|
|
1278
|
+
await client._withBackgroundRpc(() => client._pullV2());
|
|
1142
1279
|
const newContig = ns ? client._seqTracker.getContiguousSeq(ns) : -1;
|
|
1143
1280
|
client._clientLog.debug(`_onV2PushNotification pull done: contiguous_seq=${contigBefore}->${newContig} (push_seq=${pushSeq || 'null'})`);
|
|
1144
1281
|
} while (client._v2PullPending);
|
|
@@ -1265,50 +1402,141 @@ export class MessageDeliveryEngine {
|
|
|
1265
1402
|
}
|
|
1266
1403
|
return newState;
|
|
1267
1404
|
}
|
|
1268
|
-
saveSeqTrackerState() {
|
|
1405
|
+
async saveSeqTrackerState() {
|
|
1269
1406
|
const client = this.runtime.client;
|
|
1270
|
-
|
|
1407
|
+
const context = this.currentSeqTrackerContext();
|
|
1408
|
+
if (!context) {
|
|
1409
|
+
await this.flushSeqTrackerPending();
|
|
1271
1410
|
return;
|
|
1411
|
+
}
|
|
1272
1412
|
const state = client._seqTracker.exportState();
|
|
1273
|
-
|
|
1413
|
+
const pending = this.seqTrackerPending();
|
|
1414
|
+
if (pending.size > 0 && client._seqTrackerPendingPersistContext === context) {
|
|
1415
|
+
const liveNamespaces = new Set(Object.keys(state));
|
|
1416
|
+
for (const ns of [...pending.keys()]) {
|
|
1417
|
+
if (!liveNamespaces.has(ns))
|
|
1418
|
+
pending.delete(ns);
|
|
1419
|
+
}
|
|
1420
|
+
if (pending.size === 0)
|
|
1421
|
+
client._seqTrackerPendingPersistContext = null;
|
|
1422
|
+
}
|
|
1423
|
+
if (Object.keys(state).length > 0)
|
|
1424
|
+
this.mergeSeqTrackerPending(context, state);
|
|
1425
|
+
await this.flushSeqTrackerPending();
|
|
1426
|
+
}
|
|
1427
|
+
persistSeq(ns, forceSeq) {
|
|
1428
|
+
const client = this.runtime.client;
|
|
1429
|
+
const context = this.currentSeqTrackerContext();
|
|
1430
|
+
if (!context) {
|
|
1431
|
+
void this.flushSeqTrackerPending();
|
|
1432
|
+
return;
|
|
1433
|
+
}
|
|
1434
|
+
const seq = forceSeq ?? client._seqTracker.getContiguousSeq(ns);
|
|
1435
|
+
if (!ns || !Number.isFinite(seq) || seq <= 0)
|
|
1436
|
+
return;
|
|
1437
|
+
if (forceSeq !== undefined) {
|
|
1438
|
+
client._seqTracker.forceContiguousSeq(ns, seq);
|
|
1439
|
+
}
|
|
1440
|
+
this.mergeSeqTrackerPending(context, { [ns]: seq });
|
|
1441
|
+
this.scheduleSeqTrackerFlush();
|
|
1442
|
+
}
|
|
1443
|
+
async flushSeqTrackerPending() {
|
|
1444
|
+
const client = this.runtime.client;
|
|
1445
|
+
this.cancelSeqTrackerFlushTimer();
|
|
1446
|
+
const context = client._seqTrackerPendingPersistContext;
|
|
1447
|
+
const pending = this.seqTrackerPending();
|
|
1448
|
+
if (!context || pending.size === 0) {
|
|
1449
|
+
client._seqTrackerPendingPersistContext = null;
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
const [aid, deviceId, slotId] = JSON.parse(context);
|
|
1453
|
+
const state = Object.fromEntries(pending.entries());
|
|
1454
|
+
pending.clear();
|
|
1455
|
+
client._seqTrackerPendingPersistContext = null;
|
|
1456
|
+
await this.writeSeqTrackerState(aid, deviceId, slotId, state);
|
|
1457
|
+
}
|
|
1458
|
+
dropSeqTrackerPending(ns) {
|
|
1459
|
+
const client = this.runtime.client;
|
|
1460
|
+
const pending = this.seqTrackerPending();
|
|
1461
|
+
pending.delete(ns);
|
|
1462
|
+
if (pending.size === 0) {
|
|
1463
|
+
client._seqTrackerPendingPersistContext = null;
|
|
1464
|
+
this.cancelSeqTrackerFlushTimer();
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
currentSeqTrackerContext() {
|
|
1468
|
+
const client = this.runtime.client;
|
|
1469
|
+
if (!client._aid)
|
|
1470
|
+
return null;
|
|
1471
|
+
return JSON.stringify([client._aid, client._deviceId, client._slotId]);
|
|
1472
|
+
}
|
|
1473
|
+
seqTrackerPending() {
|
|
1474
|
+
const client = this.runtime.client;
|
|
1475
|
+
if (!(client._seqTrackerPendingPersist instanceof Map)) {
|
|
1476
|
+
client._seqTrackerPendingPersist = new Map();
|
|
1477
|
+
}
|
|
1478
|
+
return client._seqTrackerPendingPersist;
|
|
1479
|
+
}
|
|
1480
|
+
mergeSeqTrackerPending(context, state) {
|
|
1481
|
+
const client = this.runtime.client;
|
|
1482
|
+
const pending = this.seqTrackerPending();
|
|
1483
|
+
const pendingContext = client._seqTrackerPendingPersistContext;
|
|
1484
|
+
if (pendingContext && pendingContext !== context && pending.size > 0) {
|
|
1485
|
+
void this.flushSeqTrackerPending();
|
|
1486
|
+
}
|
|
1487
|
+
client._seqTrackerPendingPersistContext = context;
|
|
1488
|
+
for (const [ns, seq] of Object.entries(state)) {
|
|
1489
|
+
const seqNum = Number(seq);
|
|
1490
|
+
if (ns && Number.isFinite(seqNum) && seqNum > 0) {
|
|
1491
|
+
pending.set(ns, seqNum);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
scheduleSeqTrackerFlush() {
|
|
1496
|
+
const client = this.runtime.client;
|
|
1497
|
+
const pending = this.seqTrackerPending();
|
|
1498
|
+
if (pending.size === 0)
|
|
1499
|
+
return;
|
|
1500
|
+
if (client._seqTrackerFlushTimer)
|
|
1501
|
+
return;
|
|
1502
|
+
client._seqTrackerFlushTimer = setTimeout(() => {
|
|
1503
|
+
client._seqTrackerFlushTimer = null;
|
|
1504
|
+
void this.flushSeqTrackerPending();
|
|
1505
|
+
}, SEQ_TRACKER_PERSIST_FLUSH_DELAY_MS);
|
|
1506
|
+
}
|
|
1507
|
+
cancelSeqTrackerFlushTimer() {
|
|
1508
|
+
const client = this.runtime.client;
|
|
1509
|
+
if (client._seqTrackerFlushTimer) {
|
|
1510
|
+
clearTimeout(client._seqTrackerFlushTimer);
|
|
1511
|
+
client._seqTrackerFlushTimer = null;
|
|
1512
|
+
}
|
|
1513
|
+
}
|
|
1514
|
+
async writeSeqTrackerState(aid, deviceId, slotId, state) {
|
|
1515
|
+
const client = this.runtime.client;
|
|
1516
|
+
if (!aid || Object.keys(state).length === 0)
|
|
1274
1517
|
return;
|
|
1275
1518
|
try {
|
|
1276
1519
|
const saveFn = client._tokenStore.saveSeq?.bind(client._tokenStore);
|
|
1277
1520
|
if (typeof saveFn === 'function') {
|
|
1278
|
-
|
|
1279
|
-
saveFn(client._aid, client._deviceId, client._slotId, ns, seq).catch((exc) => {
|
|
1280
|
-
client._dispatcher.publish('seq_tracker.persist_error', {
|
|
1281
|
-
phase: 'save',
|
|
1282
|
-
aid: client._aid,
|
|
1283
|
-
device_id: client._deviceId,
|
|
1284
|
-
slot_id: client._slotId,
|
|
1285
|
-
error: String(exc),
|
|
1286
|
-
}).catch(() => { });
|
|
1287
|
-
});
|
|
1288
|
-
}
|
|
1521
|
+
await Promise.all(Object.entries(state).map(([ns, seq]) => saveFn(aid, deviceId, slotId, ns, seq)));
|
|
1289
1522
|
return;
|
|
1290
1523
|
}
|
|
1291
1524
|
if (typeof client._tokenStore.updateInstanceState === 'function') {
|
|
1292
|
-
client._tokenStore.updateInstanceState(
|
|
1293
|
-
current.seq_tracker_state
|
|
1525
|
+
await client._tokenStore.updateInstanceState(aid, deviceId, slotId, (current) => {
|
|
1526
|
+
const existing = isJsonObject(current.seq_tracker_state)
|
|
1527
|
+
? { ...current.seq_tracker_state }
|
|
1528
|
+
: {};
|
|
1529
|
+
current.seq_tracker_state = { ...existing, ...state };
|
|
1294
1530
|
return current;
|
|
1295
|
-
}).catch((exc) => {
|
|
1296
|
-
client._dispatcher.publish('seq_tracker.persist_error', {
|
|
1297
|
-
phase: 'save',
|
|
1298
|
-
aid: client._aid,
|
|
1299
|
-
device_id: client._deviceId,
|
|
1300
|
-
slot_id: client._slotId,
|
|
1301
|
-
error: String(exc),
|
|
1302
|
-
}).catch(() => { });
|
|
1303
1531
|
});
|
|
1304
1532
|
}
|
|
1305
1533
|
}
|
|
1306
1534
|
catch (exc) {
|
|
1307
1535
|
client._dispatcher.publish('seq_tracker.persist_error', {
|
|
1308
1536
|
phase: 'save',
|
|
1309
|
-
aid
|
|
1310
|
-
device_id:
|
|
1311
|
-
slot_id:
|
|
1537
|
+
aid,
|
|
1538
|
+
device_id: deviceId,
|
|
1539
|
+
slot_id: slotId,
|
|
1312
1540
|
error: String(exc),
|
|
1313
1541
|
}).catch(() => { });
|
|
1314
1542
|
}
|
|
@@ -1318,6 +1546,7 @@ export class MessageDeliveryEngine {
|
|
|
1318
1546
|
if (!client._aid || !ns)
|
|
1319
1547
|
return;
|
|
1320
1548
|
const seq = client._seqTracker.getContiguousSeq(ns);
|
|
1549
|
+
this.dropSeqTrackerPending(ns);
|
|
1321
1550
|
try {
|
|
1322
1551
|
if (seq > 0 && typeof client._tokenStore.saveSeq === 'function') {
|
|
1323
1552
|
client._tokenStore.saveSeq(client._aid, client._deviceId, client._slotId, ns, seq).catch((exc) => {
|
|
@@ -1332,8 +1561,21 @@ export class MessageDeliveryEngine {
|
|
|
1332
1561
|
});
|
|
1333
1562
|
return;
|
|
1334
1563
|
}
|
|
1564
|
+
if (seq <= 0 && typeof client._tokenStore.updateInstanceState === 'function') {
|
|
1565
|
+
Promise.resolve(client._tokenStore.updateInstanceState(client._aid, client._deviceId, client._slotId, (current) => {
|
|
1566
|
+
if (isJsonObject(current.seq_tracker_state)) {
|
|
1567
|
+
const next = { ...current.seq_tracker_state };
|
|
1568
|
+
delete next[ns];
|
|
1569
|
+
current.seq_tracker_state = next;
|
|
1570
|
+
}
|
|
1571
|
+
return current;
|
|
1572
|
+
})).catch((exc) => {
|
|
1573
|
+
client._clientLog.debug(`delete repaired seq failed: ns=${ns} err=${formatDeliveryError(exc)}`);
|
|
1574
|
+
});
|
|
1575
|
+
return;
|
|
1576
|
+
}
|
|
1335
1577
|
if (seq > 0) {
|
|
1336
|
-
this.saveSeqTrackerState();
|
|
1578
|
+
void this.saveSeqTrackerState();
|
|
1337
1579
|
}
|
|
1338
1580
|
}
|
|
1339
1581
|
catch (exc) {
|
|
@@ -1395,6 +1637,7 @@ export class MessageDeliveryEngine {
|
|
|
1395
1637
|
const ready = [...queue.keys()]
|
|
1396
1638
|
.filter((seq) => seq <= contig && (beforeSeq === undefined || seq < beforeSeq))
|
|
1397
1639
|
.sort((a, b) => a - b);
|
|
1640
|
+
let delivered = false;
|
|
1398
1641
|
for (const seq of ready) {
|
|
1399
1642
|
const item = queue.get(seq);
|
|
1400
1643
|
queue.delete(seq);
|
|
@@ -1402,9 +1645,13 @@ export class MessageDeliveryEngine {
|
|
|
1402
1645
|
continue;
|
|
1403
1646
|
await this.publishOrderedQueueItem(ns, item.event, seq, item.payload);
|
|
1404
1647
|
this.markPublishedSeq(ns, seq);
|
|
1648
|
+
delivered = true;
|
|
1405
1649
|
}
|
|
1406
|
-
if (queue.size === 0)
|
|
1650
|
+
if (queue.size === 0) {
|
|
1407
1651
|
client._pendingOrderedMsgs.delete(ns);
|
|
1652
|
+
if (delivered)
|
|
1653
|
+
await this.saveSeqTrackerState();
|
|
1654
|
+
}
|
|
1408
1655
|
}
|
|
1409
1656
|
async publishOrderedMessage(event, ns, seq, payload) {
|
|
1410
1657
|
const client = this.runtime.client;
|
|
@@ -1435,6 +1682,8 @@ export class MessageDeliveryEngine {
|
|
|
1435
1682
|
await this.publishOrderedQueueItem(ns, event, seqNum, payload);
|
|
1436
1683
|
this.markPublishedSeq(ns, seqNum);
|
|
1437
1684
|
await this.drainOrderedMessages(ns);
|
|
1685
|
+
if (!client._pendingOrderedMsgs.get(ns))
|
|
1686
|
+
await this.saveSeqTrackerState();
|
|
1438
1687
|
return true;
|
|
1439
1688
|
}
|
|
1440
1689
|
async publishPulledMessage(event, ns, seq, payload) {
|