@agentunion/fastaun 0.5.33 → 0.5.35
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 +59 -2
- package/README.md +28 -26
- package/_packed_docs/CHANGELOG.md +59 -2
- package/_packed_docs/INDEX.md +26 -12
- package/_packed_docs/KITE_DOCS_GUIDE.md +17 -11
- package/_packed_docs/agent.md/SCHEMA.md +1 -1
- package/_packed_docs/aun/346/265/213/350/257/225/350/277/220/350/241/214/346/214/207/345/215/227.md +2 -2
- package/_packed_docs/protocol/03-Gateway-/350/277/236/346/216/245/346/250/241/345/274/217.md +58 -8
- package/_packed_docs/protocol/Ownership/345/215/217/350/256/256/345/256/232/344/271/2112.1.md +17 -17
- package/_packed_docs/protocol/echo-probe-gateway.md +99 -0
- package/_packed_docs/protocol/echo-probe-sdk.md +91 -0
- package/_packed_docs/sdk/0.5.33-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +464 -316
- package/_packed_docs/sdk/0.5.33-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +499 -304
- package/_packed_docs/sdk/0.5.34-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +133 -0
- package/_packed_docs/sdk/0.5.34-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +107 -0
- package/_packed_docs/sdk/0.5.35-SDK/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +189 -0
- package/_packed_docs/sdk/0.5.35-SDK/345/274/200/345/217/221/344/270/216/347/247/273/346/244/215/346/214/207/345/215/227.md +204 -0
- package/_packed_docs/sdk/01-/345/277/253/351/200/237/345/274/200/345/247/213.md +16 -13
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +13 -1
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +19 -9
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +87 -20
- package/_packed_docs/sdk/09-collab-rpc-manual.md +48 -30
- package/_packed_docs/sdk/09-custody-api-manual.md +3 -3
- package/_packed_docs/sdk/09-group-rpc-manual.md +60 -13
- package/_packed_docs/sdk/09-message-rpc-manual.md +22 -0
- package/_packed_docs/sdk/09-meta-rpc-manual.md +2 -1
- package/_packed_docs/sdk/09-payload-reference.md +9 -8
- package/_packed_docs/sdk/09-proxy-rpc-manual.md +20 -14
- package/_packed_docs/sdk/09-storage-rpc-manual.md +1698 -1659
- package/_packed_docs/sdk/09-stream-rpc-manual.md +33 -20
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +19 -17
- package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/272/224/347/224/250/351/233/206/346/210/220/346/214/207/345/215/227.md +125 -30
- package/_packed_docs/sdk/AUN/344/272/213/344/273/266/345/217/221/345/270/203/350/256/242/351/230/205/345/274/200/345/217/221/347/247/273/346/244/215/346/214/207/345/215/227.md +109 -33
- package/_packed_docs/sdk/INDEX.md +30 -18
- package/_packed_docs/sdk/Ownership-SDK/347/274/226/346/216/222/346/214/207/345/215/2272.1.md +1 -1
- 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 +5 -5
- 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 +5 -5
- package/_packed_docs/sdk/Ownership2.1/350/257/246/347/273/206/350/256/276/350/256/241.md +9 -9
- package/_packed_docs/sdk/README.md +11 -6
- package/_packed_docs//346/266/210/346/201/257/345/274/271/346/200/247/350/241/245/346/213/211/351/200/232/351/201/223/345/256/236/346/226/275/350/256/241/345/210/222.md +400 -0
- package/_packed_docs//346/266/210/346/201/257/345/274/271/346/200/247/350/241/245/346/213/211/351/200/232/351/201/223/346/226/271/346/241/210.md +485 -0
- package/dist/auth.js +39 -45
- package/dist/auth.js.map +1 -1
- package/dist/client/control-messages.js +2 -2
- package/dist/client/control-messages.js.map +1 -1
- package/dist/client/delivery.d.ts +12 -1
- package/dist/client/delivery.js +281 -46
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/lifecycle.js +13 -1
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-budget.d.ts +21 -0
- package/dist/client/rpc-budget.js +82 -0
- package/dist/client/rpc-budget.js.map +1 -0
- package/dist/client/rpc-pipeline.d.ts +5 -0
- package/dist/client/rpc-pipeline.js +227 -63
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/sync-channels.d.ts +90 -0
- package/dist/client/sync-channels.js +508 -0
- package/dist/client/sync-channels.js.map +1 -0
- package/dist/client/v2-e2ee.d.ts +5 -1
- package/dist/client/v2-e2ee.js +420 -303
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +17 -0
- package/dist/client.js +373 -50
- package/dist/client.js.map +1 -1
- package/dist/events.d.ts +1 -0
- package/dist/events.js +5 -0
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mail-client.d.ts +101 -0
- package/dist/mail-client.js +369 -0
- package/dist/mail-client.js.map +1 -0
- package/dist/pubsub/binding.js +23 -3
- package/dist/pubsub/binding.js.map +1 -1
- package/dist/pubsub/channel.d.ts +15 -3
- package/dist/pubsub/channel.js +114 -7
- package/dist/pubsub/channel.js.map +1 -1
- package/dist/pubsub/client.d.ts +17 -2
- package/dist/pubsub/client.js +205 -40
- package/dist/pubsub/client.js.map +1 -1
- package/dist/pubsub/credentials.d.ts +3 -0
- package/dist/pubsub/credentials.js +57 -20
- package/dist/pubsub/credentials.js.map +1 -1
- package/dist/pubsub/index.d.ts +5 -2
- package/dist/pubsub/index.js +70 -54
- package/dist/pubsub/index.js.map +1 -1
- package/dist/pubsub/service.d.ts +9 -0
- package/dist/pubsub/service.js +266 -20
- package/dist/pubsub/service.js.map +1 -1
- package/dist/pubsub/types.d.ts +13 -1
- package/dist/storage/lowlevel.d.ts +3 -1
- package/dist/storage/lowlevel.js +13 -0
- package/dist/storage/lowlevel.js.map +1 -1
- package/dist/storage/types.d.ts +10 -0
- package/dist/storage/types.js.map +1 -1
- package/dist/storage/vfs.d.ts +3 -1
- package/dist/storage/vfs.js +2 -2
- package/dist/storage/vfs.js.map +1 -1
- package/dist/tools/cross-sdk-agent.d.ts +55 -5
- package/dist/tools/cross-sdk-agent.js +88 -21
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +16 -0
- package/dist/transport.js +129 -7
- package/dist/transport.js.map +1 -1
- package/dist/udp-standby.d.ts +89 -0
- package/dist/udp-standby.js +487 -0
- package/dist/udp-standby.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -1
- package/scripts/patch-imapflow-external.mjs +69 -0
package/dist/client/v2-e2ee.js
CHANGED
|
@@ -73,10 +73,10 @@ function strictWindowSeq(value, field, positive = false) {
|
|
|
73
73
|
function safeNonNegativeSeq(value) {
|
|
74
74
|
return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
75
75
|
}
|
|
76
|
-
function strictWindowLimit(value) {
|
|
76
|
+
function strictWindowLimit(value, maxLimit = V2_PULL_MAX_PAGE_LIMIT) {
|
|
77
77
|
const limit = strictWindowSeq(value, 'limit', true);
|
|
78
|
-
if (limit >
|
|
79
|
-
throw new ValidationError(`limit must be <= ${
|
|
78
|
+
if (limit > maxLimit)
|
|
79
|
+
throw new ValidationError(`limit must be <= ${maxLimit}`);
|
|
80
80
|
return limit;
|
|
81
81
|
}
|
|
82
82
|
function deduplicateWindowMessages(value, lowerSeq, upperSeq, boundary) {
|
|
@@ -545,6 +545,7 @@ export class V2E2EECoordinator {
|
|
|
545
545
|
if (identityChanged())
|
|
546
546
|
return;
|
|
547
547
|
this.runtime.v2.setSessionState(v2Store, session);
|
|
548
|
+
client._delivery.requestBackgroundPullRecovery();
|
|
548
549
|
client._clientLog.debug(`V2 session initialized aid=${aidAtStart} device=${deviceIdAtStart}`);
|
|
549
550
|
}
|
|
550
551
|
scheduleGroupSpkRegistration(groupId, opts) {
|
|
@@ -1316,12 +1317,91 @@ export class V2E2EECoordinator {
|
|
|
1316
1317
|
}
|
|
1317
1318
|
return plaintext;
|
|
1318
1319
|
}
|
|
1320
|
+
async pullPeerV2Volatile(senderAid, opts = {}) {
|
|
1321
|
+
const client = this.client;
|
|
1322
|
+
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
1323
|
+
const inlineGeneration = client._delivery.captureInlineAckGeneration?.();
|
|
1324
|
+
const ns = client._delivery.peerNamespace(senderAid);
|
|
1325
|
+
const limit = Number(opts.limit ?? _INTERNAL_MESSAGE_FLOW_LIMITS.pullPageSize);
|
|
1326
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > 200)
|
|
1327
|
+
throw new ValidationError('peer limit must be between 1 and 200');
|
|
1328
|
+
const maxPages = Math.max(1, Math.min(100, Number(opts.maxPages ?? 100) || 100));
|
|
1329
|
+
const maxPageBytes = client._syncChannelCoordinator?.volatilePageBytes?.() ?? 262144;
|
|
1330
|
+
await client._ensureV2SessionReady('message.pull');
|
|
1331
|
+
return await client._delivery.runPeerPageSerialized(`${ns}|volatile`, async () => {
|
|
1332
|
+
const messages = [];
|
|
1333
|
+
let pages = 0, rawCount = 0, hasMore = false;
|
|
1334
|
+
while (pages < maxPages) {
|
|
1335
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1336
|
+
if (opts.background && !client._delivery.hasP2pApplicationSubscription())
|
|
1337
|
+
break;
|
|
1338
|
+
let cursor = client._delivery.peerVolatilePullCursor(senderAid);
|
|
1339
|
+
const request = () => client._callRawV2Rpc('message.v2.pull', {
|
|
1340
|
+
cursor_scope: 'peer', sender_aid: senderAid, device_id: client._deviceId, slot_id: client._slotId,
|
|
1341
|
+
storage_scope: 'volatile', limit, max_page_bytes: maxPageBytes,
|
|
1342
|
+
...(cursor ? { after_volatile_cursor: cursor } : {}),
|
|
1343
|
+
...(opts.background ? { _rpc_background: true } : { _rpc_foreground: true }),
|
|
1344
|
+
});
|
|
1345
|
+
const reservation = await client._dispatcher?._reserveMessageCapacity?.(limit);
|
|
1346
|
+
try {
|
|
1347
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1348
|
+
let response;
|
|
1349
|
+
try {
|
|
1350
|
+
response = await request();
|
|
1351
|
+
}
|
|
1352
|
+
catch (error) {
|
|
1353
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1354
|
+
if (!cursor || !String(error instanceof Error ? error.message : error).toLowerCase().includes('volatile cursor epoch mismatch'))
|
|
1355
|
+
throw error;
|
|
1356
|
+
client._delivery.setPeerVolatilePullCursor(senderAid, '');
|
|
1357
|
+
cursor = '';
|
|
1358
|
+
response = await request();
|
|
1359
|
+
}
|
|
1360
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1361
|
+
if (!response || response.storage_scope !== 'volatile' || !Array.isArray(response.messages)
|
|
1362
|
+
|| response.messages.length > limit || typeof response.volatile_has_more !== 'boolean'
|
|
1363
|
+
|| response.has_more !== response.volatile_has_more || typeof response.next_volatile_cursor !== 'string'
|
|
1364
|
+
|| new TextEncoder().encode(JSON.stringify(response)).byteLength > maxPageBytes)
|
|
1365
|
+
throw new ValidationError('invalid volatile peer page');
|
|
1366
|
+
const rows = response.messages;
|
|
1367
|
+
for (const row of rows) {
|
|
1368
|
+
if (!isJsonObject(row) || row.storage !== 'volatile' || String(row.sender_aid ?? row.from_aid ?? row.from ?? '') !== senderAid)
|
|
1369
|
+
throw new ValidationError('volatile peer sender mismatch');
|
|
1370
|
+
}
|
|
1371
|
+
const handlers = client._delivery.snapshotApplicationHandlers?.();
|
|
1372
|
+
await this.withApplicationPage(ns, 'pull', async () => {
|
|
1373
|
+
const delivered = await this.publishV2VolatileMessages(rows, 'pull', true);
|
|
1374
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1375
|
+
if (opts.retainMessages !== false)
|
|
1376
|
+
messages.push(...delivered);
|
|
1377
|
+
}, reservation, handlers);
|
|
1378
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1379
|
+
if (!rows.every(row => client._delivery.isVolatileSeen(row)))
|
|
1380
|
+
throw new E2EEError('volatile peer delivery remains pending');
|
|
1381
|
+
client._delivery.setPeerVolatilePullCursor(senderAid, response.next_volatile_cursor);
|
|
1382
|
+
pages++;
|
|
1383
|
+
rawCount += rows.length;
|
|
1384
|
+
hasMore = response.volatile_has_more;
|
|
1385
|
+
if (!hasMore)
|
|
1386
|
+
break;
|
|
1387
|
+
if (response.next_volatile_cursor === cursor)
|
|
1388
|
+
throw new E2EEError('volatile peer cursor made no progress');
|
|
1389
|
+
}
|
|
1390
|
+
finally {
|
|
1391
|
+
reservation?.release();
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
return { sender_aid: senderAid, messages, complete: !hasMore, pages, raw_count: rawCount, volatile_has_more: hasMore };
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1319
1397
|
async pullPeerV2(senderAid, opts = {}) {
|
|
1320
1398
|
const client = this.client;
|
|
1321
1399
|
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
1322
1400
|
const inlineGeneration = client._delivery.captureInlineAckGeneration?.();
|
|
1323
1401
|
const pullVersion = opts.peerPullVersion ?? client._delivery.capturePeerTailVersion(senderAid);
|
|
1324
1402
|
await client._ensureV2SessionReady('message.pull');
|
|
1403
|
+
const volatile = opts.background && opts.window_mode !== 'tail' && client._syncChannelCoordinator?.needsSplit?.()
|
|
1404
|
+
? await this.pullPeerV2Volatile(senderAid, { background: true, limit: opts.limit, retainMessages: opts.retainMessages }) : null;
|
|
1325
1405
|
this.ensureDeliveryCurrent(client._delivery.peerNamespace(senderAid), generation, inlineGeneration);
|
|
1326
1406
|
const ns = await client._delivery.initializePeer(senderAid);
|
|
1327
1407
|
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
@@ -1337,14 +1417,14 @@ export class V2E2EECoordinator {
|
|
|
1337
1417
|
if (mode !== 'tail' && opts.force !== true && after > client._seqTracker.getContiguousSeq(ns))
|
|
1338
1418
|
throw new ValidationError('after_seq cannot exceed the peer delivery cursor');
|
|
1339
1419
|
let volatileCursor = String(opts.after_volatile_cursor ?? client._delivery.peerVolatilePullCursor(senderAid));
|
|
1340
|
-
const decoded = [];
|
|
1420
|
+
const decoded = volatile?.messages ?? [];
|
|
1341
1421
|
let complete = false;
|
|
1342
1422
|
let head = after;
|
|
1343
1423
|
let tailVolatileHasMore;
|
|
1344
1424
|
let tailServerAck = null;
|
|
1345
1425
|
let pages = 0;
|
|
1346
|
-
let rawCount = 0;
|
|
1347
|
-
let forwardVolatileHasMore = false;
|
|
1426
|
+
let rawCount = volatile?.raw_count ?? 0;
|
|
1427
|
+
let forwardVolatileHasMore = volatile?.volatile_has_more ?? false;
|
|
1348
1428
|
let durableAck = 0;
|
|
1349
1429
|
let initializedAck = false;
|
|
1350
1430
|
let confirmedAck = 0;
|
|
@@ -1380,7 +1460,7 @@ export class V2E2EECoordinator {
|
|
|
1380
1460
|
else {
|
|
1381
1461
|
start = strictWindowSeq(response.scan_after_seq, 'scan_after_seq');
|
|
1382
1462
|
const retention = safeNonNegativeSeq(response.retention_floor_seq) ?? 0;
|
|
1383
|
-
if (start < requestAfter || (!preparing && start > Math.max(requestAfter, retention, opts.force === true ? 0 : committedAck))
|
|
1463
|
+
if (start < Math.max(requestAfter, retention) || (!preparing && start > Math.max(requestAfter, retention, opts.force === true ? 0 : committedAck))
|
|
1384
1464
|
|| end < start || end > pageHead || response.next_after_seq !== end)
|
|
1385
1465
|
throw new ValidationError('invalid peer Forward proof');
|
|
1386
1466
|
if (typeof response.persistent_has_more !== 'boolean' || typeof response.volatile_has_more !== 'boolean'
|
|
@@ -1492,6 +1572,7 @@ export class V2E2EECoordinator {
|
|
|
1492
1572
|
}
|
|
1493
1573
|
catch (error) {
|
|
1494
1574
|
fetched.reservation?.release();
|
|
1575
|
+
client._syncChannelCoordinator?.releaseResponse(fetched.response);
|
|
1495
1576
|
throw error;
|
|
1496
1577
|
}
|
|
1497
1578
|
});
|
|
@@ -1606,13 +1687,14 @@ export class V2E2EECoordinator {
|
|
|
1606
1687
|
}
|
|
1607
1688
|
finally {
|
|
1608
1689
|
reservation?.release();
|
|
1690
|
+
client._syncChannelCoordinator?.releaseResponse(response);
|
|
1609
1691
|
}
|
|
1610
1692
|
});
|
|
1611
1693
|
if (!page)
|
|
1612
1694
|
break;
|
|
1613
1695
|
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1614
1696
|
rawCount += page.response.messages.length;
|
|
1615
|
-
forwardVolatileHasMore = page.response.volatile_has_more === true;
|
|
1697
|
+
forwardVolatileHasMore = volatile ? volatile.volatile_has_more === true : page.response.volatile_has_more === true;
|
|
1616
1698
|
if (opts.retainMessages !== false)
|
|
1617
1699
|
decoded.push(...page.messages);
|
|
1618
1700
|
head = page.head;
|
|
@@ -1624,7 +1706,7 @@ export class V2E2EECoordinator {
|
|
|
1624
1706
|
}
|
|
1625
1707
|
volatileCursor = client._delivery.peerVolatilePullCursor(senderAid);
|
|
1626
1708
|
const more = page.response.persistent_has_more === true || page.response.volatile_has_more === true || page.response.has_more === true;
|
|
1627
|
-
complete = !more;
|
|
1709
|
+
complete = !more && !forwardVolatileHasMore;
|
|
1628
1710
|
client._delivery.syncProgress(ns, { rawCount: page.response.messages.length, pulledCount: page.messages.length, hasMore: more,
|
|
1629
1711
|
currentSeq: client._seqTracker.getContiguousSeq(ns), targetSeq: head });
|
|
1630
1712
|
after = page.end;
|
|
@@ -1640,7 +1722,9 @@ export class V2E2EECoordinator {
|
|
|
1640
1722
|
stopping.abort();
|
|
1641
1723
|
for (const pending of prefetched) {
|
|
1642
1724
|
try {
|
|
1643
|
-
|
|
1725
|
+
const fetched = await (pending.prepared ?? pending.response);
|
|
1726
|
+
fetched?.reservation?.release();
|
|
1727
|
+
client._syncChannelCoordinator?.releaseResponse(fetched?.response);
|
|
1644
1728
|
}
|
|
1645
1729
|
catch { }
|
|
1646
1730
|
}
|
|
@@ -1649,9 +1733,16 @@ export class V2E2EECoordinator {
|
|
|
1649
1733
|
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1650
1734
|
const pending = this.pendingForwardAck(ns, durableAck);
|
|
1651
1735
|
if (pending > 0) {
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1736
|
+
const confirm = async (seq) => {
|
|
1737
|
+
await this.ackPeerV2(senderAid, seq, [], [], Boolean(opts.background));
|
|
1738
|
+
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1739
|
+
confirmAck(seq);
|
|
1740
|
+
this.clearForwardAck(ns, seq);
|
|
1741
|
+
};
|
|
1742
|
+
if (opts.background)
|
|
1743
|
+
client._delivery.scheduleForwardAck(ns, pending, confirm);
|
|
1744
|
+
else
|
|
1745
|
+
await confirm(pending);
|
|
1655
1746
|
}
|
|
1656
1747
|
}
|
|
1657
1748
|
catch (error) {
|
|
@@ -1681,7 +1772,7 @@ export class V2E2EECoordinator {
|
|
|
1681
1772
|
|| (response.slot_id !== undefined && response.slot_id !== client._slotId))
|
|
1682
1773
|
throw new ValidationError('peer response scope mismatch');
|
|
1683
1774
|
}
|
|
1684
|
-
async ackPeerV2(senderAid, upToSeq, volatileCursors = [], volatileIds = []) {
|
|
1775
|
+
async ackPeerV2(senderAid, upToSeq, volatileCursors = [], volatileIds = [], background = true) {
|
|
1685
1776
|
const client = this.client;
|
|
1686
1777
|
const generation = Number(client._v2RuntimeGeneration ?? 0);
|
|
1687
1778
|
const inlineGeneration = client._delivery.captureInlineAckGeneration?.();
|
|
@@ -1696,7 +1787,7 @@ export class V2E2EECoordinator {
|
|
|
1696
1787
|
const result = await client._callRawV2Rpc('message.v2.ack', { cursor_scope: 'peer', sender_aid: senderAid,
|
|
1697
1788
|
device_id: client._deviceId, slot_id: client._slotId,
|
|
1698
1789
|
...(seq ? { up_to_seq: seq } : {}), ...(volatileCursors.length ? { volatile_cursors: volatileCursors } : {}),
|
|
1699
|
-
...(volatileIds.length ? { volatile_ids: volatileIds } : {}), _rpc_background:
|
|
1790
|
+
...(volatileIds.length ? { volatile_ids: volatileIds } : {}), _rpc_background: background, _rpc_foreground: !background });
|
|
1700
1791
|
this.ensureDeliveryCurrent(ns, generation, inlineGeneration);
|
|
1701
1792
|
this.validatePeerResponse(result, senderAid);
|
|
1702
1793
|
const actual = safeNonNegativeSeq(result.peer_ack_seq ?? result.effective_ack_seq ?? result.up_to_seq);
|
|
@@ -2296,7 +2387,10 @@ export class V2E2EECoordinator {
|
|
|
2296
2387
|
this.ensureDeliveryCurrent(ns, generation);
|
|
2297
2388
|
client._clientLog.debug(`message.v2.pull flushing pending auto-ack: ns=${ns}, ack_seq=${pendingAckSeq}`);
|
|
2298
2389
|
const ackToFlush = pendingAckSeq;
|
|
2299
|
-
|
|
2390
|
+
if (opts?.background)
|
|
2391
|
+
client._delivery.scheduleForwardAck(ns, ackToFlush, (seq) => this.confirmForwardP2PAck(seq));
|
|
2392
|
+
else
|
|
2393
|
+
await this.confirmForwardP2PAck(ackToFlush);
|
|
2300
2394
|
if (pendingAckSeq <= ackToFlush)
|
|
2301
2395
|
pendingAckSeq = 0;
|
|
2302
2396
|
}
|
|
@@ -2710,7 +2804,7 @@ export class V2E2EECoordinator {
|
|
|
2710
2804
|
throw new ValidationError('group_id is required');
|
|
2711
2805
|
let groupAid = String(params.group_aid ?? '').trim();
|
|
2712
2806
|
const afterSeq = strictWindowSeq(params.after_seq ?? 0, 'after_seq');
|
|
2713
|
-
const limit = strictWindowLimit(params.limit ?? V2_PULL_DEFAULT_PAGE_LIMIT);
|
|
2807
|
+
const limit = strictWindowLimit(params.limit ?? V2_PULL_DEFAULT_PAGE_LIMIT, 200);
|
|
2714
2808
|
const ns = `group:${groupId}`;
|
|
2715
2809
|
const namespaceGeneration = client._delivery.captureSeqNamespaceGeneration?.(ns);
|
|
2716
2810
|
const explicitCursorParams = isJsonObject(params._group_cursor_params)
|
|
@@ -2823,6 +2917,7 @@ export class V2E2EECoordinator {
|
|
|
2823
2917
|
await this.pullGroupV2(params.group_id, params.after_seq, params.limit, {
|
|
2824
2918
|
gateLocked: true,
|
|
2825
2919
|
explicitAfterSeq: true,
|
|
2920
|
+
retainMessages: false,
|
|
2826
2921
|
force: params.force === true,
|
|
2827
2922
|
});
|
|
2828
2923
|
}
|
|
@@ -2871,6 +2966,12 @@ export class V2E2EECoordinator {
|
|
|
2871
2966
|
}), opts?.background !== false);
|
|
2872
2967
|
}
|
|
2873
2968
|
const decrypted = [];
|
|
2969
|
+
let decryptedCount = 0;
|
|
2970
|
+
const collectMessage = (message) => {
|
|
2971
|
+
decryptedCount += 1;
|
|
2972
|
+
if (opts?.retainMessages !== false)
|
|
2973
|
+
decrypted.push(message);
|
|
2974
|
+
};
|
|
2874
2975
|
const wireGroupId = String(opts?.wireGroupId ?? groupId ?? '').trim() || gid;
|
|
2875
2976
|
const cursorParams = opts?.cursorParams ?? {};
|
|
2876
2977
|
const ownsCursor = opts?.ownsCursor !== false;
|
|
@@ -2886,18 +2987,21 @@ export class V2E2EECoordinator {
|
|
|
2886
2987
|
let lastAutoAckSeq = 0;
|
|
2887
2988
|
let pendingAckSeq = deferredForwardAck;
|
|
2888
2989
|
let committedAckSeq = ownsCursor ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
2990
|
+
const syncRetainedPages = new Set();
|
|
2889
2991
|
const fetchPage = async (after, ack) => {
|
|
2890
2992
|
const key = pullGateKeyForClient(client, 'group.v2.pull', {
|
|
2891
2993
|
group_id: gid, after_seq: after, force: opts?.force === true, limit, _group_cursor_params: cursorParams,
|
|
2892
2994
|
}, `${ns}|forward|after=${after}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
2893
2995
|
return await client._runPullSerialized(key, async () => {
|
|
2894
2996
|
this.ensureDeliveryCurrent(ns, generation);
|
|
2895
|
-
|
|
2997
|
+
const response = await client._callRawV2Rpc('group.v2.pull', withExplicitGroupAid({
|
|
2896
2998
|
group_id: wireGroupId, after_seq: after, limit, ...cursorParams,
|
|
2897
2999
|
...(opts?.force ? { force: true } : {}),
|
|
2898
3000
|
...(ack > 0 ? { ack_up_to_seq: Math.min(ack, committedAckSeq) } : {}),
|
|
2899
3001
|
_rpc_background: background, _rpc_foreground: !background,
|
|
2900
3002
|
}, groupAid), key);
|
|
3003
|
+
syncRetainedPages.add(response);
|
|
3004
|
+
return response;
|
|
2901
3005
|
}, background);
|
|
2902
3006
|
};
|
|
2903
3007
|
const maxPages = Math.max(1, Math.min(100, Math.trunc(Number(opts?.maxPages ?? 100) || 100)));
|
|
@@ -2998,92 +3102,121 @@ export class V2E2EECoordinator {
|
|
|
2998
3102
|
else {
|
|
2999
3103
|
result = await fetchPage(nextAfterSeq, ackUpToSeq);
|
|
3000
3104
|
}
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
if (
|
|
3007
|
-
|
|
3105
|
+
try {
|
|
3106
|
+
if (ownsCursor)
|
|
3107
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
3108
|
+
if (ackUpToSeq > 0) {
|
|
3109
|
+
const actualAckSeq = client._delivery.resolveGroupPullAckSeq(result, ackUpToSeq);
|
|
3110
|
+
if (actualAckSeq >= ackUpToSeq) {
|
|
3111
|
+
if (inlineAckGeneration !== null) {
|
|
3112
|
+
client._delivery.onGroupV2PullAckResult(gid, result, ackUpToSeq, inlineAckGeneration);
|
|
3113
|
+
}
|
|
3114
|
+
if (pendingAckSeq <= ackUpToSeq)
|
|
3115
|
+
pendingAckSeq = 0;
|
|
3116
|
+
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
3117
|
+
this.clearForwardAck(ns, ackUpToSeq);
|
|
3118
|
+
}
|
|
3119
|
+
else {
|
|
3120
|
+
client._clientLog.warn(`group.v2.pull piggyback ACK below target: group=${gid}, requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
|
|
3008
3121
|
}
|
|
3009
|
-
if (pendingAckSeq <= ackUpToSeq)
|
|
3010
|
-
pendingAckSeq = 0;
|
|
3011
|
-
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackUpToSeq);
|
|
3012
|
-
this.clearForwardAck(ns, ackUpToSeq);
|
|
3013
|
-
}
|
|
3014
|
-
else {
|
|
3015
|
-
client._clientLog.warn(`group.v2.pull piggyback ACK below target: group=${gid}, requested=${ackUpToSeq}, actual=${actualAckSeq}; fallback terminal ACK`);
|
|
3016
3122
|
}
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
client._clientLog.debug(`group.v2.pull page response: group=${gid}, page=${pageCount}, raw_count=${messages.length}, stale_count=${Math.max(0, pageMessages.length - messages.length)}, has_more=${String(result.has_more ?? '')}, server_head=${serverHead}, cursor_current=${String(cursor?.current_seq ?? '')}`);
|
|
3029
|
-
for (const msg of messages) {
|
|
3030
|
-
client._logMessageDebug('pull-raw', 'group.v2.pull', 'group.message_created', msg);
|
|
3031
|
-
}
|
|
3032
|
-
const seqs = messages
|
|
3033
|
-
.map((msg) => Number(msg.seq ?? 0))
|
|
3034
|
-
.filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
3035
|
-
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
3036
|
-
const namespaceGeneration = client._delivery.captureSeqNamespaceGeneration?.(ns);
|
|
3037
|
-
let pageTrackerSnapshot = null;
|
|
3038
|
-
let trackerMutated = false;
|
|
3039
|
-
let pageMaxSeq = nextAfterSeq;
|
|
3040
|
-
if (seqs.length > 0) {
|
|
3041
|
-
pageMaxSeq = Math.max(...seqs);
|
|
3042
|
-
}
|
|
3043
|
-
const pageContiguousSeq = contiguousPullSeq(pageContigBefore, seqs, result.contiguous_seq);
|
|
3044
|
-
if (!pipelineRunning && ownsCursor && shouldContinueForwardPage({
|
|
3045
|
-
allowEmptyCoverage: true,
|
|
3046
|
-
rawCount: messages.length, nextAfterSeq, nextAfter: pageMaxSeq, limit,
|
|
3047
|
-
hasMoreField, hasMore, serverHead,
|
|
3048
|
-
observedUpper: Math.max(client._seqTracker.getMaxSeenSeq(ns), Number(client._delivery.pendingPullUpper?.(ns) ?? 0)),
|
|
3049
|
-
pendingAckSeq: 0,
|
|
3050
|
-
}))
|
|
3051
|
-
startPipeline(pageMaxSeq);
|
|
3052
|
-
const deferredKeyFetches = new Map();
|
|
3053
|
-
let blockedSeq = 0;
|
|
3054
|
-
const pageDecryptedBefore = decrypted.length;
|
|
3055
|
-
const hasServerCursor = cursor !== null && Object.prototype.hasOwnProperty.call(cursor, 'current_seq');
|
|
3056
|
-
const parsedCursorCurrentSeq = hasServerCursor ? safeNonNegativeSeq(cursor?.current_seq) : null;
|
|
3057
|
-
const cursorCurrentSeq = parsedCursorCurrentSeq ?? 0;
|
|
3058
|
-
const retentionFloor = Math.max(client._pullRetentionFloor(result, 'retention_floor_message_seq', 'retention_floor_message_seq'));
|
|
3059
|
-
const visibilityFloor = Math.max(0, Math.max(0, Number(result.earliest_available_message_seq ?? 1) - 1), Number(cursor?.join_seq ?? 0));
|
|
3060
|
-
const effectiveFloor = Math.max(retentionFloor, visibilityFloor);
|
|
3061
|
-
let commitTarget = pageContigBefore;
|
|
3062
|
-
await this.withApplicationPage(ns, 'pull', async () => {
|
|
3123
|
+
const resultGroupAid = String(result.group_aid ?? result.groupAid ?? '').trim();
|
|
3124
|
+
if (resultGroupAid)
|
|
3125
|
+
groupAid = resultGroupAid;
|
|
3126
|
+
const eventGroupAid = resultGroupAid || groupAid || gid;
|
|
3127
|
+
const pageMessages = (Array.isArray(result.messages) ? result.messages : []);
|
|
3128
|
+
const messages = deduplicateForwardPageMessages(pageMessages, nextAfterSeq);
|
|
3129
|
+
const cursor = isJsonObject(result.cursor) ? result.cursor : null;
|
|
3130
|
+
const hasMoreField = Object.prototype.hasOwnProperty.call(result, 'has_more');
|
|
3131
|
+
const hasMore = result.has_more === true;
|
|
3132
|
+
const serverHead = Math.max(safeNonNegativeSeq(result.latest_message_seq) ?? 0, safeNonNegativeSeq(cursor?.latest_seq) ?? 0);
|
|
3133
|
+
client._clientLog.debug(`group.v2.pull page response: group=${gid}, page=${pageCount}, raw_count=${messages.length}, stale_count=${Math.max(0, pageMessages.length - messages.length)}, has_more=${String(result.has_more ?? '')}, server_head=${serverHead}, cursor_current=${String(cursor?.current_seq ?? '')}`);
|
|
3063
3134
|
for (const msg of messages) {
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
|
|
3078
|
-
|
|
3079
|
-
|
|
3080
|
-
|
|
3081
|
-
|
|
3082
|
-
|
|
3135
|
+
client._logMessageDebug('pull-raw', 'group.v2.pull', 'group.message_created', msg);
|
|
3136
|
+
}
|
|
3137
|
+
const seqs = messages
|
|
3138
|
+
.map((msg) => Number(msg.seq ?? 0))
|
|
3139
|
+
.filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
3140
|
+
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
3141
|
+
const namespaceGeneration = client._delivery.captureSeqNamespaceGeneration?.(ns);
|
|
3142
|
+
let pageTrackerSnapshot = null;
|
|
3143
|
+
let trackerMutated = false;
|
|
3144
|
+
let pageMaxSeq = nextAfterSeq;
|
|
3145
|
+
if (seqs.length > 0) {
|
|
3146
|
+
pageMaxSeq = Math.max(...seqs);
|
|
3147
|
+
}
|
|
3148
|
+
const pageContiguousSeq = contiguousPullSeq(pageContigBefore, seqs, result.contiguous_seq);
|
|
3149
|
+
if (!pipelineRunning && ownsCursor && shouldContinueForwardPage({
|
|
3150
|
+
allowEmptyCoverage: true,
|
|
3151
|
+
rawCount: messages.length, nextAfterSeq, nextAfter: pageMaxSeq, limit,
|
|
3152
|
+
hasMoreField, hasMore, serverHead,
|
|
3153
|
+
observedUpper: Math.max(client._seqTracker.getMaxSeenSeq(ns), Number(client._delivery.pendingPullUpper?.(ns) ?? 0)),
|
|
3154
|
+
pendingAckSeq: 0,
|
|
3155
|
+
}))
|
|
3156
|
+
startPipeline(pageMaxSeq);
|
|
3157
|
+
const deferredKeyFetches = new Map();
|
|
3158
|
+
let blockedSeq = 0;
|
|
3159
|
+
const pageDecryptedBefore = decryptedCount;
|
|
3160
|
+
const hasServerCursor = cursor !== null && Object.prototype.hasOwnProperty.call(cursor, 'current_seq');
|
|
3161
|
+
const parsedCursorCurrentSeq = hasServerCursor ? safeNonNegativeSeq(cursor?.current_seq) : null;
|
|
3162
|
+
const cursorCurrentSeq = parsedCursorCurrentSeq ?? 0;
|
|
3163
|
+
const retentionFloor = Math.max(client._pullRetentionFloor(result, 'retention_floor_message_seq', 'retention_floor_message_seq'));
|
|
3164
|
+
const visibilityFloor = Math.max(0, Math.max(0, Number(result.earliest_available_message_seq ?? 1) - 1), Number(cursor?.join_seq ?? 0));
|
|
3165
|
+
const effectiveFloor = Math.max(retentionFloor, visibilityFloor);
|
|
3166
|
+
let commitTarget = pageContigBefore;
|
|
3167
|
+
await this.withApplicationPage(ns, 'pull', async () => {
|
|
3168
|
+
for (const msg of messages) {
|
|
3169
|
+
const seq = Number(msg.seq ?? 0);
|
|
3170
|
+
if (!Number.isFinite(seq) || seq <= 0)
|
|
3083
3171
|
continue;
|
|
3084
|
-
|
|
3085
|
-
if (
|
|
3086
|
-
|
|
3172
|
+
const version = String(msg.version ?? 'v2');
|
|
3173
|
+
if (version === 'v1') {
|
|
3174
|
+
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
3175
|
+
const payload = msg.payload !== undefined ? msg.payload : legacy.payload;
|
|
3176
|
+
const payloadObj = isJsonObject(payload) ? payload : null;
|
|
3177
|
+
const payloadType = payloadObj ? String(payloadObj.type ?? '').trim() : '';
|
|
3178
|
+
// 群撤回 tombstone(占位 / 通知):归一化为 group.message_recalled 事件,仍占 seq。
|
|
3179
|
+
if (typeof client._delivery?.recallEventFromGroupMessage === 'function'
|
|
3180
|
+
&& client._delivery.recallEventFromGroupMessage(msg)) {
|
|
3181
|
+
await client._delivery.publishGroupRecallTombstone(gid, seq, {
|
|
3182
|
+
...msg,
|
|
3183
|
+
group_id: gid,
|
|
3184
|
+
group_aid: eventGroupAid,
|
|
3185
|
+
}, 'pull');
|
|
3186
|
+
client._markPublishedSeq(ns, seq);
|
|
3187
|
+
client._clientLog.debug(`group.v2.pull recall tombstone delivered: group=${gid}, seq=${seq}`);
|
|
3188
|
+
continue;
|
|
3189
|
+
}
|
|
3190
|
+
if (payloadObj) {
|
|
3191
|
+
if (!['e2ee.encrypted', 'e2ee.group_encrypted', 'e2ee.p2p_encrypted'].includes(payloadType)) {
|
|
3192
|
+
let v1Msg = {
|
|
3193
|
+
message_id: String(msg.message_id ?? ''),
|
|
3194
|
+
from: String(msg.from_aid ?? ''),
|
|
3195
|
+
group_id: gid,
|
|
3196
|
+
group_aid: eventGroupAid,
|
|
3197
|
+
seq: msg.seq,
|
|
3198
|
+
type: String(msg.type ?? ''),
|
|
3199
|
+
timestamp: msg.t_server,
|
|
3200
|
+
payload,
|
|
3201
|
+
encrypted: false,
|
|
3202
|
+
need_render: msg.need_render !== false,
|
|
3203
|
+
};
|
|
3204
|
+
if (msg.sender_device_id !== undefined)
|
|
3205
|
+
v1Msg.sender_device_id = msg.sender_device_id;
|
|
3206
|
+
v1Msg = normalizeGroupMentionMode(v1Msg, msg, msg.envelope_json);
|
|
3207
|
+
attachGatewayProximity(v1Msg, msg);
|
|
3208
|
+
if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
|
|
3209
|
+
await client._publishPulledMessage('group.message_created', ns, seq, v1Msg, false);
|
|
3210
|
+
}
|
|
3211
|
+
else {
|
|
3212
|
+
client._markPublishedSeq(ns, seq);
|
|
3213
|
+
}
|
|
3214
|
+
collectMessage(v1Msg);
|
|
3215
|
+
client._clientLog.debug(`group.v2.pull plaintext V1 delivered: group=${gid}, seq=${seq}`);
|
|
3216
|
+
continue;
|
|
3217
|
+
}
|
|
3218
|
+
}
|
|
3219
|
+
else if (payload !== undefined && payload !== null) {
|
|
3087
3220
|
let v1Msg = {
|
|
3088
3221
|
message_id: String(msg.message_id ?? ''),
|
|
3089
3222
|
from: String(msg.from_aid ?? ''),
|
|
@@ -3100,231 +3233,211 @@ export class V2E2EECoordinator {
|
|
|
3100
3233
|
v1Msg.sender_device_id = msg.sender_device_id;
|
|
3101
3234
|
v1Msg = normalizeGroupMentionMode(v1Msg, msg, msg.envelope_json);
|
|
3102
3235
|
attachGatewayProximity(v1Msg, msg);
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
}
|
|
3106
|
-
else {
|
|
3107
|
-
client._markPublishedSeq(ns, seq);
|
|
3108
|
-
}
|
|
3109
|
-
decrypted.push(v1Msg);
|
|
3236
|
+
await client._publishPulledMessage('group.message_created', ns, seq, v1Msg, false);
|
|
3237
|
+
collectMessage(v1Msg);
|
|
3110
3238
|
client._clientLog.debug(`group.v2.pull plaintext V1 delivered: group=${gid}, seq=${seq}`);
|
|
3111
3239
|
continue;
|
|
3112
3240
|
}
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
timestamp: msg.t_server,
|
|
3123
|
-
payload,
|
|
3124
|
-
encrypted: false,
|
|
3125
|
-
need_render: msg.need_render !== false,
|
|
3126
|
-
};
|
|
3127
|
-
if (msg.sender_device_id !== undefined)
|
|
3128
|
-
v1Msg.sender_device_id = msg.sender_device_id;
|
|
3129
|
-
v1Msg = normalizeGroupMentionMode(v1Msg, msg, msg.envelope_json);
|
|
3130
|
-
attachGatewayProximity(v1Msg, msg);
|
|
3131
|
-
await client._publishPulledMessage('group.message_created', ns, seq, v1Msg, false);
|
|
3132
|
-
decrypted.push(v1Msg);
|
|
3133
|
-
client._clientLog.debug(`group.v2.pull plaintext V1 delivered: group=${gid}, seq=${seq}`);
|
|
3134
|
-
continue;
|
|
3135
|
-
}
|
|
3136
|
-
if (payloadObj && ['e2ee.group_encrypted', 'e2ee.p2p_encrypted', 'e2ee.encrypted'].includes(payloadType)) {
|
|
3137
|
-
const deferStatus = {};
|
|
3138
|
-
const plaintext = await client._decryptV2Message({ ...msg, envelope_json: JSON.stringify(payloadObj) }, true, true, true, true, deferStatus, true, 'pull');
|
|
3139
|
-
if (deferStatus.deferred) {
|
|
3140
|
-
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
3141
|
-
if (deferStatus.fromAid) {
|
|
3142
|
-
const key = `${deferStatus.fromAid}\u0000${deferStatus.senderDeviceId ?? ''}\u0000${deferStatus.groupId ?? ''}`;
|
|
3143
|
-
deferredKeyFetches.set(key, { fromAid: deferStatus.fromAid, senderDeviceId: deferStatus.senderDeviceId ?? '', groupId: deferStatus.groupId ?? '' });
|
|
3144
|
-
}
|
|
3145
|
-
}
|
|
3146
|
-
if (plaintext) {
|
|
3147
|
-
plaintext.group_id = gid;
|
|
3148
|
-
plaintext.group_aid = eventGroupAid;
|
|
3149
|
-
plaintext.need_render = typeof msg.need_render === 'boolean' ? msg.need_render : plaintext.need_render !== false;
|
|
3150
|
-
if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
|
|
3151
|
-
await client._publishPulledMessage('group.message_created', ns, seq, plaintext, false);
|
|
3241
|
+
if (payloadObj && ['e2ee.group_encrypted', 'e2ee.p2p_encrypted', 'e2ee.encrypted'].includes(payloadType)) {
|
|
3242
|
+
const deferStatus = {};
|
|
3243
|
+
const plaintext = await client._decryptV2Message({ ...msg, envelope_json: JSON.stringify(payloadObj) }, true, true, true, true, deferStatus, true, 'pull');
|
|
3244
|
+
if (deferStatus.deferred) {
|
|
3245
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
3246
|
+
if (deferStatus.fromAid) {
|
|
3247
|
+
const key = `${deferStatus.fromAid}\u0000${deferStatus.senderDeviceId ?? ''}\u0000${deferStatus.groupId ?? ''}`;
|
|
3248
|
+
deferredKeyFetches.set(key, { fromAid: deferStatus.fromAid, senderDeviceId: deferStatus.senderDeviceId ?? '', groupId: deferStatus.groupId ?? '' });
|
|
3249
|
+
}
|
|
3152
3250
|
}
|
|
3153
|
-
|
|
3154
|
-
|
|
3251
|
+
if (plaintext) {
|
|
3252
|
+
plaintext.group_id = gid;
|
|
3253
|
+
plaintext.group_aid = eventGroupAid;
|
|
3254
|
+
plaintext.need_render = typeof msg.need_render === 'boolean' ? msg.need_render : plaintext.need_render !== false;
|
|
3255
|
+
if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
|
|
3256
|
+
await client._publishPulledMessage('group.message_created', ns, seq, plaintext, false);
|
|
3257
|
+
}
|
|
3258
|
+
else {
|
|
3259
|
+
client._markPublishedSeq(ns, seq);
|
|
3260
|
+
}
|
|
3261
|
+
collectMessage(plaintext);
|
|
3155
3262
|
}
|
|
3156
|
-
decrypted.push(plaintext);
|
|
3157
3263
|
}
|
|
3264
|
+
else {
|
|
3265
|
+
client._clientLog.debug(`group.v2.pull skipping V1 envelope group=${gid} seq=${seq} payload_type=${payloadObj ? String(payloadObj.type ?? '') : '<none>'} (V1 E2EE removed)`);
|
|
3266
|
+
}
|
|
3267
|
+
continue;
|
|
3158
3268
|
}
|
|
3159
|
-
|
|
3160
|
-
client._clientLog.debug(`group.v2.pull skipping
|
|
3269
|
+
if (version !== 'v2') {
|
|
3270
|
+
client._clientLog.debug(`group.v2.pull skipping non-V2 row group=${gid} seq=${seq} version=${String(msg.version ?? '')}`);
|
|
3271
|
+
continue;
|
|
3161
3272
|
}
|
|
3162
|
-
|
|
3163
|
-
|
|
3164
|
-
|
|
3165
|
-
|
|
3166
|
-
continue;
|
|
3167
|
-
}
|
|
3168
|
-
const deferStatus = {};
|
|
3169
|
-
let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, 'pull');
|
|
3170
|
-
if (deferStatus.deferred) {
|
|
3171
|
-
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
3172
|
-
}
|
|
3173
|
-
if (deferStatus.deferred && deferStatus.fromAid) {
|
|
3174
|
-
const key = `${deferStatus.fromAid}\u0000${deferStatus.senderDeviceId ?? ''}\u0000${deferStatus.groupId ?? ''}`;
|
|
3175
|
-
deferredKeyFetches.set(key, {
|
|
3176
|
-
fromAid: deferStatus.fromAid,
|
|
3177
|
-
senderDeviceId: deferStatus.senderDeviceId ?? '',
|
|
3178
|
-
groupId: deferStatus.groupId ?? '',
|
|
3179
|
-
});
|
|
3180
|
-
}
|
|
3181
|
-
if (plaintext === null) {
|
|
3182
|
-
client._clientLog.debug(`group.v2.pull decrypt returned null: group=${gid}, seq=${seq}`);
|
|
3183
|
-
continue;
|
|
3184
|
-
}
|
|
3185
|
-
plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
|
|
3186
|
-
plaintext.group_id = gid;
|
|
3187
|
-
plaintext.group_aid = eventGroupAid;
|
|
3188
|
-
plaintext.need_render = typeof msg.need_render === 'boolean' ? msg.need_render : plaintext.need_render !== false;
|
|
3189
|
-
if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
|
|
3190
|
-
await client._publishPulledMessage('group.message_created', ns, seq, plaintext, false);
|
|
3191
|
-
}
|
|
3192
|
-
else {
|
|
3193
|
-
client._markPublishedSeq(ns, seq);
|
|
3194
|
-
}
|
|
3195
|
-
decrypted.push(plaintext);
|
|
3196
|
-
client._logMessageDebug('decrypt-ok', 'group.v2.pull', 'group.message_created', plaintext);
|
|
3197
|
-
}
|
|
3198
|
-
commitTarget = Math.max(pageContigBefore, effectiveFloor, ownsCursor && pageCount === 1 ? deferredServerCursor : 0, ownsCursor ? cursorCurrentSeq : 0, blockedSeq > 0 ? blockedSeq - 1 : pageContiguousSeq);
|
|
3199
|
-
if (ownsCursor) {
|
|
3200
|
-
this.ensureDeliveryCurrent(ns, generation);
|
|
3201
|
-
const window = client._seqTracker.getSyncState?.(ns);
|
|
3202
|
-
const deliveryTarget = window && commitTarget >= window.tail - 1
|
|
3203
|
-
? Math.max(commitTarget, window.head) : commitTarget;
|
|
3204
|
-
if (deliveryTarget > pageContigBefore) {
|
|
3205
|
-
await client._drainOrderedMessages(ns, undefined, false, false, 'pull', deliveryTarget);
|
|
3206
|
-
}
|
|
3207
|
-
this.ensureDeliveryCurrent(ns, generation);
|
|
3208
|
-
}
|
|
3209
|
-
});
|
|
3210
|
-
let ackSeq = pageContigBefore;
|
|
3211
|
-
let contigAdvanced = false;
|
|
3212
|
-
if (ownsCursor) {
|
|
3213
|
-
try {
|
|
3214
|
-
this.ensureDeliveryCurrent(ns, generation);
|
|
3215
|
-
if (client._delivery.isSeqNamespaceGenerationCurrent?.(ns, namespaceGeneration) === false)
|
|
3216
|
-
throw new Error('pull invalidated');
|
|
3217
|
-
client._delivery.rememberCommittedSeqState?.(ns);
|
|
3218
|
-
pageTrackerSnapshot = client._seqTracker.snapshotNamespace?.(ns) ?? null;
|
|
3219
|
-
if (commitTarget > pageContigBefore) {
|
|
3220
|
-
trackerMutated = true;
|
|
3221
|
-
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
3222
|
-
if (effectiveFloor > pageContigBefore) {
|
|
3223
|
-
client._clientLog.info(`group.v2.pull floor advance: ns=${ns} contiguous=${pageContigBefore} -> effective_floor=${effectiveFloor} retention_floor=${retentionFloor} visibility_floor=${visibilityFloor}`);
|
|
3273
|
+
const deferStatus = {};
|
|
3274
|
+
let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, 'pull');
|
|
3275
|
+
if (deferStatus.deferred) {
|
|
3276
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq) : seq;
|
|
3224
3277
|
}
|
|
3225
|
-
if (
|
|
3226
|
-
|
|
3278
|
+
if (deferStatus.deferred && deferStatus.fromAid) {
|
|
3279
|
+
const key = `${deferStatus.fromAid}\u0000${deferStatus.senderDeviceId ?? ''}\u0000${deferStatus.groupId ?? ''}`;
|
|
3280
|
+
deferredKeyFetches.set(key, {
|
|
3281
|
+
fromAid: deferStatus.fromAid,
|
|
3282
|
+
senderDeviceId: deferStatus.senderDeviceId ?? '',
|
|
3283
|
+
groupId: deferStatus.groupId ?? '',
|
|
3284
|
+
});
|
|
3227
3285
|
}
|
|
3228
|
-
if (
|
|
3229
|
-
client._clientLog.debug(`group.v2.pull
|
|
3286
|
+
if (plaintext === null) {
|
|
3287
|
+
client._clientLog.debug(`group.v2.pull decrypt returned null: group=${gid}, seq=${seq}`);
|
|
3288
|
+
continue;
|
|
3289
|
+
}
|
|
3290
|
+
plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
|
|
3291
|
+
plaintext.group_id = gid;
|
|
3292
|
+
plaintext.group_aid = eventGroupAid;
|
|
3293
|
+
plaintext.need_render = typeof msg.need_render === 'boolean' ? msg.need_render : plaintext.need_render !== false;
|
|
3294
|
+
if (!client._delivery.isSelfSentGroupMessage?.(msg)) {
|
|
3295
|
+
await client._publishPulledMessage('group.message_created', ns, seq, plaintext, false);
|
|
3230
3296
|
}
|
|
3297
|
+
else {
|
|
3298
|
+
client._markPublishedSeq(ns, seq);
|
|
3299
|
+
}
|
|
3300
|
+
collectMessage(plaintext);
|
|
3301
|
+
client._logMessageDebug('decrypt-ok', 'group.v2.pull', 'group.message_created', plaintext);
|
|
3231
3302
|
}
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3303
|
+
commitTarget = Math.max(pageContigBefore, effectiveFloor, ownsCursor && pageCount === 1 ? deferredServerCursor : 0, ownsCursor ? cursorCurrentSeq : 0, blockedSeq > 0 ? blockedSeq - 1 : pageContiguousSeq);
|
|
3304
|
+
if (ownsCursor) {
|
|
3305
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
3306
|
+
const window = client._seqTracker.getSyncState?.(ns);
|
|
3307
|
+
const deliveryTarget = window && commitTarget >= window.tail - 1
|
|
3308
|
+
? Math.max(commitTarget, window.head) : commitTarget;
|
|
3309
|
+
if (deliveryTarget > pageContigBefore) {
|
|
3310
|
+
await client._drainOrderedMessages(ns, undefined, false, false, 'pull', deliveryTarget);
|
|
3311
|
+
}
|
|
3312
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
3313
|
+
}
|
|
3314
|
+
});
|
|
3315
|
+
let ackSeq = pageContigBefore;
|
|
3316
|
+
let contigAdvanced = false;
|
|
3317
|
+
if (ownsCursor) {
|
|
3318
|
+
try {
|
|
3236
3319
|
this.ensureDeliveryCurrent(ns, generation);
|
|
3237
3320
|
if (client._delivery.isSeqNamespaceGenerationCurrent?.(ns, namespaceGeneration) === false)
|
|
3238
3321
|
throw new Error('pull invalidated');
|
|
3239
|
-
|
|
3240
|
-
|
|
3322
|
+
client._delivery.rememberCommittedSeqState?.(ns);
|
|
3323
|
+
pageTrackerSnapshot = client._seqTracker.snapshotNamespace?.(ns) ?? null;
|
|
3324
|
+
if (commitTarget > pageContigBefore) {
|
|
3325
|
+
trackerMutated = true;
|
|
3326
|
+
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
3327
|
+
if (effectiveFloor > pageContigBefore) {
|
|
3328
|
+
client._clientLog.info(`group.v2.pull floor advance: ns=${ns} contiguous=${pageContigBefore} -> effective_floor=${effectiveFloor} retention_floor=${retentionFloor} visibility_floor=${visibilityFloor}`);
|
|
3329
|
+
}
|
|
3330
|
+
if (deferredServerCursor > pageContigBefore && pageCount === 1) {
|
|
3331
|
+
client._clientLog.info(`group.v2.pull restore local A from deferred server ACK cursor: group=${gid}, ns=${ns}, contiguous=${pageContigBefore} -> current_seq=${deferredServerCursor}`);
|
|
3332
|
+
}
|
|
3333
|
+
if (seqs.length > 0) {
|
|
3334
|
+
client._clientLog.debug(`group.v2.pull force contiguous: group=${gid}, ns=${ns}, page_max_seq=${pageMaxSeq}, previous=${pageContigBefore}`);
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
ackSeq = client._seqTracker.getContiguousSeq(ns);
|
|
3338
|
+
contigAdvanced = ackSeq !== pageContigBefore;
|
|
3339
|
+
if (contigAdvanced) {
|
|
3340
|
+
await client._commitSeqTrackerState(ns);
|
|
3341
|
+
this.ensureDeliveryCurrent(ns, generation);
|
|
3342
|
+
if (client._delivery.isSeqNamespaceGenerationCurrent?.(ns, namespaceGeneration) === false)
|
|
3343
|
+
throw new Error('pull invalidated');
|
|
3344
|
+
if (pageCount === 1 && deferredServerCursor > 0)
|
|
3345
|
+
this.clearForwardCursor(ns, ackSeq);
|
|
3346
|
+
}
|
|
3347
|
+
committedAckSeq = ackSeq;
|
|
3241
3348
|
}
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3349
|
+
catch (exc) {
|
|
3350
|
+
if (trackerMutated && Number(client._v2RuntimeGeneration ?? 0) === generation
|
|
3351
|
+
&& client._delivery.isSeqNamespaceGenerationCurrent?.(ns, namespaceGeneration) !== false
|
|
3352
|
+
&& client._rpcPipeline?.isPullNamespaceCurrent?.(ns) !== false
|
|
3353
|
+
&& typeof client._seqTracker.restoreNamespaceSnapshot === 'function') {
|
|
3354
|
+
(client._seqTracker._restoreNamespaceSnapshotPreservingPush ?? client._seqTracker.restoreNamespaceSnapshot)
|
|
3355
|
+
.call(client._seqTracker, ns, pageTrackerSnapshot);
|
|
3356
|
+
}
|
|
3357
|
+
client._clientLog.error?.(`group.v2.pull Forward commit rolled back: group=${gid} ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
|
|
3358
|
+
throw exc;
|
|
3251
3359
|
}
|
|
3252
|
-
client._clientLog.error?.(`group.v2.pull Forward commit rolled back: group=${gid} ns=${ns} target=${commitTarget} error=${formatE2EEError(exc)}`);
|
|
3253
|
-
throw exc;
|
|
3254
3360
|
}
|
|
3361
|
+
for (const fetch of deferredKeyFetches.values()) {
|
|
3362
|
+
this.scheduleSenderIKFetch(fetch.fromAid, fetch.senderDeviceId, fetch.groupId);
|
|
3363
|
+
}
|
|
3364
|
+
if (ownsCursor && parsedCursorCurrentSeq !== null && cursorCurrentSeq > ackSeq) {
|
|
3365
|
+
this.recordForwardCursor(ns, cursorCurrentSeq, ackSeq);
|
|
3366
|
+
}
|
|
3367
|
+
// 无本页 cursor.current_seq 时,只有本页成功提交了比已知 cursor
|
|
3368
|
+
// 更高的 A 才新增 ACK 义务;空页不得因已有本地 A 重复确认。
|
|
3369
|
+
const knownServerCursorSeq = hasServerCursor
|
|
3370
|
+
? cursorCurrentSeq
|
|
3371
|
+
: (ownsCursor && pageCount === 1 ? deferredServerCursor : 0);
|
|
3372
|
+
const ackNeeded = ackSeq > 0
|
|
3373
|
+
&& ackSeq > lastAutoAckSeq
|
|
3374
|
+
&& ownsCursor
|
|
3375
|
+
&& ((hasServerCursor && ackSeq > cursorCurrentSeq)
|
|
3376
|
+
|| (contigAdvanced && ackSeq > knownServerCursorSeq));
|
|
3377
|
+
if (ackNeeded) {
|
|
3378
|
+
this.recordForwardAck(ns, ackSeq);
|
|
3379
|
+
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
3380
|
+
client._clientLog.debug(`group.v2.pull queued piggyback auto-ack: group=${gid}, ns=${ns}, ack_seq=${pendingAckSeq}, raw_count=${messages.length}`);
|
|
3381
|
+
}
|
|
3382
|
+
client._delivery.syncProgress(ns, {
|
|
3383
|
+
rawCount: messages.length,
|
|
3384
|
+
pulledCount: decryptedCount - pageDecryptedBefore,
|
|
3385
|
+
remaining: result.remaining,
|
|
3386
|
+
hasMore: hasMoreField ? hasMore : undefined,
|
|
3387
|
+
currentSeq: client._seqTracker.getContiguousSeq(ns),
|
|
3388
|
+
targetSeq: serverHead,
|
|
3389
|
+
});
|
|
3390
|
+
const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
3391
|
+
if (!ownsCursor)
|
|
3392
|
+
break;
|
|
3393
|
+
const rawCount = messages.length;
|
|
3394
|
+
if (blockedSeq > 0)
|
|
3395
|
+
break;
|
|
3396
|
+
const shouldContinue = shouldContinueForwardPage({
|
|
3397
|
+
allowEmptyCoverage: true,
|
|
3398
|
+
rawCount,
|
|
3399
|
+
nextAfterSeq,
|
|
3400
|
+
nextAfter,
|
|
3401
|
+
limit,
|
|
3402
|
+
hasMoreField,
|
|
3403
|
+
hasMore,
|
|
3404
|
+
serverHead,
|
|
3405
|
+
observedUpper: Math.max(client._seqTracker.getMaxSeenSeq(ns), Number(client._delivery.pendingPullUpper?.(ns) ?? 0)),
|
|
3406
|
+
pendingAckSeq,
|
|
3407
|
+
});
|
|
3408
|
+
const shouldContinueForPipeline = shouldContinueForwardPage({
|
|
3409
|
+
allowEmptyCoverage: true,
|
|
3410
|
+
rawCount,
|
|
3411
|
+
nextAfterSeq,
|
|
3412
|
+
nextAfter,
|
|
3413
|
+
limit,
|
|
3414
|
+
hasMoreField,
|
|
3415
|
+
hasMore,
|
|
3416
|
+
serverHead,
|
|
3417
|
+
observedUpper: Math.max(client._seqTracker.getMaxSeenSeq(ns), Number(client._delivery.pendingPullUpper?.(ns) ?? 0)),
|
|
3418
|
+
pendingAckSeq: 0,
|
|
3419
|
+
});
|
|
3420
|
+
if (!shouldContinue)
|
|
3421
|
+
break;
|
|
3422
|
+
if (shouldContinueForPipeline && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
|
|
3423
|
+
startPipeline(nextAfter);
|
|
3424
|
+
}
|
|
3425
|
+
const tailDelayMs = pipelineRunning ? 0 : v2PullTailDelayMs(rawCount, limit);
|
|
3426
|
+
if (tailDelayMs > 0)
|
|
3427
|
+
await v2Sleep(tailDelayMs);
|
|
3428
|
+
nextAfterSeq = nextAfter;
|
|
3255
3429
|
}
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
if (ownsCursor && parsedCursorCurrentSeq !== null && cursorCurrentSeq > ackSeq) {
|
|
3260
|
-
this.recordForwardCursor(ns, cursorCurrentSeq, ackSeq);
|
|
3261
|
-
}
|
|
3262
|
-
// 无本页 cursor.current_seq 时,只有本页成功提交了比已知 cursor
|
|
3263
|
-
// 更高的 A 才新增 ACK 义务;空页不得因已有本地 A 重复确认。
|
|
3264
|
-
const knownServerCursorSeq = hasServerCursor
|
|
3265
|
-
? cursorCurrentSeq
|
|
3266
|
-
: (ownsCursor && pageCount === 1 ? deferredServerCursor : 0);
|
|
3267
|
-
const ackNeeded = ackSeq > 0
|
|
3268
|
-
&& ackSeq > lastAutoAckSeq
|
|
3269
|
-
&& ownsCursor
|
|
3270
|
-
&& ((hasServerCursor && ackSeq > cursorCurrentSeq)
|
|
3271
|
-
|| (contigAdvanced && ackSeq > knownServerCursorSeq));
|
|
3272
|
-
if (ackNeeded) {
|
|
3273
|
-
this.recordForwardAck(ns, ackSeq);
|
|
3274
|
-
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
3275
|
-
client._clientLog.debug(`group.v2.pull queued piggyback auto-ack: group=${gid}, ns=${ns}, ack_seq=${pendingAckSeq}, raw_count=${messages.length}`);
|
|
3276
|
-
}
|
|
3277
|
-
client._delivery.syncProgress(ns, {
|
|
3278
|
-
rawCount: messages.length,
|
|
3279
|
-
pulledCount: decrypted.length - pageDecryptedBefore,
|
|
3280
|
-
remaining: result.remaining,
|
|
3281
|
-
hasMore: hasMoreField ? hasMore : undefined,
|
|
3282
|
-
currentSeq: client._seqTracker.getContiguousSeq(ns),
|
|
3283
|
-
targetSeq: serverHead,
|
|
3284
|
-
});
|
|
3285
|
-
const nextAfter = Math.max(pageContiguousSeq, nextAfterSeq);
|
|
3286
|
-
if (!ownsCursor)
|
|
3287
|
-
break;
|
|
3288
|
-
const rawCount = messages.length;
|
|
3289
|
-
if (blockedSeq > 0)
|
|
3290
|
-
break;
|
|
3291
|
-
const shouldContinue = shouldContinueForwardPage({
|
|
3292
|
-
allowEmptyCoverage: true,
|
|
3293
|
-
rawCount,
|
|
3294
|
-
nextAfterSeq,
|
|
3295
|
-
nextAfter,
|
|
3296
|
-
limit,
|
|
3297
|
-
hasMoreField,
|
|
3298
|
-
hasMore,
|
|
3299
|
-
serverHead,
|
|
3300
|
-
observedUpper: Math.max(client._seqTracker.getMaxSeenSeq(ns), Number(client._delivery.pendingPullUpper?.(ns) ?? 0)),
|
|
3301
|
-
pendingAckSeq,
|
|
3302
|
-
});
|
|
3303
|
-
const shouldContinueForPipeline = shouldContinueForwardPage({
|
|
3304
|
-
allowEmptyCoverage: true,
|
|
3305
|
-
rawCount,
|
|
3306
|
-
nextAfterSeq,
|
|
3307
|
-
nextAfter,
|
|
3308
|
-
limit,
|
|
3309
|
-
hasMoreField,
|
|
3310
|
-
hasMore,
|
|
3311
|
-
serverHead,
|
|
3312
|
-
observedUpper: Math.max(client._seqTracker.getMaxSeenSeq(ns), Number(client._delivery.pendingPullUpper?.(ns) ?? 0)),
|
|
3313
|
-
pendingAckSeq: 0,
|
|
3314
|
-
});
|
|
3315
|
-
if (!shouldContinue)
|
|
3316
|
-
break;
|
|
3317
|
-
if (shouldContinueForPipeline && !pipelineRunning && limit >= V2_PULL_MAX_PAGE_LIMIT) {
|
|
3318
|
-
startPipeline(nextAfter);
|
|
3430
|
+
finally {
|
|
3431
|
+
client._syncChannelCoordinator?.releaseResponse(result);
|
|
3432
|
+
syncRetainedPages.delete(result);
|
|
3319
3433
|
}
|
|
3320
|
-
const tailDelayMs = pipelineRunning ? 0 : v2PullTailDelayMs(rawCount, limit);
|
|
3321
|
-
if (tailDelayMs > 0)
|
|
3322
|
-
await v2Sleep(tailDelayMs);
|
|
3323
|
-
nextAfterSeq = nextAfter;
|
|
3324
3434
|
}
|
|
3325
3435
|
}
|
|
3326
3436
|
finally {
|
|
3327
3437
|
await stopPipeline();
|
|
3438
|
+
for (const response of syncRetainedPages)
|
|
3439
|
+
client._syncChannelCoordinator?.releaseResponse(response);
|
|
3440
|
+
syncRetainedPages.clear();
|
|
3328
3441
|
}
|
|
3329
3442
|
if (pageCount >= maxPages) {
|
|
3330
3443
|
client._clientLog.warn(`group.v2.pull reached max_pages=${maxPages} group=${gid} after_seq=${nextAfterSeq}`);
|
|
@@ -3333,11 +3446,15 @@ export class V2E2EECoordinator {
|
|
|
3333
3446
|
this.ensureDeliveryCurrent(ns, generation);
|
|
3334
3447
|
client._clientLog.debug(`group.v2.pull flushing pending auto-ack: group=${gid}, ns=${ns}, ack_seq=${pendingAckSeq}`);
|
|
3335
3448
|
const ackToFlush = pendingAckSeq;
|
|
3336
|
-
|
|
3449
|
+
if (opts?.background === true)
|
|
3450
|
+
client._delivery.scheduleForwardAck(ns, ackToFlush, (seq) => this.confirmForwardGroupAck(gid, seq, groupAid, background));
|
|
3451
|
+
else
|
|
3452
|
+
await this.confirmForwardGroupAck(gid, ackToFlush, groupAid, background);
|
|
3337
3453
|
if (pendingAckSeq <= ackToFlush)
|
|
3338
3454
|
pendingAckSeq = 0;
|
|
3339
3455
|
}
|
|
3340
|
-
client._clientLog.debug(`group.v2.pull done: group=${gid}, requested_after_seq=${afterSeq}, pages=${pageCount}, decrypted=${
|
|
3456
|
+
client._clientLog.debug(`group.v2.pull done: group=${gid}, requested_after_seq=${afterSeq}, pages=${pageCount}, decrypted=${decryptedCount}, ns=${ns}`);
|
|
3457
|
+
opts?.onMessageCount?.(decryptedCount);
|
|
3341
3458
|
return decrypted;
|
|
3342
3459
|
}
|
|
3343
3460
|
async pullGroupV2(groupId, afterSeq = 0, limit = 50, opts) {
|