@agentunion/fastaun-browser 0.5.9 → 0.5.11
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 +64 -0
- package/_packed_docs/CHANGELOG.md +64 -0
- package/_packed_docs/INDEX.md +3 -3
- package/_packed_docs/KITE_DOCS_GUIDE.md +1 -1
- package/_packed_docs/sdk/04-/350/277/236/346/216/245/344/270/216/350/256/244/350/257/201.md +6 -5
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +102 -8
- package/_packed_docs/sdk/09-group-rpc-manual.md +18 -3
- package/_packed_docs/sdk/09-message-rpc-manual.md +34 -10
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +3 -2
- package/_packed_docs/sdk/INDEX.md +2 -1
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +23 -1
- package/dist/agent-md.js.map +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +16 -2
- package/dist/auth.js.map +1 -1
- package/dist/bundle.js +1374 -267
- package/dist/client/delivery.d.ts +34 -11
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +390 -100
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +6 -6
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.d.ts.map +1 -1
- package/dist/client/lifecycle.js +34 -38
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +4 -0
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +70 -4
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +4 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +169 -38
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +83 -33
- package/dist/client.js.map +1 -1
- package/dist/events.d.ts +31 -2
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +167 -7
- package/dist/events.js.map +1 -1
- package/dist/register-flow.d.ts.map +1 -1
- package/dist/register-flow.js +16 -2
- package/dist/register-flow.js.map +1 -1
- package/dist/transport.d.ts +10 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +427 -29
- package/dist/transport.js.map +1 -1
- 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 +1 -1
package/dist/bundle.js
CHANGED
|
@@ -460,7 +460,7 @@ var init_indexeddb_store = __esm({
|
|
|
460
460
|
});
|
|
461
461
|
|
|
462
462
|
// src/version.ts
|
|
463
|
-
var VERSION = "0.5.
|
|
463
|
+
var VERSION = "0.5.11";
|
|
464
464
|
|
|
465
465
|
// src/types.ts
|
|
466
466
|
var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
|
|
@@ -799,6 +799,15 @@ var EventDispatcher = class {
|
|
|
799
799
|
constructor() {
|
|
800
800
|
__publicField(this, "_log", _noopLog);
|
|
801
801
|
__publicField(this, "_handlers", /* @__PURE__ */ new Map());
|
|
802
|
+
__publicField(this, "_queue", []);
|
|
803
|
+
__publicField(this, "_draining", false);
|
|
804
|
+
__publicField(this, "_drainScheduled", false);
|
|
805
|
+
__publicField(this, "_handlerDepth", 0);
|
|
806
|
+
__publicField(this, "_synchronousHandlerDepth", 0);
|
|
807
|
+
__publicField(this, "_drainPromise", null);
|
|
808
|
+
__publicField(this, "_drainResolve", null);
|
|
809
|
+
__publicField(this, "_closing", false);
|
|
810
|
+
__publicField(this, "_closed", false);
|
|
802
811
|
}
|
|
803
812
|
setLogger(log) {
|
|
804
813
|
this._log = log;
|
|
@@ -827,17 +836,154 @@ var EventDispatcher = class {
|
|
|
827
836
|
this._handlers.delete(event);
|
|
828
837
|
}
|
|
829
838
|
}
|
|
830
|
-
/**
|
|
839
|
+
/**
|
|
840
|
+
* 发布事件。事件总是异步进入 FIFO 队列;调用方 await 时等待该事件处理完成。
|
|
841
|
+
* drain 中派生事件只入队,不等待自身,避免事件处理器互相等待形成死锁。
|
|
842
|
+
*/
|
|
831
843
|
async publish(event, payload) {
|
|
844
|
+
if (this._closed || this._closing) return;
|
|
845
|
+
let resolveItem;
|
|
846
|
+
const itemDone = new Promise((resolve) => {
|
|
847
|
+
resolveItem = resolve;
|
|
848
|
+
});
|
|
849
|
+
this._queue.push({
|
|
850
|
+
run: () => this.dispatchNow(event, payload),
|
|
851
|
+
resolve: resolveItem
|
|
852
|
+
});
|
|
853
|
+
if (this._handlerDepth > 0) return;
|
|
854
|
+
if (this._drainPromise === null) {
|
|
855
|
+
this._drainPromise = new Promise((resolve) => {
|
|
856
|
+
this._drainResolve = resolve;
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
this.scheduleDrain();
|
|
860
|
+
await itemDone;
|
|
861
|
+
}
|
|
862
|
+
/** 将事件放入异步 FIFO 队列,不等待处理器执行完成。 */
|
|
863
|
+
enqueue(event, payload) {
|
|
864
|
+
void this.publish(event, payload).catch((exc) => {
|
|
865
|
+
this._log.warn(`event ${event} enqueue failed:`, exc);
|
|
866
|
+
});
|
|
867
|
+
}
|
|
868
|
+
/** 将非事件 observer 放入同一 FIFO 队列。 */
|
|
869
|
+
enqueueTask(task) {
|
|
870
|
+
if (this._closed || this._closing) return;
|
|
871
|
+
this._queue.push({
|
|
872
|
+
run: () => this.dispatchTask(task),
|
|
873
|
+
resolve: () => {
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
if (this._drainPromise === null) {
|
|
877
|
+
this._drainPromise = new Promise((resolve) => {
|
|
878
|
+
this._drainResolve = resolve;
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
this.scheduleDrain();
|
|
882
|
+
}
|
|
883
|
+
/** 关闭调度器,排空已经入队的应用事件后拒绝后续事件。 */
|
|
884
|
+
async close() {
|
|
885
|
+
if (this._closed) return;
|
|
886
|
+
this._closing = true;
|
|
887
|
+
if (this._synchronousHandlerDepth > 0) return;
|
|
888
|
+
if (this._drainPromise !== null) await this._drainPromise;
|
|
889
|
+
this.finishClose();
|
|
890
|
+
}
|
|
891
|
+
/** 等待当前队列排空;事件 handler 重入时直接返回,避免自等待。 */
|
|
892
|
+
async flush() {
|
|
893
|
+
if (this._synchronousHandlerDepth > 0) return;
|
|
894
|
+
if (this._drainPromise !== null) await this._drainPromise;
|
|
895
|
+
await Promise.resolve();
|
|
896
|
+
}
|
|
897
|
+
finishClose() {
|
|
898
|
+
if (!this._closing || this._closed || this._draining || this._drainScheduled || this._queue.length > 0) return;
|
|
899
|
+
this._closed = true;
|
|
900
|
+
this._handlers.clear();
|
|
901
|
+
}
|
|
902
|
+
scheduleDrain() {
|
|
903
|
+
if (this._draining || this._drainScheduled || this._queue.length === 0) return;
|
|
904
|
+
this._drainScheduled = true;
|
|
905
|
+
queueMicrotask(() => {
|
|
906
|
+
this._drainScheduled = false;
|
|
907
|
+
void this._drain();
|
|
908
|
+
});
|
|
909
|
+
}
|
|
910
|
+
async _drain() {
|
|
911
|
+
if (this._draining) return;
|
|
912
|
+
this._draining = true;
|
|
913
|
+
try {
|
|
914
|
+
while (this._queue.length > 0) {
|
|
915
|
+
const item = this._queue.shift();
|
|
916
|
+
try {
|
|
917
|
+
await item.run();
|
|
918
|
+
} catch (exc) {
|
|
919
|
+
this._log.warn("event dispatch failed:", exc);
|
|
920
|
+
} finally {
|
|
921
|
+
item.resolve();
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
} finally {
|
|
925
|
+
this._draining = false;
|
|
926
|
+
const resolve = this._drainResolve;
|
|
927
|
+
this._drainResolve = null;
|
|
928
|
+
this._drainPromise = null;
|
|
929
|
+
resolve?.();
|
|
930
|
+
this.finishClose();
|
|
931
|
+
if (this._queue.length > 0 && !this._closed) {
|
|
932
|
+
if (this._drainPromise === null) {
|
|
933
|
+
this._drainPromise = new Promise((nextResolve) => {
|
|
934
|
+
this._drainResolve = nextResolve;
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
this.scheduleDrain();
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
/** 当前是否正在调用应用 handler 或 observer。供生命周期入口识别重入。 */
|
|
942
|
+
isDispatchingHandler() {
|
|
943
|
+
return this._handlerDepth > 0;
|
|
944
|
+
}
|
|
945
|
+
async dispatchTask(task) {
|
|
946
|
+
let result;
|
|
947
|
+
this._handlerDepth += 1;
|
|
948
|
+
try {
|
|
949
|
+
this._synchronousHandlerDepth += 1;
|
|
950
|
+
result = task();
|
|
951
|
+
} catch (exc) {
|
|
952
|
+
this._log.warn("event task execution exception:", exc);
|
|
953
|
+
this._handlerDepth -= 1;
|
|
954
|
+
return;
|
|
955
|
+
} finally {
|
|
956
|
+
this._synchronousHandlerDepth -= 1;
|
|
957
|
+
}
|
|
958
|
+
try {
|
|
959
|
+
await result;
|
|
960
|
+
} catch (exc) {
|
|
961
|
+
this._log.warn("event task execution exception:", exc);
|
|
962
|
+
} finally {
|
|
963
|
+
this._handlerDepth -= 1;
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
async dispatchNow(event, payload) {
|
|
832
967
|
const handlers = [...this._handlers.get(event) ?? []];
|
|
833
968
|
for (const handler of handlers) {
|
|
969
|
+
let result;
|
|
970
|
+
this._handlerDepth += 1;
|
|
834
971
|
try {
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
await result;
|
|
838
|
-
}
|
|
972
|
+
this._synchronousHandlerDepth += 1;
|
|
973
|
+
result = handler(payload);
|
|
839
974
|
} catch (exc) {
|
|
840
975
|
this._log.warn(`event ${event} handler execution exception:`, exc);
|
|
976
|
+
this._handlerDepth -= 1;
|
|
977
|
+
continue;
|
|
978
|
+
} finally {
|
|
979
|
+
this._synchronousHandlerDepth -= 1;
|
|
980
|
+
}
|
|
981
|
+
try {
|
|
982
|
+
await result;
|
|
983
|
+
} catch (exc) {
|
|
984
|
+
this._log.warn(`event ${event} handler execution exception:`, exc);
|
|
985
|
+
} finally {
|
|
986
|
+
this._handlerDepth -= 1;
|
|
841
987
|
}
|
|
842
988
|
}
|
|
843
989
|
}
|
|
@@ -1150,12 +1296,293 @@ var GatewayDiscovery = class {
|
|
|
1150
1296
|
var MAX_WS_PAYLOAD_SIZE = 1e6;
|
|
1151
1297
|
var MAX_RPC_INFLIGHT = 16;
|
|
1152
1298
|
var MAX_BACKGROUND_RPC_INFLIGHT = 8;
|
|
1299
|
+
var WORKER_START_TIMEOUT_MS = 2e3;
|
|
1300
|
+
var WORKER_OPEN_TIMEOUT_MS = 2e3;
|
|
1153
1301
|
var _noopLog3 = { error: () => {
|
|
1154
1302
|
}, warn: () => {
|
|
1155
1303
|
}, info: () => {
|
|
1156
1304
|
}, debug: () => {
|
|
1157
1305
|
} };
|
|
1158
1306
|
var _rpcIdCounter = 0;
|
|
1307
|
+
var WORKER_WEBSOCKET_SOURCE = `
|
|
1308
|
+
let socket = null;
|
|
1309
|
+
self.postMessage({ type: 'ready' });
|
|
1310
|
+
self.onmessage = (event) => {
|
|
1311
|
+
const command = event.data || {};
|
|
1312
|
+
if (command.type === 'connect') {
|
|
1313
|
+
try {
|
|
1314
|
+
socket = new WebSocket(command.url);
|
|
1315
|
+
socket.onopen = () => self.postMessage({ type: 'open' });
|
|
1316
|
+
socket.onmessage = (message) => self.postMessage({ type: 'message', data: message.data });
|
|
1317
|
+
socket.onerror = () => self.postMessage({ type: 'error', message: 'websocket error' });
|
|
1318
|
+
socket.onclose = (close) => self.postMessage({
|
|
1319
|
+
type: 'close', code: close.code, reason: close.reason || '', wasClean: close.wasClean === true,
|
|
1320
|
+
});
|
|
1321
|
+
} catch (error) {
|
|
1322
|
+
self.postMessage({ type: 'error', message: error instanceof Error ? error.message : String(error) });
|
|
1323
|
+
}
|
|
1324
|
+
return;
|
|
1325
|
+
}
|
|
1326
|
+
if (command.type === 'send') {
|
|
1327
|
+
try {
|
|
1328
|
+
if (!socket || socket.readyState !== WebSocket.OPEN) throw new Error('websocket is not open');
|
|
1329
|
+
socket.send(command.data);
|
|
1330
|
+
self.postMessage({ type: 'send_result', id: command.id, ok: true });
|
|
1331
|
+
} catch (error) {
|
|
1332
|
+
self.postMessage({
|
|
1333
|
+
type: 'send_result', id: command.id, ok: false,
|
|
1334
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
if (command.type === 'close' && socket) socket.close(command.code, command.reason);
|
|
1340
|
+
};
|
|
1341
|
+
`;
|
|
1342
|
+
var _WorkerWebSocketProxy = class _WorkerWebSocketProxy {
|
|
1343
|
+
constructor(url) {
|
|
1344
|
+
__publicField(this, "readyState", _WorkerWebSocketProxy.CONNECTING);
|
|
1345
|
+
__publicField(this, "onopen", null);
|
|
1346
|
+
__publicField(this, "onmessage", null);
|
|
1347
|
+
__publicField(this, "onerror", null);
|
|
1348
|
+
__publicField(this, "onclose", null);
|
|
1349
|
+
__publicField(this, "_url");
|
|
1350
|
+
__publicField(this, "_worker");
|
|
1351
|
+
__publicField(this, "_workerUrl");
|
|
1352
|
+
__publicField(this, "_nativeSocket", null);
|
|
1353
|
+
__publicField(this, "_workerReady", false);
|
|
1354
|
+
__publicField(this, "_opened", false);
|
|
1355
|
+
__publicField(this, "_closedEventEmitted", false);
|
|
1356
|
+
__publicField(this, "_workerStartTimer", null);
|
|
1357
|
+
__publicField(this, "_workerOpenTimer", null);
|
|
1358
|
+
__publicField(this, "_listeners", /* @__PURE__ */ new Map());
|
|
1359
|
+
__publicField(this, "_pendingSends", /* @__PURE__ */ new Map());
|
|
1360
|
+
__publicField(this, "_sendSeq", 0);
|
|
1361
|
+
this._url = url;
|
|
1362
|
+
this._workerUrl = URL.createObjectURL(new Blob([WORKER_WEBSOCKET_SOURCE], { type: "text/javascript" }));
|
|
1363
|
+
try {
|
|
1364
|
+
this._worker = new Worker(this._workerUrl);
|
|
1365
|
+
} catch (error) {
|
|
1366
|
+
URL.revokeObjectURL(this._workerUrl);
|
|
1367
|
+
throw error;
|
|
1368
|
+
}
|
|
1369
|
+
const worker = this._worker;
|
|
1370
|
+
worker.onmessage = (event) => this._handleWorkerMessage(event.data);
|
|
1371
|
+
worker.onerror = (event) => {
|
|
1372
|
+
event.preventDefault?.();
|
|
1373
|
+
if (!this._opened) {
|
|
1374
|
+
this._fallbackToNative();
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
this._fallbackToNative(true);
|
|
1378
|
+
};
|
|
1379
|
+
this._workerStartTimer = globalThis.setTimeout(() => this._fallbackToNative(), WORKER_START_TIMEOUT_MS);
|
|
1380
|
+
}
|
|
1381
|
+
send(data) {
|
|
1382
|
+
if (this.readyState !== _WorkerWebSocketProxy.OPEN) return Promise.reject(new Error("websocket is not open"));
|
|
1383
|
+
if (this._nativeSocket) {
|
|
1384
|
+
try {
|
|
1385
|
+
this._nativeSocket.send(data);
|
|
1386
|
+
return Promise.resolve();
|
|
1387
|
+
} catch (error) {
|
|
1388
|
+
return Promise.reject(error instanceof Error ? error : new Error(String(error)));
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
const worker = this._worker;
|
|
1392
|
+
if (!worker) return Promise.reject(new Error("websocket worker is not available"));
|
|
1393
|
+
const id = ++this._sendSeq;
|
|
1394
|
+
return new Promise((resolve, reject) => {
|
|
1395
|
+
this._pendingSends.set(id, { resolve, reject });
|
|
1396
|
+
try {
|
|
1397
|
+
worker.postMessage({ type: "send", id, data });
|
|
1398
|
+
} catch (error) {
|
|
1399
|
+
this._pendingSends.delete(id);
|
|
1400
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1403
|
+
}
|
|
1404
|
+
close(code, reason) {
|
|
1405
|
+
if (this.readyState === _WorkerWebSocketProxy.CLOSED) return;
|
|
1406
|
+
this.readyState = _WorkerWebSocketProxy.CLOSING;
|
|
1407
|
+
if (this._nativeSocket) {
|
|
1408
|
+
try {
|
|
1409
|
+
this._nativeSocket.close(code, reason);
|
|
1410
|
+
} catch {
|
|
1411
|
+
this._emitClose(new CloseEvent("close", { code: code ?? 1e3, reason: reason ?? "", wasClean: true }));
|
|
1412
|
+
}
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
if (!this._workerReady || !this._worker) {
|
|
1416
|
+
this._disposeWorker();
|
|
1417
|
+
this._emitClose(new CloseEvent("close", { code: code ?? 1e3, reason: reason ?? "", wasClean: true }));
|
|
1418
|
+
return;
|
|
1419
|
+
}
|
|
1420
|
+
try {
|
|
1421
|
+
this._worker.postMessage({ type: "close", code, reason });
|
|
1422
|
+
} catch {
|
|
1423
|
+
this._fallbackToNative();
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
addEventListener(type, listener) {
|
|
1427
|
+
const callback = typeof listener === "function" ? listener : (event) => listener.handleEvent(event);
|
|
1428
|
+
const listeners = this._listeners.get(type) ?? /* @__PURE__ */ new Set();
|
|
1429
|
+
listeners.add(callback);
|
|
1430
|
+
this._listeners.set(type, listeners);
|
|
1431
|
+
}
|
|
1432
|
+
removeEventListener(type, listener) {
|
|
1433
|
+
if (typeof listener === "function") this._listeners.get(type)?.delete(listener);
|
|
1434
|
+
}
|
|
1435
|
+
_handleWorkerMessage(reply) {
|
|
1436
|
+
if (reply.type === "ready") {
|
|
1437
|
+
if (this._workerReady || !this._worker) return;
|
|
1438
|
+
this._workerReady = true;
|
|
1439
|
+
this._clearWorkerStartTimer();
|
|
1440
|
+
try {
|
|
1441
|
+
this._worker.postMessage({ type: "connect", url: this._url });
|
|
1442
|
+
this._workerOpenTimer = globalThis.setTimeout(
|
|
1443
|
+
() => this._fallbackToNative(),
|
|
1444
|
+
WORKER_OPEN_TIMEOUT_MS
|
|
1445
|
+
);
|
|
1446
|
+
} catch {
|
|
1447
|
+
this._fallbackToNative();
|
|
1448
|
+
}
|
|
1449
|
+
return;
|
|
1450
|
+
}
|
|
1451
|
+
if (reply.type === "send_result" && reply.id !== void 0) {
|
|
1452
|
+
const pending = this._pendingSends.get(reply.id);
|
|
1453
|
+
if (!pending) return;
|
|
1454
|
+
this._pendingSends.delete(reply.id);
|
|
1455
|
+
if (reply.ok) pending.resolve();
|
|
1456
|
+
else pending.reject(new Error(reply.error || "websocket send failed"));
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
if (reply.type === "open") {
|
|
1460
|
+
this._clearWorkerOpenTimer();
|
|
1461
|
+
this._opened = true;
|
|
1462
|
+
this.readyState = _WorkerWebSocketProxy.OPEN;
|
|
1463
|
+
const event = new Event("open");
|
|
1464
|
+
this.onopen?.(event);
|
|
1465
|
+
this._emit("open", event);
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
if (reply.type === "message") {
|
|
1469
|
+
const event = new MessageEvent("message", { data: reply.data });
|
|
1470
|
+
this.onmessage?.(event);
|
|
1471
|
+
this._emit("message", event);
|
|
1472
|
+
return;
|
|
1473
|
+
}
|
|
1474
|
+
if (reply.type === "error") {
|
|
1475
|
+
if (!this._opened && this.readyState === _WorkerWebSocketProxy.CONNECTING) {
|
|
1476
|
+
this._fallbackToNative();
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
this._emitError(reply.message || "websocket worker error");
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
if (reply.type === "close") {
|
|
1483
|
+
if (!this._opened && this.readyState === _WorkerWebSocketProxy.CONNECTING) {
|
|
1484
|
+
this._fallbackToNative();
|
|
1485
|
+
return;
|
|
1486
|
+
}
|
|
1487
|
+
this._emitClose(new CloseEvent("close", {
|
|
1488
|
+
code: reply.code ?? 1006,
|
|
1489
|
+
reason: reply.reason ?? "",
|
|
1490
|
+
wasClean: reply.wasClean === true
|
|
1491
|
+
}));
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
_fallbackToNative(force = false) {
|
|
1495
|
+
if (this._nativeSocket || this.readyState === _WorkerWebSocketProxy.CLOSED) return;
|
|
1496
|
+
if (!force && (this.readyState !== _WorkerWebSocketProxy.CONNECTING || this._opened)) return;
|
|
1497
|
+
const wasOpened = this._opened;
|
|
1498
|
+
this._opened = false;
|
|
1499
|
+
this.readyState = _WorkerWebSocketProxy.CONNECTING;
|
|
1500
|
+
this._disposeWorker();
|
|
1501
|
+
try {
|
|
1502
|
+
const socket = new WebSocket(this._url);
|
|
1503
|
+
this._nativeSocket = socket;
|
|
1504
|
+
socket.onopen = (event) => {
|
|
1505
|
+
this._opened = true;
|
|
1506
|
+
this.readyState = _WorkerWebSocketProxy.OPEN;
|
|
1507
|
+
if (!wasOpened) {
|
|
1508
|
+
this.onopen?.(event);
|
|
1509
|
+
this._emit("open", event);
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
socket.onmessage = (event) => {
|
|
1513
|
+
this.onmessage?.(event);
|
|
1514
|
+
this._emit("message", event);
|
|
1515
|
+
};
|
|
1516
|
+
socket.onerror = (event) => {
|
|
1517
|
+
this.onerror?.(event);
|
|
1518
|
+
this._emit("error", event);
|
|
1519
|
+
};
|
|
1520
|
+
socket.onclose = (event) => this._emitClose(event);
|
|
1521
|
+
} catch (error) {
|
|
1522
|
+
this.readyState = _WorkerWebSocketProxy.CLOSED;
|
|
1523
|
+
this._emitError(error instanceof Error ? error.message : String(error));
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
_emitClose(event) {
|
|
1527
|
+
if (this._closedEventEmitted) return;
|
|
1528
|
+
this._closedEventEmitted = true;
|
|
1529
|
+
this.readyState = _WorkerWebSocketProxy.CLOSED;
|
|
1530
|
+
for (const pending of this._pendingSends.values()) pending.reject(new Error("websocket closed"));
|
|
1531
|
+
this._pendingSends.clear();
|
|
1532
|
+
this.onclose?.(event);
|
|
1533
|
+
this._emit("close", event);
|
|
1534
|
+
this._disposeWorker();
|
|
1535
|
+
this._nativeSocket = null;
|
|
1536
|
+
}
|
|
1537
|
+
_clearWorkerStartTimer() {
|
|
1538
|
+
if (this._workerStartTimer === null) return;
|
|
1539
|
+
globalThis.clearTimeout(this._workerStartTimer);
|
|
1540
|
+
this._workerStartTimer = null;
|
|
1541
|
+
}
|
|
1542
|
+
_clearWorkerOpenTimer() {
|
|
1543
|
+
if (this._workerOpenTimer === null) return;
|
|
1544
|
+
globalThis.clearTimeout(this._workerOpenTimer);
|
|
1545
|
+
this._workerOpenTimer = null;
|
|
1546
|
+
}
|
|
1547
|
+
_disposeWorker() {
|
|
1548
|
+
this._clearWorkerStartTimer();
|
|
1549
|
+
this._clearWorkerOpenTimer();
|
|
1550
|
+
for (const pending of this._pendingSends.values()) pending.reject(new Error("websocket worker unavailable"));
|
|
1551
|
+
this._pendingSends.clear();
|
|
1552
|
+
const worker = this._worker;
|
|
1553
|
+
this._worker = null;
|
|
1554
|
+
if (worker) {
|
|
1555
|
+
worker.onmessage = null;
|
|
1556
|
+
worker.onerror = null;
|
|
1557
|
+
worker.terminate();
|
|
1558
|
+
}
|
|
1559
|
+
const workerUrl = this._workerUrl;
|
|
1560
|
+
this._workerUrl = null;
|
|
1561
|
+
if (workerUrl && typeof URL.revokeObjectURL === "function") URL.revokeObjectURL(workerUrl);
|
|
1562
|
+
}
|
|
1563
|
+
_emitError(message) {
|
|
1564
|
+
const event = new ErrorEvent("error", { message });
|
|
1565
|
+
this.onerror?.(event);
|
|
1566
|
+
this._emit("error", event);
|
|
1567
|
+
}
|
|
1568
|
+
_emit(type, event) {
|
|
1569
|
+
for (const listener of [...this._listeners.get(type) ?? []]) listener(event);
|
|
1570
|
+
}
|
|
1571
|
+
};
|
|
1572
|
+
__publicField(_WorkerWebSocketProxy, "CONNECTING", 0);
|
|
1573
|
+
__publicField(_WorkerWebSocketProxy, "OPEN", 1);
|
|
1574
|
+
__publicField(_WorkerWebSocketProxy, "CLOSING", 2);
|
|
1575
|
+
__publicField(_WorkerWebSocketProxy, "CLOSED", 3);
|
|
1576
|
+
var WorkerWebSocketProxy = _WorkerWebSocketProxy;
|
|
1577
|
+
function createTransportWebSocket(url) {
|
|
1578
|
+
if (typeof Worker === "function" && typeof Blob === "function" && typeof URL !== "undefined" && typeof URL.createObjectURL === "function" && typeof URL.revokeObjectURL === "function") {
|
|
1579
|
+
try {
|
|
1580
|
+
return new WorkerWebSocketProxy(url);
|
|
1581
|
+
} catch {
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
return new WebSocket(url);
|
|
1585
|
+
}
|
|
1159
1586
|
var TRACE_SPAN_DETAIL_FIELDS = [
|
|
1160
1587
|
"method",
|
|
1161
1588
|
"route",
|
|
@@ -1426,6 +1853,10 @@ var RPCTransport = class {
|
|
|
1426
1853
|
__publicField(this, "_traceMode", "off");
|
|
1427
1854
|
// Trace observer:observer(traceInfo) 在每次 RPC/事件携带 _trace 时调用
|
|
1428
1855
|
__publicField(this, "_traceObserver", null);
|
|
1856
|
+
// 每个 transport 实例一个轻量级网络 actor。命令只在此处启动网络操作,
|
|
1857
|
+
// 不等待 RPC 响应,避免应用回调中的 send 反向占住收包路径。
|
|
1858
|
+
__publicField(this, "_actorTail", Promise.resolve());
|
|
1859
|
+
__publicField(this, "_actorBusy", false);
|
|
1429
1860
|
this._dispatcher = opts.eventDispatcher;
|
|
1430
1861
|
this._timeout = opts.timeout ?? 10;
|
|
1431
1862
|
this._connectTimeout = opts.timeout ?? 10;
|
|
@@ -1455,15 +1886,18 @@ var RPCTransport = class {
|
|
|
1455
1886
|
setMetaObserver(observer) {
|
|
1456
1887
|
this._metaObserver = observer;
|
|
1457
1888
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1889
|
+
_notifyMetaObserver(message) {
|
|
1890
|
+
const observer = this._metaObserver;
|
|
1891
|
+
if (observer === null) return;
|
|
1460
1892
|
const meta = message._meta;
|
|
1461
1893
|
if (!isJsonObject(meta)) return;
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1894
|
+
this._dispatcher.enqueueTask(async () => {
|
|
1895
|
+
try {
|
|
1896
|
+
await observer(meta);
|
|
1897
|
+
} catch (exc) {
|
|
1898
|
+
this._log.debug(`meta_observer raised: ${String(exc)}`);
|
|
1899
|
+
}
|
|
1900
|
+
});
|
|
1467
1901
|
}
|
|
1468
1902
|
/** 设置 trace 模式:off / log / diag */
|
|
1469
1903
|
setTraceMode(mode) {
|
|
@@ -1499,7 +1933,10 @@ var RPCTransport = class {
|
|
|
1499
1933
|
* 连接到 WebSocket URL。
|
|
1500
1934
|
* 等待首条消息,若为 challenge 则返回,否则进入消息路由。
|
|
1501
1935
|
*/
|
|
1502
|
-
|
|
1936
|
+
connect(url) {
|
|
1937
|
+
return this._enqueueActorStart(() => this._connectImpl(url));
|
|
1938
|
+
}
|
|
1939
|
+
async _connectImpl(url) {
|
|
1503
1940
|
const tStart = Date.now();
|
|
1504
1941
|
this._log.debug(`connect enter: url=${url}`);
|
|
1505
1942
|
const setup = await this._withConnectionSetup(async () => {
|
|
@@ -1508,7 +1945,7 @@ var RPCTransport = class {
|
|
|
1508
1945
|
this._lastCloseCode = null;
|
|
1509
1946
|
this._lastCloseReason = "";
|
|
1510
1947
|
const handshake = new Promise((resolve, reject) => {
|
|
1511
|
-
const ws =
|
|
1948
|
+
const ws = createTransportWebSocket(url);
|
|
1512
1949
|
this._ws = ws;
|
|
1513
1950
|
this._closed = false;
|
|
1514
1951
|
let initialResolved = false;
|
|
@@ -1637,7 +2074,10 @@ var RPCTransport = class {
|
|
|
1637
2074
|
return setup.handshake;
|
|
1638
2075
|
}
|
|
1639
2076
|
/** 关闭连接 */
|
|
1640
|
-
|
|
2077
|
+
close() {
|
|
2078
|
+
return this._enqueueActor(() => this._closeImpl());
|
|
2079
|
+
}
|
|
2080
|
+
async _closeImpl() {
|
|
1641
2081
|
await this._withConnectionSetup(() => this._closeUnlocked());
|
|
1642
2082
|
}
|
|
1643
2083
|
/** 已持有连接建立串行权时关闭当前 WebSocket。 */
|
|
@@ -1708,7 +2148,13 @@ var RPCTransport = class {
|
|
|
1708
2148
|
* 发起 JSON-RPC 2.0 调用。
|
|
1709
2149
|
* 返回 result 字段的值;若有 error 字段则抛出映射后的错误。
|
|
1710
2150
|
*/
|
|
1711
|
-
|
|
2151
|
+
call(method, params2, timeout, trace, background = false) {
|
|
2152
|
+
return this._enqueueActorStart(
|
|
2153
|
+
() => this._callImpl(method, params2, timeout, trace, background),
|
|
2154
|
+
() => new TimeoutError(`rpc cancelled: ${method}`, { retryable: true })
|
|
2155
|
+
);
|
|
2156
|
+
}
|
|
2157
|
+
_callImpl(method, params2, timeout, trace, background = false) {
|
|
1712
2158
|
if (this._closed || !this._ws) {
|
|
1713
2159
|
throw this._notConnectedError();
|
|
1714
2160
|
}
|
|
@@ -1749,7 +2195,7 @@ var RPCTransport = class {
|
|
|
1749
2195
|
this._drainRpcQueue();
|
|
1750
2196
|
}, effectiveTimeout);
|
|
1751
2197
|
const pending = {
|
|
1752
|
-
resolve:
|
|
2198
|
+
resolve: (response) => {
|
|
1753
2199
|
clearTimeout(timer);
|
|
1754
2200
|
const elapsed = Date.now() - tStart;
|
|
1755
2201
|
if (response.error !== void 0) {
|
|
@@ -1767,7 +2213,7 @@ var RPCTransport = class {
|
|
|
1767
2213
|
if (traceId) {
|
|
1768
2214
|
this._log.info(`[trace=${traceId}] rpc_recv method=${method} rpc_id=${rpcId} duration_ms=${elapsed} status=ok`);
|
|
1769
2215
|
}
|
|
1770
|
-
|
|
2216
|
+
this._notifyMetaObserver(response);
|
|
1771
2217
|
const respTrace = response._trace;
|
|
1772
2218
|
if (respTrace && typeof respTrace === "object" && !Array.isArray(respTrace)) {
|
|
1773
2219
|
this._handleResponseTrace(method, "ok", elapsed, respTrace);
|
|
@@ -1815,7 +2261,10 @@ var RPCTransport = class {
|
|
|
1815
2261
|
return Object.assign(promise, { cancel: () => cancelRpc?.() });
|
|
1816
2262
|
}
|
|
1817
2263
|
/** 发送 JSON-RPC 2.0 Notification,不分配 id,也不等待响应。 */
|
|
1818
|
-
|
|
2264
|
+
notify(method, params2) {
|
|
2265
|
+
return this._enqueueActorStart(() => this._notifyImpl(method, params2));
|
|
2266
|
+
}
|
|
2267
|
+
async _notifyImpl(method, params2) {
|
|
1819
2268
|
if (this._closed || !this._ws) {
|
|
1820
2269
|
throw this._notConnectedError();
|
|
1821
2270
|
}
|
|
@@ -1844,6 +2293,58 @@ var RPCTransport = class {
|
|
|
1844
2293
|
});
|
|
1845
2294
|
return run;
|
|
1846
2295
|
}
|
|
2296
|
+
_enqueueActor(operation) {
|
|
2297
|
+
this._actorBusy = true;
|
|
2298
|
+
const run = this._actorTail.then(async () => {
|
|
2299
|
+
return await operation();
|
|
2300
|
+
}, async () => {
|
|
2301
|
+
return await operation();
|
|
2302
|
+
});
|
|
2303
|
+
const tail = run.then(() => void 0, () => void 0);
|
|
2304
|
+
this._actorTail = tail;
|
|
2305
|
+
void tail.then(() => {
|
|
2306
|
+
if (this._actorTail === tail) this._actorBusy = false;
|
|
2307
|
+
});
|
|
2308
|
+
return run;
|
|
2309
|
+
}
|
|
2310
|
+
_enqueueActorStart(operation, cancellationError) {
|
|
2311
|
+
let resolveResult;
|
|
2312
|
+
let rejectResult;
|
|
2313
|
+
const result = new Promise((resolve, reject) => {
|
|
2314
|
+
resolveResult = resolve;
|
|
2315
|
+
rejectResult = reject;
|
|
2316
|
+
});
|
|
2317
|
+
let cancel;
|
|
2318
|
+
let cancelRequested = false;
|
|
2319
|
+
const launch = () => {
|
|
2320
|
+
if (cancelRequested) return;
|
|
2321
|
+
try {
|
|
2322
|
+
const inner = operation();
|
|
2323
|
+
cancel = inner.cancel;
|
|
2324
|
+
inner.then(resolveResult, rejectResult);
|
|
2325
|
+
} catch (err) {
|
|
2326
|
+
rejectResult(err);
|
|
2327
|
+
}
|
|
2328
|
+
};
|
|
2329
|
+
if (this._actorBusy) {
|
|
2330
|
+
const gate = this._actorTail.then(launch, launch);
|
|
2331
|
+
const tail = gate.then(() => void 0, () => void 0);
|
|
2332
|
+
this._actorTail = tail;
|
|
2333
|
+
void tail.then(() => {
|
|
2334
|
+
if (this._actorTail === tail) this._actorBusy = false;
|
|
2335
|
+
});
|
|
2336
|
+
} else {
|
|
2337
|
+
launch();
|
|
2338
|
+
}
|
|
2339
|
+
return Object.assign(result, {
|
|
2340
|
+
cancel: () => {
|
|
2341
|
+
if (cancelRequested) return;
|
|
2342
|
+
cancelRequested = true;
|
|
2343
|
+
if (cancel) cancel();
|
|
2344
|
+
else rejectResult(cancellationError?.() ?? new TimeoutError("rpc cancelled", { retryable: true }));
|
|
2345
|
+
}
|
|
2346
|
+
});
|
|
2347
|
+
}
|
|
1847
2348
|
_sendText(payload, context, beforeSend) {
|
|
1848
2349
|
return this._enqueueSend(async () => {
|
|
1849
2350
|
if (beforeSend && !beforeSend()) {
|
|
@@ -1854,7 +2355,7 @@ var RPCTransport = class {
|
|
|
1854
2355
|
}
|
|
1855
2356
|
const ws = this._ws;
|
|
1856
2357
|
try {
|
|
1857
|
-
ws.send(payload);
|
|
2358
|
+
await Promise.resolve(ws.send(payload));
|
|
1858
2359
|
} catch (err) {
|
|
1859
2360
|
throw await this._sendFailureError(context, err, ws);
|
|
1860
2361
|
}
|
|
@@ -1978,7 +2479,14 @@ var RPCTransport = class {
|
|
|
1978
2479
|
const enriched = { ...respTrace, spans };
|
|
1979
2480
|
this._log.info(traceDisplay(method, status, elapsedMs, respTrace, spans));
|
|
1980
2481
|
if (this._traceObserver !== null) {
|
|
1981
|
-
this._traceObserver
|
|
2482
|
+
const observer = this._traceObserver;
|
|
2483
|
+
this._dispatcher.enqueueTask(async () => {
|
|
2484
|
+
try {
|
|
2485
|
+
await observer({ type: "rpc", method, trace: enriched, status, duration_ms: elapsedMs });
|
|
2486
|
+
} catch (err) {
|
|
2487
|
+
this._log.debug(`trace observer raised: ${err instanceof Error ? err.message : String(err)}`);
|
|
2488
|
+
}
|
|
2489
|
+
});
|
|
1982
2490
|
}
|
|
1983
2491
|
} catch (err) {
|
|
1984
2492
|
this._log.debug(`trace handling raised: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -2019,10 +2527,17 @@ var RPCTransport = class {
|
|
|
2019
2527
|
this._backgroundRpcQueue = [];
|
|
2020
2528
|
if (!wasClosed) {
|
|
2021
2529
|
const error = new ConnectionError(`websocket closed: code=${event.code} reason=${event.reason}`);
|
|
2022
|
-
this._dispatcher.publish("connection.error", { error });
|
|
2023
2530
|
if (this._onDisconnect) {
|
|
2024
|
-
|
|
2531
|
+
const onDisconnect = this._onDisconnect;
|
|
2532
|
+
this._dispatcher.enqueueTask(async () => {
|
|
2533
|
+
try {
|
|
2534
|
+
await onDisconnect(error, event.code);
|
|
2535
|
+
} catch (exc) {
|
|
2536
|
+
this._log.warn("[aun_core.transport] disconnect callback exception:", exc);
|
|
2537
|
+
}
|
|
2538
|
+
});
|
|
2025
2539
|
}
|
|
2540
|
+
this._dispatcher.enqueue("connection.error", { error });
|
|
2026
2541
|
}
|
|
2027
2542
|
}
|
|
2028
2543
|
_notConnectedError() {
|
|
@@ -2050,39 +2565,43 @@ var RPCTransport = class {
|
|
|
2050
2565
|
if (method === "challenge") {
|
|
2051
2566
|
this._challenge = message;
|
|
2052
2567
|
this._log.debug("challenge received");
|
|
2053
|
-
this._dispatcher.
|
|
2568
|
+
this._dispatcher.enqueue("connection.challenge", message.params ?? {});
|
|
2054
2569
|
return;
|
|
2055
2570
|
}
|
|
2056
2571
|
if (method.startsWith("event/")) {
|
|
2057
2572
|
const protocolEvent = method.slice(6);
|
|
2058
2573
|
const sdkEvent = EVENT_NAME_MAP[protocolEvent] ?? protocolEvent;
|
|
2059
2574
|
this._log.debug(`event recv: event=${sdkEvent} ${summarizeDict(message.params, DIAG_RESULT_FIELDS)}`);
|
|
2060
|
-
|
|
2575
|
+
this._notifyMetaObserver(message);
|
|
2061
2576
|
const params2 = message.params ?? {};
|
|
2062
2577
|
if ("_trace" in params2) {
|
|
2063
2578
|
const eventTrace = params2._trace;
|
|
2064
2579
|
delete params2._trace;
|
|
2065
2580
|
if (eventTrace && typeof eventTrace === "object" && !Array.isArray(eventTrace)) {
|
|
2066
2581
|
if (this._traceObserver !== null) {
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2582
|
+
const observer = this._traceObserver;
|
|
2583
|
+
const tracePayload = eventTrace;
|
|
2584
|
+
this._dispatcher.enqueueTask(async () => {
|
|
2585
|
+
try {
|
|
2586
|
+
await observer({ type: "event", event: sdkEvent, trace: tracePayload });
|
|
2587
|
+
} catch {
|
|
2588
|
+
}
|
|
2589
|
+
});
|
|
2071
2590
|
}
|
|
2072
2591
|
const traceObj = eventTrace;
|
|
2073
2592
|
this._log.info(`[trace=${String(traceObj.trace_id ?? "")}] event_recv event=${sdkEvent}`);
|
|
2074
2593
|
}
|
|
2075
2594
|
}
|
|
2076
2595
|
if (sdkEvent.startsWith("app.")) {
|
|
2077
|
-
this._dispatcher.
|
|
2596
|
+
this._dispatcher.enqueue(sdkEvent, params2);
|
|
2078
2597
|
return;
|
|
2079
2598
|
}
|
|
2080
|
-
this._dispatcher.
|
|
2599
|
+
this._dispatcher.enqueue(`_raw.${sdkEvent}`, params2);
|
|
2081
2600
|
return;
|
|
2082
2601
|
}
|
|
2083
|
-
|
|
2602
|
+
this._notifyMetaObserver(message);
|
|
2084
2603
|
this._log.debug(`notification recv: method=${method || "<no-method>"}`);
|
|
2085
|
-
this._dispatcher.
|
|
2604
|
+
this._dispatcher.enqueue("notification", message);
|
|
2086
2605
|
}
|
|
2087
2606
|
_decodeMessage(raw) {
|
|
2088
2607
|
if (isJsonObject(raw)) {
|
|
@@ -2806,7 +3325,7 @@ var _AuthFlow = class _AuthFlow {
|
|
|
2806
3325
|
return new Promise((resolve, reject) => {
|
|
2807
3326
|
let ws;
|
|
2808
3327
|
try {
|
|
2809
|
-
ws =
|
|
3328
|
+
ws = createTransportWebSocket(gatewayUrl);
|
|
2810
3329
|
} catch (e) {
|
|
2811
3330
|
reject(new AuthError(`WebSocket \u8FDE\u63A5\u5931\u8D25: ${gatewayUrl}`));
|
|
2812
3331
|
return;
|
|
@@ -2849,7 +3368,19 @@ var _AuthFlow = class _AuthFlow {
|
|
|
2849
3368
|
params: params2
|
|
2850
3369
|
});
|
|
2851
3370
|
this._log.debug(`short RPC request full: ${JSON.stringify(redactRpcLogPayload(JSON.parse(requestPayload)))}`);
|
|
2852
|
-
ws.send(requestPayload);
|
|
3371
|
+
const sendResult = ws.send(requestPayload);
|
|
3372
|
+
if (sendResult && typeof sendResult.then === "function") {
|
|
3373
|
+
void Promise.resolve(sendResult).catch((error) => {
|
|
3374
|
+
if (settled) return;
|
|
3375
|
+
settled = true;
|
|
3376
|
+
globalThis.clearTimeout(timeout);
|
|
3377
|
+
try {
|
|
3378
|
+
ws.close();
|
|
3379
|
+
} catch {
|
|
3380
|
+
}
|
|
3381
|
+
reject(error instanceof Error ? error : new AuthError(String(error)));
|
|
3382
|
+
});
|
|
3383
|
+
}
|
|
2853
3384
|
return;
|
|
2854
3385
|
}
|
|
2855
3386
|
if (!isJsonObject(msg) || msg.id !== requestId) return;
|
|
@@ -4479,6 +5010,26 @@ function nonNegativeSafeSequenceHint(value) {
|
|
|
4479
5010
|
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) return null;
|
|
4480
5011
|
return value;
|
|
4481
5012
|
}
|
|
5013
|
+
function canonicalDeliverySource(source) {
|
|
5014
|
+
switch (String(source ?? "").trim()) {
|
|
5015
|
+
case "group-push":
|
|
5016
|
+
case "ordered":
|
|
5017
|
+
case "legacy":
|
|
5018
|
+
return "push";
|
|
5019
|
+
case "tail":
|
|
5020
|
+
case "pull-drained":
|
|
5021
|
+
case "pull_drained":
|
|
5022
|
+
return "pull";
|
|
5023
|
+
case "inline-push":
|
|
5024
|
+
return "inline_push";
|
|
5025
|
+
case "pending-retry":
|
|
5026
|
+
return "pending_retry";
|
|
5027
|
+
case "":
|
|
5028
|
+
return "direct";
|
|
5029
|
+
default:
|
|
5030
|
+
return String(source).trim();
|
|
5031
|
+
}
|
|
5032
|
+
}
|
|
4482
5033
|
function deduplicatePlainForwardPageMessages(value, afterSeq) {
|
|
4483
5034
|
if (!Array.isArray(value)) return [];
|
|
4484
5035
|
const bySeq = /* @__PURE__ */ new Map();
|
|
@@ -4507,6 +5058,11 @@ function p2pAppEventFromPlainPullMessage(message) {
|
|
|
4507
5058
|
var MessageDeliveryEngine = class {
|
|
4508
5059
|
constructor(runtime) {
|
|
4509
5060
|
__publicField(this, "runtime");
|
|
5061
|
+
__publicField(this, "syncRun", 0);
|
|
5062
|
+
__publicField(this, "syncingNamespaces", /* @__PURE__ */ new Set());
|
|
5063
|
+
__publicField(this, "syncSessions", /* @__PURE__ */ new Map());
|
|
5064
|
+
__publicField(this, "pendingPullDeliveryChanges", null);
|
|
5065
|
+
__publicField(this, "deliveryGeneration", 0);
|
|
4510
5066
|
__publicField(this, "realtimeTailResults", null);
|
|
4511
5067
|
__publicField(this, "realtimeSyncing", null);
|
|
4512
5068
|
__publicField(this, "pendingP2pPullUpper", null);
|
|
@@ -4525,7 +5081,10 @@ var MessageDeliveryEngine = class {
|
|
|
4525
5081
|
this.runtime = runtime;
|
|
4526
5082
|
}
|
|
4527
5083
|
resetInlineAckState() {
|
|
5084
|
+
this.syncStopped("aborted");
|
|
4528
5085
|
this.inlineGeneration += 1;
|
|
5086
|
+
this.deliveryGeneration += 1;
|
|
5087
|
+
this.pendingPullDeliveryChanges = null;
|
|
4529
5088
|
void this.runtime.client._rpcPipeline?.invalidatePulls?.();
|
|
4530
5089
|
this.realtimeSyncing = null;
|
|
4531
5090
|
this.pendingP2pPullUpper = null;
|
|
@@ -4538,6 +5097,8 @@ var MessageDeliveryEngine = class {
|
|
|
4538
5097
|
this.pendingP2PInlineAcks = null;
|
|
4539
5098
|
this.pendingGroupInlineAcks = null;
|
|
4540
5099
|
this.pendingGroupInlineAckNamespaces = null;
|
|
5100
|
+
this.syncingNamespaces.clear();
|
|
5101
|
+
this.syncSessions.clear();
|
|
4541
5102
|
}
|
|
4542
5103
|
isInlineGenerationCurrent(generation) {
|
|
4543
5104
|
return generation === this.inlineGeneration;
|
|
@@ -4565,6 +5126,91 @@ var MessageDeliveryEngine = class {
|
|
|
4565
5126
|
endRealtimeSync(ns) {
|
|
4566
5127
|
this.realtimeSyncing?.delete(ns);
|
|
4567
5128
|
}
|
|
5129
|
+
onPullStarted(ns) {
|
|
5130
|
+
if (!ns || this.syncingNamespaces.has(ns)) return;
|
|
5131
|
+
if (this.syncingNamespaces.size === 0) {
|
|
5132
|
+
this.syncRun += 1;
|
|
5133
|
+
this.syncSessions.clear();
|
|
5134
|
+
this.runtime.client._dispatcher?.enqueue?.("sync.started", {
|
|
5135
|
+
run_id: this.syncRun,
|
|
5136
|
+
source: "pull",
|
|
5137
|
+
syncing: true,
|
|
5138
|
+
namespaces_pending: 1,
|
|
5139
|
+
received_total: 0,
|
|
5140
|
+
namespace: ns,
|
|
5141
|
+
started_at: Date.now()
|
|
5142
|
+
});
|
|
5143
|
+
}
|
|
5144
|
+
this.syncingNamespaces.add(ns);
|
|
5145
|
+
this.syncSessions.set(ns, { pages: 0, raw: 0, pulled: 0 });
|
|
5146
|
+
}
|
|
5147
|
+
onPullPage(ns, pulledCount, hasMore, maxSeen, remaining, rawCount = pulledCount, currentSeq, targetSeq) {
|
|
5148
|
+
this.onPullStarted(ns);
|
|
5149
|
+
if (!ns) return;
|
|
5150
|
+
const session = this.syncSessions.get(ns) ?? { pages: 0, raw: 0, pulled: 0 };
|
|
5151
|
+
session.pages += 1;
|
|
5152
|
+
session.raw += Math.max(0, rawCount);
|
|
5153
|
+
session.pulled += Math.max(0, pulledCount);
|
|
5154
|
+
this.syncSessions.set(ns, session);
|
|
5155
|
+
const received = session.pulled;
|
|
5156
|
+
const progress = {
|
|
5157
|
+
run_id: this.syncRun,
|
|
5158
|
+
source: "pull",
|
|
5159
|
+
namespace: ns,
|
|
5160
|
+
page: session.pages,
|
|
5161
|
+
page_raw_count: Math.max(0, rawCount),
|
|
5162
|
+
page_pulled_count: Math.max(0, pulledCount),
|
|
5163
|
+
pulled_count: received,
|
|
5164
|
+
received_total: received,
|
|
5165
|
+
batch_size: Math.max(0, pulledCount),
|
|
5166
|
+
estimated_remaining: hasMore ? void 0 : 0,
|
|
5167
|
+
max_seen: maxSeen,
|
|
5168
|
+
has_more: hasMore
|
|
5169
|
+
};
|
|
5170
|
+
if (typeof currentSeq === "number" && Number.isSafeInteger(currentSeq) && currentSeq >= 0) {
|
|
5171
|
+
progress.current_seq = currentSeq;
|
|
5172
|
+
}
|
|
5173
|
+
if (typeof targetSeq === "number" && Number.isSafeInteger(targetSeq) && targetSeq >= 0) {
|
|
5174
|
+
progress.target_seq = targetSeq;
|
|
5175
|
+
}
|
|
5176
|
+
if (typeof remaining === "number" && Number.isSafeInteger(remaining) && remaining >= 0) {
|
|
5177
|
+
progress.remaining = remaining;
|
|
5178
|
+
progress.estimated_remaining = progress.remaining;
|
|
5179
|
+
}
|
|
5180
|
+
this.runtime.client._dispatcher?.enqueue?.("sync.progress", progress);
|
|
5181
|
+
}
|
|
5182
|
+
onPullAborted() {
|
|
5183
|
+
this.syncStopped("aborted");
|
|
5184
|
+
}
|
|
5185
|
+
syncStopped(reason = "pull_drained") {
|
|
5186
|
+
if (this.syncingNamespaces.size === 0) return;
|
|
5187
|
+
const sessions = [...this.syncSessions.entries()];
|
|
5188
|
+
const receivedTotal = sessions.reduce((total, [, session]) => total + session.pulled, 0);
|
|
5189
|
+
this.syncingNamespaces.clear();
|
|
5190
|
+
this.syncSessions.clear();
|
|
5191
|
+
const payload = {
|
|
5192
|
+
run_id: this.syncRun,
|
|
5193
|
+
source: "pull",
|
|
5194
|
+
syncing: false,
|
|
5195
|
+
namespaces_pending: 0,
|
|
5196
|
+
received_total: receivedTotal,
|
|
5197
|
+
reason,
|
|
5198
|
+
stopped_at: Date.now()
|
|
5199
|
+
};
|
|
5200
|
+
if (sessions.length === 1) {
|
|
5201
|
+
const [namespace, session] = sessions[0];
|
|
5202
|
+
payload.namespace = namespace;
|
|
5203
|
+
payload.pages = session.pages;
|
|
5204
|
+
payload.pulled_count = session.pulled;
|
|
5205
|
+
} else if (sessions.length > 1) {
|
|
5206
|
+
payload.namespaces = sessions.map(([namespace]) => namespace);
|
|
5207
|
+
payload.pages = sessions.reduce((total, [, session]) => total + session.pages, 0);
|
|
5208
|
+
payload.pulled_count = receivedTotal;
|
|
5209
|
+
}
|
|
5210
|
+
this.runtime.client._dispatcher?.enqueue?.("sync.stopped", {
|
|
5211
|
+
...payload
|
|
5212
|
+
});
|
|
5213
|
+
}
|
|
4568
5214
|
hasPendingPull(ns) {
|
|
4569
5215
|
const pending = ns.startsWith("p2p:") ? this.pendingP2pPullUpper : this.pendingGroupPullUpper;
|
|
4570
5216
|
return (pending?.get(ns) ?? 0) > 0;
|
|
@@ -4573,6 +5219,75 @@ var MessageDeliveryEngine = class {
|
|
|
4573
5219
|
if (!ns) return;
|
|
4574
5220
|
this.schedulePendingPullIfNeeded(ns, "pull-gate-idle");
|
|
4575
5221
|
}
|
|
5222
|
+
onPullWorkSettled() {
|
|
5223
|
+
const pipeline = this.runtime.client._rpcPipeline;
|
|
5224
|
+
if (pipeline?.hasAnyPullActivity?.() === true) return;
|
|
5225
|
+
void this.flushPullDeliveryChanges().finally(() => this.syncStopped("pull_drained"));
|
|
5226
|
+
}
|
|
5227
|
+
deliveryChangeNamespace(event, payload, ns = "") {
|
|
5228
|
+
if (event === "message.received") return "p2p";
|
|
5229
|
+
if (event !== "group.message_created") return "";
|
|
5230
|
+
if (!isJsonObject(payload)) return "";
|
|
5231
|
+
const aid = String(this.runtime.client._aid ?? "").trim().toLowerCase();
|
|
5232
|
+
const dot = aid.indexOf(".");
|
|
5233
|
+
const localIssuer = dot > 0 ? aid.slice(dot + 1) : "";
|
|
5234
|
+
const fallback = ns.startsWith("group:") ? ns.slice("group:".length) : "";
|
|
5235
|
+
const groupAid = normalizeGroupAid(payload.group_aid ?? payload.group_id ?? fallback, { localIssuer });
|
|
5236
|
+
return groupAid.includes(".") ? `group:${groupAid}` : "";
|
|
5237
|
+
}
|
|
5238
|
+
isDeliverableMessageBody(event, payload) {
|
|
5239
|
+
if (event !== "message.received" && event !== "group.message_created" || !isJsonObject(payload)) return false;
|
|
5240
|
+
const messageId = typeof payload.message_id === "string" && payload.message_id.trim().length > 0;
|
|
5241
|
+
const seq2 = typeof payload.seq === "number" && Number.isSafeInteger(payload.seq) && payload.seq > 0;
|
|
5242
|
+
return messageId || seq2;
|
|
5243
|
+
}
|
|
5244
|
+
recordDeliveryChange(changes, event, payload, ns, generation) {
|
|
5245
|
+
if (generation !== this.deliveryGeneration || !this.isDeliverableMessageBody(event, payload)) return;
|
|
5246
|
+
const namespace = this.deliveryChangeNamespace(event, payload, ns);
|
|
5247
|
+
if (!namespace) return;
|
|
5248
|
+
changes.set(namespace, (changes.get(namespace) ?? 0) + 1);
|
|
5249
|
+
}
|
|
5250
|
+
deliveryChangesPayload(changes) {
|
|
5251
|
+
return [...changes.entries()].filter(([, deliveredCount]) => deliveredCount > 0).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([namespace, deliveredCount]) => ({ namespace, delivered_count: deliveredCount }));
|
|
5252
|
+
}
|
|
5253
|
+
createDeliveryChangeBatch(trigger) {
|
|
5254
|
+
return { generation: this.deliveryGeneration, trigger, changes: /* @__PURE__ */ new Map(), closed: false };
|
|
5255
|
+
}
|
|
5256
|
+
async flushRealtimeDeliveryChanges(batch) {
|
|
5257
|
+
if (batch.closed) return;
|
|
5258
|
+
batch.closed = true;
|
|
5259
|
+
if (batch.generation !== this.deliveryGeneration) return;
|
|
5260
|
+
if (this.deliveryChangesPayload(batch.changes).length === 0) return;
|
|
5261
|
+
const pendingPull = this.pendingPullDeliveryChanges;
|
|
5262
|
+
this.pendingPullDeliveryChanges = null;
|
|
5263
|
+
if (pendingPull) {
|
|
5264
|
+
for (const [namespace, count] of pendingPull) {
|
|
5265
|
+
batch.changes.set(namespace, (batch.changes.get(namespace) ?? 0) + count);
|
|
5266
|
+
}
|
|
5267
|
+
}
|
|
5268
|
+
if (batch.generation !== this.deliveryGeneration) return;
|
|
5269
|
+
const changes = this.deliveryChangesPayload(batch.changes);
|
|
5270
|
+
if (changes.length === 0) return;
|
|
5271
|
+
this.runtime.client._dispatcher.enqueue("delivery.changed", {
|
|
5272
|
+
trigger: batch.trigger,
|
|
5273
|
+
source: batch.trigger,
|
|
5274
|
+
changes
|
|
5275
|
+
});
|
|
5276
|
+
}
|
|
5277
|
+
async flushPullDeliveryChanges() {
|
|
5278
|
+
const generation = this.deliveryGeneration;
|
|
5279
|
+
const pending = this.pendingPullDeliveryChanges;
|
|
5280
|
+
if (!pending || pending.size === 0 || generation !== this.deliveryGeneration) return;
|
|
5281
|
+
this.pendingPullDeliveryChanges = null;
|
|
5282
|
+
if (generation !== this.deliveryGeneration) return;
|
|
5283
|
+
const changes = this.deliveryChangesPayload(pending);
|
|
5284
|
+
if (changes.length === 0) return;
|
|
5285
|
+
this.runtime.client._dispatcher.enqueue("delivery.changed", {
|
|
5286
|
+
trigger: "pull_drained",
|
|
5287
|
+
source: "pull",
|
|
5288
|
+
changes
|
|
5289
|
+
});
|
|
5290
|
+
}
|
|
4576
5291
|
recordPendingPull(ns, seq2) {
|
|
4577
5292
|
if (!ns || !Number.isSafeInteger(seq2) || seq2 <= 0) return;
|
|
4578
5293
|
const pending = ns.startsWith("p2p:") ? this.pendingP2pPullUpper ?? /* @__PURE__ */ new Map() : this.pendingGroupPullUpper ?? /* @__PURE__ */ new Map();
|
|
@@ -5074,7 +5789,7 @@ var MessageDeliveryEngine = class {
|
|
|
5074
5789
|
}
|
|
5075
5790
|
const message = normalizeGroupMentionMode(rawMessage);
|
|
5076
5791
|
if (this.recallEventFromGroupMessage(message)) {
|
|
5077
|
-
if (await this.publishGroupRecallTombstone(groupId, seq2, message)) {
|
|
5792
|
+
if (await this.publishGroupRecallTombstone(groupId, seq2, message, "pull")) {
|
|
5078
5793
|
this.ensurePullOperationCurrent();
|
|
5079
5794
|
this.markPublishedSeq(ns, seq2);
|
|
5080
5795
|
publishedCount += 1;
|
|
@@ -5145,16 +5860,27 @@ var MessageDeliveryEngine = class {
|
|
|
5145
5860
|
this.ensurePullOperationCurrent();
|
|
5146
5861
|
await this.confirmPlainForwardAck(ns, ackMethod, pendingAckSeq, groupId);
|
|
5147
5862
|
}
|
|
5863
|
+
const remaining = typeof response.remaining === "number" && Number.isSafeInteger(response.remaining) && response.remaining >= 0 ? response.remaining : null;
|
|
5864
|
+
this.onPullPage(
|
|
5865
|
+
ns,
|
|
5866
|
+
publishedCount,
|
|
5867
|
+
response.has_more === true,
|
|
5868
|
+
messages.length > 0 ? Number(messages[messages.length - 1].seq ?? 0) : void 0,
|
|
5869
|
+
remaining,
|
|
5870
|
+
messages.length,
|
|
5871
|
+
client._seqTracker.getContiguousSeq(ns),
|
|
5872
|
+
messages.length > 0 ? Number(messages[messages.length - 1].seq ?? 0) : void 0
|
|
5873
|
+
);
|
|
5148
5874
|
return { rawCount: messages.length, publishedCount };
|
|
5149
5875
|
}
|
|
5150
|
-
enqueueOrderedMessage(ns, event, seq2, payload) {
|
|
5876
|
+
enqueueOrderedMessage(ns, event, seq2, payload, source = "push") {
|
|
5151
5877
|
const client = this.runtime.client;
|
|
5152
5878
|
let queue = client._pendingOrderedMsgs.get(ns);
|
|
5153
5879
|
if (!queue) {
|
|
5154
5880
|
queue = /* @__PURE__ */ new Map();
|
|
5155
5881
|
client._pendingOrderedMsgs.set(ns, queue);
|
|
5156
5882
|
}
|
|
5157
|
-
queue.set(seq2, { event, payload });
|
|
5883
|
+
queue.set(seq2, { event, payload, source });
|
|
5158
5884
|
if (queue.size > PENDING_ORDERED_LIMIT) {
|
|
5159
5885
|
const drop = [...queue.keys()].sort((a, b) => a - b).slice(0, queue.size - PENDING_ORDERED_LIMIT);
|
|
5160
5886
|
for (const oldSeq of drop) queue.delete(oldSeq);
|
|
@@ -5163,23 +5889,23 @@ var MessageDeliveryEngine = class {
|
|
|
5163
5889
|
isGroupEventNamespace(ns) {
|
|
5164
5890
|
return ns.startsWith("group_event:");
|
|
5165
5891
|
}
|
|
5166
|
-
async publishOrderedQueueItem(ns, event, seq2, payload, pullResponse = false) {
|
|
5892
|
+
async publishOrderedQueueItem(ns, event, seq2, payload, pullResponse = false, source = "push", batch) {
|
|
5167
5893
|
const client = this.runtime.client;
|
|
5168
5894
|
if (event === "group.changed" && this.isGroupEventNamespace(ns)) {
|
|
5169
|
-
await this.publishOrderedGroupChanged(payload);
|
|
5895
|
+
await this.publishOrderedGroupChanged(payload, source);
|
|
5170
5896
|
return;
|
|
5171
5897
|
}
|
|
5172
5898
|
if (event === "message.recalled") {
|
|
5173
|
-
await this.publishMessageRecallTombstone(seq2, payload);
|
|
5899
|
+
await this.publishMessageRecallTombstone(seq2, payload, source);
|
|
5174
5900
|
return;
|
|
5175
5901
|
}
|
|
5176
5902
|
if (pullResponse) {
|
|
5177
|
-
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload));
|
|
5903
|
+
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source, ns, batch));
|
|
5178
5904
|
return;
|
|
5179
5905
|
}
|
|
5180
|
-
await client._publishAppEvent(event, payload);
|
|
5906
|
+
await client._publishAppEvent(event, payload, source, ns, batch);
|
|
5181
5907
|
}
|
|
5182
|
-
async publishOrderedGroupChanged(payload) {
|
|
5908
|
+
async publishOrderedGroupChanged(payload, source = "ordered") {
|
|
5183
5909
|
const client = this.runtime.client;
|
|
5184
5910
|
if (isJsonObject(payload)) {
|
|
5185
5911
|
const eventPayload = payload;
|
|
@@ -5190,7 +5916,7 @@ var MessageDeliveryEngine = class {
|
|
|
5190
5916
|
if (groupId) client._cleanupDissolvedGroup?.(groupId);
|
|
5191
5917
|
}
|
|
5192
5918
|
}
|
|
5193
|
-
await client._publishAppEvent("group.changed", payload);
|
|
5919
|
+
await client._publishAppEvent("group.changed", payload, source);
|
|
5194
5920
|
}
|
|
5195
5921
|
isInstanceScopedMessageEvent(event) {
|
|
5196
5922
|
return event === "message.received" || event === "message.recalled" || event === "message.undecryptable" || event === "group.message_created" || event === "group.message_recalled" || event === "group.message_undecryptable";
|
|
@@ -5207,17 +5933,22 @@ var MessageDeliveryEngine = class {
|
|
|
5207
5933
|
}
|
|
5208
5934
|
return result;
|
|
5209
5935
|
}
|
|
5210
|
-
normalizePublishedMessagePayload(event, payload) {
|
|
5936
|
+
normalizePublishedMessagePayload(event, payload, source = "direct") {
|
|
5937
|
+
let normalized;
|
|
5211
5938
|
if (this.isInstanceScopedMessageEvent(event)) {
|
|
5212
5939
|
payload = this.stripInlineInternalFields(payload);
|
|
5213
5940
|
if (event === "group.message_created") payload = normalizeGroupMentionMode(payload);
|
|
5214
|
-
|
|
5215
|
-
}
|
|
5216
|
-
if (this.isGroupScopedEvent(event)) {
|
|
5941
|
+
normalized = this.attachAppMessageEnvelope(this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload)));
|
|
5942
|
+
} else if (this.isGroupScopedEvent(event)) {
|
|
5217
5943
|
payload = this.stripInlineInternalFields(payload);
|
|
5218
|
-
|
|
5944
|
+
normalized = this.attachAppGroupEventEnvelope(this.attachCurrentInstanceContext(payload));
|
|
5945
|
+
} else {
|
|
5946
|
+
normalized = payload;
|
|
5219
5947
|
}
|
|
5220
|
-
|
|
5948
|
+
const canonicalSource = canonicalDeliverySource(source);
|
|
5949
|
+
if (canonicalSource === "direct" || !isJsonObject(normalized)) return normalized;
|
|
5950
|
+
if (!this.isInstanceScopedMessageEvent(event) && !this.isGroupScopedEvent(event)) return normalized;
|
|
5951
|
+
return { ...normalized, source: canonicalSource };
|
|
5221
5952
|
}
|
|
5222
5953
|
stripInlineInternalFields(payload) {
|
|
5223
5954
|
if (!isJsonObject(payload)) return payload;
|
|
@@ -5320,6 +6051,7 @@ var MessageDeliveryEngine = class {
|
|
|
5320
6051
|
setIfPresent("type", firstValue(body.type, params2.type, params2.message_type, params2.payload_type));
|
|
5321
6052
|
setIfPresent("kind", firstValue(body.kind, params2.kind));
|
|
5322
6053
|
setIfPresent("version", firstValue(body.version, params2.version));
|
|
6054
|
+
setIfPresent("message_id", firstValue(params2.message_id, body.message_id, resultObj.message_id));
|
|
5323
6055
|
setIfPresent("timestamp", firstValue(params2.timestamp, resultObj.timestamp, resultObj.created_at, resultObj.t_server, Date.now()));
|
|
5324
6056
|
envelope.encrypted = Boolean(encrypted);
|
|
5325
6057
|
const context = this.envelopeMetadata(params2.context);
|
|
@@ -5432,7 +6164,7 @@ var MessageDeliveryEngine = class {
|
|
|
5432
6164
|
if (tombstoneId) return `p2p|tombstone:${tombstoneId}`;
|
|
5433
6165
|
return `p2p|unknown:${Date.now()}:${Math.random()}`;
|
|
5434
6166
|
}
|
|
5435
|
-
async publishMessageRecallTombstone(seq2, message) {
|
|
6167
|
+
async publishMessageRecallTombstone(seq2, message, source = "push") {
|
|
5436
6168
|
const client = this.runtime.client;
|
|
5437
6169
|
const eventPayload = this.recallEventFromMessage(message);
|
|
5438
6170
|
if (!eventPayload) return false;
|
|
@@ -5451,7 +6183,7 @@ var MessageDeliveryEngine = class {
|
|
|
5451
6183
|
const drop = [...seen.entries()].sort((a, b) => a[1] - b[1]).slice(0, seen.size - MESSAGE_RECALL_SEEN_LIMIT);
|
|
5452
6184
|
for (const [oldKey] of drop) seen.delete(oldKey);
|
|
5453
6185
|
}
|
|
5454
|
-
await client._publishAppEvent("message.recalled", eventPayload);
|
|
6186
|
+
await client._publishAppEvent("message.recalled", eventPayload, source);
|
|
5455
6187
|
client._clientLog.debug(`message.recalled published: seq=${String(seq2)} ids=${JSON.stringify(eventPayload.message_ids)}`);
|
|
5456
6188
|
return true;
|
|
5457
6189
|
}
|
|
@@ -5513,7 +6245,7 @@ var MessageDeliveryEngine = class {
|
|
|
5513
6245
|
if (tombstoneId) return `${normalizedGroupId}|tombstone:${tombstoneId}`;
|
|
5514
6246
|
return `${normalizedGroupId}|unknown:${Date.now()}:${Math.random()}`;
|
|
5515
6247
|
}
|
|
5516
|
-
async publishGroupRecallTombstone(groupId, seq2, message) {
|
|
6248
|
+
async publishGroupRecallTombstone(groupId, seq2, message, source = "push") {
|
|
5517
6249
|
const client = this.runtime.client;
|
|
5518
6250
|
const eventPayload = this.recallEventFromGroupMessage(message);
|
|
5519
6251
|
if (!eventPayload) return false;
|
|
@@ -5534,7 +6266,7 @@ var MessageDeliveryEngine = class {
|
|
|
5534
6266
|
const drop = [...seen.entries()].sort((a, b) => a[1] - b[1]).slice(0, seen.size - GROUP_RECALL_SEEN_LIMIT);
|
|
5535
6267
|
for (const [oldKey] of drop) seen.delete(oldKey);
|
|
5536
6268
|
}
|
|
5537
|
-
await client._publishAppEvent("group.message_recalled", eventPayload);
|
|
6269
|
+
await client._publishAppEvent("group.message_recalled", eventPayload, source);
|
|
5538
6270
|
client._clientLog.debug(`group.message_recalled published: group=${groupId} seq=${String(seq2)} ids=${JSON.stringify(eventPayload.message_ids)}`);
|
|
5539
6271
|
return true;
|
|
5540
6272
|
}
|
|
@@ -5597,8 +6329,9 @@ var MessageDeliveryEngine = class {
|
|
|
5597
6329
|
}
|
|
5598
6330
|
if (contig !== contigBefore) this.persistSeq(ns);
|
|
5599
6331
|
}
|
|
5600
|
-
async publishAppEvent(event, payload) {
|
|
6332
|
+
async publishAppEvent(event, payload, source = "direct", ns = "", batch) {
|
|
5601
6333
|
const client = this.runtime.client;
|
|
6334
|
+
const generation = this.deliveryGeneration;
|
|
5602
6335
|
if ((event === "message.received" || event === "group.message_created") && isJsonObject(payload)) {
|
|
5603
6336
|
client._maybeAppendEchoTraceReceive(payload);
|
|
5604
6337
|
}
|
|
@@ -5617,7 +6350,24 @@ var MessageDeliveryEngine = class {
|
|
|
5617
6350
|
client._clientLog.debug(`agent_md etag inject skipped: ${String(exc)}`);
|
|
5618
6351
|
}
|
|
5619
6352
|
}
|
|
5620
|
-
|
|
6353
|
+
const canonicalSource = canonicalDeliverySource(source);
|
|
6354
|
+
let normalized = this.normalizePublishedMessagePayload(event, payload, source);
|
|
6355
|
+
if (isJsonObject(normalized) && (this.isInstanceScopedMessageEvent(event) || this.isGroupScopedEvent(event)) && canonicalSource !== "direct") {
|
|
6356
|
+
normalized = { ...normalized, source: canonicalSource };
|
|
6357
|
+
}
|
|
6358
|
+
client._dispatcher.enqueue(event, normalized);
|
|
6359
|
+
if (generation !== this.deliveryGeneration) return;
|
|
6360
|
+
if (canonicalSource !== "pull" && canonicalSource !== "pending_retry" && canonicalSource !== "push" && canonicalSource !== "inline_push") return;
|
|
6361
|
+
if (canonicalSource === "push" || canonicalSource === "inline_push") {
|
|
6362
|
+
const localBatch = batch ?? this.createDeliveryChangeBatch(canonicalSource);
|
|
6363
|
+
this.recordDeliveryChange(localBatch.changes, event, payload, ns, generation);
|
|
6364
|
+
if (!batch) await this.flushRealtimeDeliveryChanges(localBatch);
|
|
6365
|
+
} else {
|
|
6366
|
+
const changes = this.pendingPullDeliveryChanges ?? /* @__PURE__ */ new Map();
|
|
6367
|
+
this.pendingPullDeliveryChanges = changes;
|
|
6368
|
+
this.recordDeliveryChange(changes, event, payload, ns, generation);
|
|
6369
|
+
if (client._rpcPipeline?.hasAnyPullActivity?.() !== true) await this.flushPullDeliveryChanges();
|
|
6370
|
+
}
|
|
5621
6371
|
}
|
|
5622
6372
|
messageTargetsCurrentInstance(message) {
|
|
5623
6373
|
if (!isJsonObject(message)) return true;
|
|
@@ -5750,7 +6500,7 @@ var MessageDeliveryEngine = class {
|
|
|
5750
6500
|
const client = this.runtime.client;
|
|
5751
6501
|
try {
|
|
5752
6502
|
if (!isJsonObject(data)) {
|
|
5753
|
-
await client._publishAppEvent("message.received", data);
|
|
6503
|
+
await client._publishAppEvent("message.received", data, "push");
|
|
5754
6504
|
return;
|
|
5755
6505
|
}
|
|
5756
6506
|
const msg = { ...data };
|
|
@@ -5795,7 +6545,7 @@ var MessageDeliveryEngine = class {
|
|
|
5795
6545
|
await client._publishEncryptedPushMessage("message.received", "message.undecryptable", "", seq2 ?? 0, msg, false);
|
|
5796
6546
|
return;
|
|
5797
6547
|
}
|
|
5798
|
-
await client._publishAppEvent("message.received", msg);
|
|
6548
|
+
await client._publishAppEvent("message.received", msg, "push");
|
|
5799
6549
|
}
|
|
5800
6550
|
} catch (exc) {
|
|
5801
6551
|
client._clientLog.warn(`P2P push processing failed:${String(exc)}`);
|
|
@@ -5810,7 +6560,7 @@ var MessageDeliveryEngine = class {
|
|
|
5810
6560
|
_decrypt_error: String(exc)
|
|
5811
6561
|
};
|
|
5812
6562
|
client._attachV2EnvelopeMetadataFromSource(safeEvent, data);
|
|
5813
|
-
await client._publishAppEvent("message.undecryptable", safeEvent);
|
|
6563
|
+
await client._publishAppEvent("message.undecryptable", safeEvent, "push");
|
|
5814
6564
|
}
|
|
5815
6565
|
}
|
|
5816
6566
|
}
|
|
@@ -5834,7 +6584,7 @@ var MessageDeliveryEngine = class {
|
|
|
5834
6584
|
const client = this.runtime.client;
|
|
5835
6585
|
try {
|
|
5836
6586
|
if (!isJsonObject(data)) {
|
|
5837
|
-
await client._publishAppEvent("group.message_created", data);
|
|
6587
|
+
await client._publishAppEvent("group.message_created", data, "push");
|
|
5838
6588
|
return;
|
|
5839
6589
|
}
|
|
5840
6590
|
const msg = { ...data };
|
|
@@ -5906,7 +6656,7 @@ var MessageDeliveryEngine = class {
|
|
|
5906
6656
|
await client._publishEncryptedPushMessage("group.message_created", "group.message_undecryptable", "", seq2 ?? 0, msg, true);
|
|
5907
6657
|
return;
|
|
5908
6658
|
}
|
|
5909
|
-
await client._publishAppEvent("group.message_created", msg);
|
|
6659
|
+
await client._publishAppEvent("group.message_created", msg, "push");
|
|
5910
6660
|
}
|
|
5911
6661
|
} catch (exc) {
|
|
5912
6662
|
client._clientLog.warn(`group push processing failed:${String(exc)}`);
|
|
@@ -5921,7 +6671,7 @@ var MessageDeliveryEngine = class {
|
|
|
5921
6671
|
_decrypt_error: String(exc)
|
|
5922
6672
|
};
|
|
5923
6673
|
client._attachV2EnvelopeMetadataFromSource(safeEvent, data);
|
|
5924
|
-
await client._publishAppEvent("group.message_undecryptable", safeEvent);
|
|
6674
|
+
await client._publishAppEvent("group.message_undecryptable", safeEvent, "push");
|
|
5925
6675
|
}
|
|
5926
6676
|
}
|
|
5927
6677
|
}
|
|
@@ -5929,7 +6679,7 @@ var MessageDeliveryEngine = class {
|
|
|
5929
6679
|
const client = this.runtime.client;
|
|
5930
6680
|
const groupId = notification.group_id ?? "";
|
|
5931
6681
|
if (!groupId) {
|
|
5932
|
-
await client._publishAppEvent("group.message_created", notification);
|
|
6682
|
+
await client._publishAppEvent("group.message_created", notification, "push");
|
|
5933
6683
|
return;
|
|
5934
6684
|
}
|
|
5935
6685
|
if (client._sessionOptions?.background_sync === false) {
|
|
@@ -5949,7 +6699,7 @@ var MessageDeliveryEngine = class {
|
|
|
5949
6699
|
return;
|
|
5950
6700
|
} catch (exc) {
|
|
5951
6701
|
client._clientLog.warn(`auto pull group message failed:${String(exc)}`);
|
|
5952
|
-
await client._publishAppEvent("group.message_created", notification);
|
|
6702
|
+
await client._publishAppEvent("group.message_created", notification, "push");
|
|
5953
6703
|
return;
|
|
5954
6704
|
}
|
|
5955
6705
|
}
|
|
@@ -5976,7 +6726,7 @@ var MessageDeliveryEngine = class {
|
|
|
5976
6726
|
} catch (exc) {
|
|
5977
6727
|
client._clientLog.warn(`auto pull group message commit/ack failed:${String(exc)}`);
|
|
5978
6728
|
if (!rawCompleted) {
|
|
5979
|
-
await client._publishAppEvent("group.message_created", notification);
|
|
6729
|
+
await client._publishAppEvent("group.message_created", notification, "push");
|
|
5980
6730
|
}
|
|
5981
6731
|
}
|
|
5982
6732
|
}
|
|
@@ -5990,11 +6740,16 @@ var MessageDeliveryEngine = class {
|
|
|
5990
6740
|
P2P_GAP_FILL_RETRY_MAX_MS
|
|
5991
6741
|
);
|
|
5992
6742
|
client._gapFillDone.add(retryKey);
|
|
6743
|
+
const releasePullWork = client._rpcPipeline?.reservePullWork?.() ?? (() => {
|
|
6744
|
+
});
|
|
5993
6745
|
client._clientLog.debug(`P2P message gap-fill retry scheduled: ns=${ns} attempt=${retryAttempt} delay_ms=${delayMs}`);
|
|
5994
6746
|
globalThis.setTimeout(() => {
|
|
5995
6747
|
client._gapFillDone.delete(retryKey);
|
|
5996
|
-
if (client.state !== "ready" /* READY */ || client._closing)
|
|
5997
|
-
|
|
6748
|
+
if (client.state !== "ready" /* READY */ || client._closing) {
|
|
6749
|
+
releasePullWork();
|
|
6750
|
+
return;
|
|
6751
|
+
}
|
|
6752
|
+
client._safeAsync(this.fillP2pGap(retryAttempt).finally(releasePullWork));
|
|
5998
6753
|
}, delayMs);
|
|
5999
6754
|
}
|
|
6000
6755
|
async fillP2pGap(retryAttempt = 0) {
|
|
@@ -6099,6 +6854,7 @@ var MessageDeliveryEngine = class {
|
|
|
6099
6854
|
client._gapFillDone.add(dedupKey);
|
|
6100
6855
|
this.runtime.delivery.setGapFillActive(true);
|
|
6101
6856
|
let continuationAfterSeq = 0;
|
|
6857
|
+
let failed = false;
|
|
6102
6858
|
try {
|
|
6103
6859
|
let nextAfterSeq = afterSeq;
|
|
6104
6860
|
const maxPages = singlePage ? 1 : 100;
|
|
@@ -6146,6 +6902,7 @@ var MessageDeliveryEngine = class {
|
|
|
6146
6902
|
}
|
|
6147
6903
|
const eventSeqs = [];
|
|
6148
6904
|
let hasDissolvedEvent = false;
|
|
6905
|
+
let publishedEventCount = 0;
|
|
6149
6906
|
for (const evt of eventObjects) {
|
|
6150
6907
|
const eventSeq = Number(evt.event_seq ?? 0);
|
|
6151
6908
|
if (Number.isFinite(eventSeq) && eventSeq > 0) eventSeqs.push(eventSeq);
|
|
@@ -6163,7 +6920,8 @@ var MessageDeliveryEngine = class {
|
|
|
6163
6920
|
}
|
|
6164
6921
|
}
|
|
6165
6922
|
if (Number.isFinite(eventSeq) && eventSeq > 0 && !client._pushedSeqs.get(ns)?.has(eventSeq)) {
|
|
6166
|
-
this.enqueueOrderedMessage(ns, "group.changed", eventSeq, evt);
|
|
6923
|
+
this.enqueueOrderedMessage(ns, "group.changed", eventSeq, evt, "pull");
|
|
6924
|
+
publishedEventCount += 1;
|
|
6167
6925
|
}
|
|
6168
6926
|
}
|
|
6169
6927
|
const ackContig = client._seqTracker.getContiguousSeq(ns);
|
|
@@ -6188,6 +6946,16 @@ var MessageDeliveryEngine = class {
|
|
|
6188
6946
|
client._clientLog.warn("group event auto-ack failed: group=" + groupId, e);
|
|
6189
6947
|
}
|
|
6190
6948
|
}
|
|
6949
|
+
this.onPullPage(
|
|
6950
|
+
ns,
|
|
6951
|
+
publishedEventCount,
|
|
6952
|
+
result.has_more === true,
|
|
6953
|
+
eventObjects.length > 0 ? Number(eventObjects[eventObjects.length - 1].event_seq ?? 0) : void 0,
|
|
6954
|
+
typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
|
|
6955
|
+
eventObjects.length,
|
|
6956
|
+
ackContig,
|
|
6957
|
+
eventObjects.length > 0 ? Number(eventObjects[eventObjects.length - 1].event_seq ?? 0) : void 0
|
|
6958
|
+
);
|
|
6191
6959
|
const nextAfter = Math.max(eventSeqs.length > 0 ? Math.max(...eventSeqs) : nextAfterSeq, nextAfterSeq);
|
|
6192
6960
|
if (singlePage && result.has_more === true && nextAfter > nextAfterSeq) {
|
|
6193
6961
|
continuationAfterSeq = nextAfter;
|
|
@@ -6199,10 +6967,12 @@ var MessageDeliveryEngine = class {
|
|
|
6199
6967
|
client._clientLog.warn(`group event gap fill reached max_pages=${maxPages} group=${groupId} after_seq=${nextAfterSeq}`);
|
|
6200
6968
|
}
|
|
6201
6969
|
} catch (exc) {
|
|
6970
|
+
failed = true;
|
|
6202
6971
|
client._clientLog.warn(`group event gap-fill failed:${String(exc)}`);
|
|
6203
6972
|
} finally {
|
|
6204
6973
|
client._gapFillDone.delete(dedupKey);
|
|
6205
6974
|
this.runtime.delivery.setGapFillActive(false);
|
|
6975
|
+
if (!client._rpcPipeline) this.syncStopped(failed ? "aborted" : "pull_drained");
|
|
6206
6976
|
if (singlePage && continuationAfterSeq > afterSeq) {
|
|
6207
6977
|
this.enqueueOnlineUnreadEventHint({
|
|
6208
6978
|
group_id: groupId,
|
|
@@ -6547,7 +7317,7 @@ var MessageDeliveryEngine = class {
|
|
|
6547
7317
|
const tracker = client._seqTracker;
|
|
6548
7318
|
const snapshot = typeof tracker.snapshotNamespace === "function" ? tracker.snapshotNamespace(ns) : null;
|
|
6549
7319
|
try {
|
|
6550
|
-
const published = await this.publishPulledMessage(event, ns, seq2, payload);
|
|
7320
|
+
const published = await this.publishPulledMessage(event, ns, seq2, payload, true, "inline_push");
|
|
6551
7321
|
if (!this.isInlineGenerationCurrent(generation)) return false;
|
|
6552
7322
|
if (!published) return false;
|
|
6553
7323
|
const needsPull = Boolean(tracker.onMessageSeq(ns, seq2));
|
|
@@ -7035,13 +7805,12 @@ var MessageDeliveryEngine = class {
|
|
|
7035
7805
|
}
|
|
7036
7806
|
}
|
|
7037
7807
|
} catch (exc) {
|
|
7038
|
-
client._dispatcher.
|
|
7808
|
+
client._dispatcher.enqueue("seq_tracker.persist_error", {
|
|
7039
7809
|
phase: "restore",
|
|
7040
7810
|
aid,
|
|
7041
7811
|
device_id: deviceId,
|
|
7042
7812
|
slot_id: slotId,
|
|
7043
7813
|
error: String(exc)
|
|
7044
|
-
}).catch(() => {
|
|
7045
7814
|
});
|
|
7046
7815
|
}
|
|
7047
7816
|
}
|
|
@@ -7232,13 +8001,12 @@ var MessageDeliveryEngine = class {
|
|
|
7232
8001
|
} catch (exc) {
|
|
7233
8002
|
const error = formatDeliveryError(exc);
|
|
7234
8003
|
client._clientLog.warn(`save SeqTracker state failed: ${error}`);
|
|
7235
|
-
client._dispatcher.
|
|
8004
|
+
client._dispatcher.enqueue("seq_tracker.persist_error", {
|
|
7236
8005
|
phase: "save",
|
|
7237
8006
|
aid,
|
|
7238
8007
|
device_id: deviceId,
|
|
7239
8008
|
slot_id: slotId,
|
|
7240
8009
|
error: String(error)
|
|
7241
|
-
}).catch(() => {
|
|
7242
8010
|
});
|
|
7243
8011
|
if (throwOnError) throw exc;
|
|
7244
8012
|
}
|
|
@@ -7343,7 +8111,7 @@ var MessageDeliveryEngine = class {
|
|
|
7343
8111
|
}
|
|
7344
8112
|
return params2;
|
|
7345
8113
|
}
|
|
7346
|
-
async drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true) {
|
|
8114
|
+
async drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true, batch, source = "pull") {
|
|
7347
8115
|
const client = this.runtime.client;
|
|
7348
8116
|
this.ensurePullOperationCurrent();
|
|
7349
8117
|
const queue = client._pendingOrderedMsgs.get(ns);
|
|
@@ -7351,15 +8119,39 @@ var MessageDeliveryEngine = class {
|
|
|
7351
8119
|
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
7352
8120
|
const ready = [...queue.keys()].filter((seq2) => seq2 <= contig && (beforeSeq === void 0 || seq2 < beforeSeq)).sort((a, b) => a - b);
|
|
7353
8121
|
let delivered = false;
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
const
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
8122
|
+
const drainBatches = /* @__PURE__ */ new Map();
|
|
8123
|
+
try {
|
|
8124
|
+
for (const seq2 of ready) {
|
|
8125
|
+
this.ensurePullOperationCurrent();
|
|
8126
|
+
const item = queue.get(seq2);
|
|
8127
|
+
queue.delete(seq2);
|
|
8128
|
+
if (!item || client._pushedSeqs.get(ns)?.has(seq2)) continue;
|
|
8129
|
+
const itemSource = item.source ?? source;
|
|
8130
|
+
let itemBatch = batch;
|
|
8131
|
+
if ((itemSource === "push" || itemSource === "inline_push") && (!batch || batch.trigger !== itemSource)) {
|
|
8132
|
+
itemBatch = drainBatches.get(itemSource);
|
|
8133
|
+
if (!itemBatch) {
|
|
8134
|
+
itemBatch = this.createDeliveryChangeBatch(itemSource);
|
|
8135
|
+
drainBatches.set(itemSource, itemBatch);
|
|
8136
|
+
}
|
|
8137
|
+
}
|
|
8138
|
+
await this.publishOrderedQueueItem(
|
|
8139
|
+
ns,
|
|
8140
|
+
item.event,
|
|
8141
|
+
seq2,
|
|
8142
|
+
item.payload,
|
|
8143
|
+
pullResponse,
|
|
8144
|
+
itemSource,
|
|
8145
|
+
itemBatch
|
|
8146
|
+
);
|
|
8147
|
+
this.ensurePullOperationCurrent();
|
|
8148
|
+
this.markPublishedSeq(ns, seq2);
|
|
8149
|
+
delivered = true;
|
|
8150
|
+
}
|
|
8151
|
+
} finally {
|
|
8152
|
+
for (const drainBatch of drainBatches.values()) {
|
|
8153
|
+
await this.flushRealtimeDeliveryChanges(drainBatch);
|
|
8154
|
+
}
|
|
7363
8155
|
}
|
|
7364
8156
|
if (queue.size === 0) {
|
|
7365
8157
|
client._pendingOrderedMsgs.delete(ns);
|
|
@@ -7369,75 +8161,87 @@ var MessageDeliveryEngine = class {
|
|
|
7369
8161
|
}
|
|
7370
8162
|
}
|
|
7371
8163
|
}
|
|
7372
|
-
async publishOrderedMessage(event, ns, seq2, payload) {
|
|
8164
|
+
async publishOrderedMessage(event, ns, seq2, payload, source = "push", operationBatch) {
|
|
7373
8165
|
const client = this.runtime.client;
|
|
7374
|
-
const
|
|
7375
|
-
|
|
7376
|
-
|
|
8166
|
+
const ownsBatch = !operationBatch && (source === "push" || source === "inline_push");
|
|
8167
|
+
const batch = operationBatch ?? (ownsBatch ? this.createDeliveryChangeBatch(source) : void 0);
|
|
8168
|
+
try {
|
|
8169
|
+
const seqNum = Number(seq2);
|
|
8170
|
+
if (!Number.isFinite(seqNum) || !Number.isInteger(seqNum) || seqNum <= 0) {
|
|
8171
|
+
await this.publishOrderedQueueItem(ns, event, seqNum, payload, false, source, batch);
|
|
8172
|
+
return true;
|
|
8173
|
+
}
|
|
8174
|
+
if (client._pushedSeqs.get(ns)?.has(seqNum)) {
|
|
8175
|
+
const queue2 = client._pendingOrderedMsgs.get(ns);
|
|
8176
|
+
queue2?.delete(seqNum);
|
|
8177
|
+
if (queue2 && queue2.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
8178
|
+
return false;
|
|
8179
|
+
}
|
|
8180
|
+
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
8181
|
+
if (seqNum > contig) {
|
|
8182
|
+
this.enqueueOrderedMessage(ns, event, seqNum, payload, source);
|
|
8183
|
+
return false;
|
|
8184
|
+
}
|
|
8185
|
+
await this.drainOrderedMessages(ns, seqNum, true, true, batch, source);
|
|
8186
|
+
if (client._pushedSeqs.get(ns)?.has(seqNum)) return false;
|
|
8187
|
+
const queue = client._pendingOrderedMsgs.get(ns);
|
|
8188
|
+
queue?.delete(seqNum);
|
|
8189
|
+
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
8190
|
+
await this.publishOrderedQueueItem(ns, event, seqNum, payload, false, source, batch);
|
|
8191
|
+
this.markPublishedSeq(ns, seqNum);
|
|
8192
|
+
await this.drainOrderedMessages(ns, void 0, false, true, batch, source);
|
|
8193
|
+
if (!client._pendingOrderedMsgs.get(ns)) await this.saveSeqTrackerState();
|
|
7377
8194
|
return true;
|
|
8195
|
+
} finally {
|
|
8196
|
+
if (ownsBatch && batch) await this.flushRealtimeDeliveryChanges(batch);
|
|
7378
8197
|
}
|
|
7379
|
-
if (client._pushedSeqs.get(ns)?.has(seqNum)) {
|
|
7380
|
-
const queue2 = client._pendingOrderedMsgs.get(ns);
|
|
7381
|
-
queue2?.delete(seqNum);
|
|
7382
|
-
if (queue2 && queue2.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7383
|
-
return false;
|
|
7384
|
-
}
|
|
7385
|
-
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
7386
|
-
if (seqNum > contig) {
|
|
7387
|
-
this.enqueueOrderedMessage(ns, event, seqNum, payload);
|
|
7388
|
-
return false;
|
|
7389
|
-
}
|
|
7390
|
-
await this.drainOrderedMessages(ns, seqNum, true);
|
|
7391
|
-
if (client._pushedSeqs.get(ns)?.has(seqNum)) return false;
|
|
7392
|
-
const queue = client._pendingOrderedMsgs.get(ns);
|
|
7393
|
-
queue?.delete(seqNum);
|
|
7394
|
-
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7395
|
-
await this.publishOrderedQueueItem(ns, event, seqNum, payload);
|
|
7396
|
-
this.markPublishedSeq(ns, seqNum);
|
|
7397
|
-
await this.drainOrderedMessages(ns);
|
|
7398
|
-
if (!client._pendingOrderedMsgs.get(ns)) await this.saveSeqTrackerState();
|
|
7399
|
-
return true;
|
|
7400
8198
|
}
|
|
7401
|
-
async publishPulledMessage(event, ns, seq2, payload, persist = true) {
|
|
8199
|
+
async publishPulledMessage(event, ns, seq2, payload, persist = true, source = "pull", operationBatch) {
|
|
7402
8200
|
const client = this.runtime.client;
|
|
7403
|
-
|
|
7404
|
-
const
|
|
7405
|
-
|
|
8201
|
+
const ownsBatch = !operationBatch && (source === "push" || source === "inline_push");
|
|
8202
|
+
const batch = operationBatch ?? (ownsBatch ? this.createDeliveryChangeBatch(source) : void 0);
|
|
8203
|
+
try {
|
|
8204
|
+
this.ensurePullOperationCurrent();
|
|
8205
|
+
const seqNum = Number(seq2);
|
|
8206
|
+
if (!Number.isFinite(seqNum) || !Number.isInteger(seqNum) || seqNum <= 0 || !ns) {
|
|
8207
|
+
if (event === "message.recalled") {
|
|
8208
|
+
const published = await client._withPullResponseProcessing(
|
|
8209
|
+
ns,
|
|
8210
|
+
() => this.publishMessageRecallTombstone(seq2, payload, source)
|
|
8211
|
+
);
|
|
8212
|
+
this.ensurePullOperationCurrent();
|
|
8213
|
+
return published;
|
|
8214
|
+
}
|
|
8215
|
+
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source, ns, batch));
|
|
8216
|
+
this.ensurePullOperationCurrent();
|
|
8217
|
+
return true;
|
|
8218
|
+
}
|
|
8219
|
+
const queue = client._pendingOrderedMsgs.get(ns);
|
|
8220
|
+
if (client._pushedSeqs.get(ns)?.has(seqNum)) {
|
|
8221
|
+
queue?.delete(seqNum);
|
|
8222
|
+
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
8223
|
+
return false;
|
|
8224
|
+
}
|
|
8225
|
+
await this.drainOrderedMessages(ns, seqNum, false, persist, batch, source);
|
|
8226
|
+
this.ensurePullOperationCurrent();
|
|
8227
|
+
queue?.delete(seqNum);
|
|
8228
|
+
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7406
8229
|
if (event === "message.recalled") {
|
|
7407
8230
|
const published = await client._withPullResponseProcessing(
|
|
7408
8231
|
ns,
|
|
7409
|
-
() => this.publishMessageRecallTombstone(
|
|
8232
|
+
() => this.publishMessageRecallTombstone(seqNum, payload, source)
|
|
7410
8233
|
);
|
|
7411
8234
|
this.ensurePullOperationCurrent();
|
|
8235
|
+
this.markPublishedSeq(ns, seqNum);
|
|
7412
8236
|
return published;
|
|
7413
8237
|
}
|
|
7414
|
-
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload));
|
|
7415
|
-
this.ensurePullOperationCurrent();
|
|
7416
|
-
return true;
|
|
7417
|
-
}
|
|
7418
|
-
const queue = client._pendingOrderedMsgs.get(ns);
|
|
7419
|
-
if (client._pushedSeqs.get(ns)?.has(seqNum)) {
|
|
7420
|
-
queue?.delete(seqNum);
|
|
7421
|
-
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7422
|
-
return false;
|
|
7423
|
-
}
|
|
7424
|
-
await this.drainOrderedMessages(ns, seqNum, false, persist);
|
|
7425
|
-
this.ensurePullOperationCurrent();
|
|
7426
|
-
queue?.delete(seqNum);
|
|
7427
|
-
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7428
|
-
if (event === "message.recalled") {
|
|
7429
|
-
const published = await client._withPullResponseProcessing(
|
|
7430
|
-
ns,
|
|
7431
|
-
() => this.publishMessageRecallTombstone(seqNum, payload)
|
|
7432
|
-
);
|
|
8238
|
+
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source, ns, batch));
|
|
7433
8239
|
this.ensurePullOperationCurrent();
|
|
7434
8240
|
this.markPublishedSeq(ns, seqNum);
|
|
7435
|
-
return
|
|
8241
|
+
return true;
|
|
8242
|
+
} finally {
|
|
8243
|
+
if (ownsBatch && batch) await this.flushRealtimeDeliveryChanges(batch);
|
|
7436
8244
|
}
|
|
7437
|
-
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload));
|
|
7438
|
-
this.ensurePullOperationCurrent();
|
|
7439
|
-
this.markPublishedSeq(ns, seqNum);
|
|
7440
|
-
return true;
|
|
7441
8245
|
}
|
|
7442
8246
|
};
|
|
7443
8247
|
|
|
@@ -7626,12 +8430,7 @@ var LifecycleController = class {
|
|
|
7626
8430
|
async publishConnectionEvent(owner, event, payload) {
|
|
7627
8431
|
const client = this.runtime.client;
|
|
7628
8432
|
this.assertConnectionAttemptOwner(owner);
|
|
7629
|
-
client.
|
|
7630
|
-
try {
|
|
7631
|
-
await client._dispatcher.publish(event, payload);
|
|
7632
|
-
} finally {
|
|
7633
|
-
client._connectEventDispatchDepth -= 1;
|
|
7634
|
-
}
|
|
8433
|
+
client._dispatcher.enqueue(event, payload);
|
|
7635
8434
|
return this.ownsConnectionAttempt(owner);
|
|
7636
8435
|
}
|
|
7637
8436
|
async cancelConnectionAttemptAndWait() {
|
|
@@ -7939,14 +8738,9 @@ var LifecycleController = class {
|
|
|
7939
8738
|
client._clientLog.warn(`reentrant close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7940
8739
|
});
|
|
7941
8740
|
}
|
|
7942
|
-
|
|
8741
|
+
publishLifecycleStopEvent(payload) {
|
|
7943
8742
|
const client = this.runtime.client;
|
|
7944
|
-
client.
|
|
7945
|
-
try {
|
|
7946
|
-
await client._dispatcher.publish("state_change", payload);
|
|
7947
|
-
} finally {
|
|
7948
|
-
client._lifecycleStopEventDispatchDepth -= 1;
|
|
7949
|
-
}
|
|
8743
|
+
client._dispatcher.enqueue("state_change", payload);
|
|
7950
8744
|
}
|
|
7951
8745
|
async withLifecycleStop(operation, kind) {
|
|
7952
8746
|
const client = this.runtime.client;
|
|
@@ -8007,7 +8801,7 @@ var LifecycleController = class {
|
|
|
8007
8801
|
client._stopBackgroundTasks();
|
|
8008
8802
|
if (client._closing) return;
|
|
8009
8803
|
this.runtime.lifecycle.resetForDisconnect("standby");
|
|
8010
|
-
|
|
8804
|
+
this.publishLifecycleStopEvent({ state: client._publicState(client._state) });
|
|
8011
8805
|
client._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms`);
|
|
8012
8806
|
}, "disconnect");
|
|
8013
8807
|
} finally {
|
|
@@ -8017,6 +8811,7 @@ var LifecycleController = class {
|
|
|
8017
8811
|
async close() {
|
|
8018
8812
|
const client = this.runtime.client;
|
|
8019
8813
|
const tStart = Date.now();
|
|
8814
|
+
const calledFromHandler = client._dispatcher.isDispatchingHandler();
|
|
8020
8815
|
client._clientLog.debug(`close enter: state=${client._state}`);
|
|
8021
8816
|
this.runtime.lifecycle.setClosing(true);
|
|
8022
8817
|
client._delivery.resetInlineAckState();
|
|
@@ -8025,34 +8820,39 @@ var LifecycleController = class {
|
|
|
8025
8820
|
return;
|
|
8026
8821
|
}
|
|
8027
8822
|
return this.withLifecycleStop(async () => {
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8031
|
-
|
|
8032
|
-
|
|
8033
|
-
|
|
8034
|
-
|
|
8035
|
-
|
|
8823
|
+
try {
|
|
8824
|
+
const pullInvalidation = client._rpcPipeline?.invalidatePulls?.();
|
|
8825
|
+
client._rpcPipeline?.stopPullGateWatchdogs?.();
|
|
8826
|
+
client._stopBackgroundTasks();
|
|
8827
|
+
if (client._state === "idle" || client._state === "closed") {
|
|
8828
|
+
const reconnectCancellation2 = client._cancelReconnectAndWait();
|
|
8829
|
+
const connectionCancellation2 = this.cancelConnectionAttemptAndWait();
|
|
8830
|
+
await Promise.all([reconnectCancellation2, connectionCancellation2]);
|
|
8831
|
+
await pullInvalidation;
|
|
8832
|
+
await client._saveSeqTrackerState();
|
|
8833
|
+
this.runtime.lifecycle.setState("closed");
|
|
8834
|
+
client._resetSeqTrackingState();
|
|
8835
|
+
client._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms reason=already_idle`);
|
|
8836
|
+
return;
|
|
8837
|
+
}
|
|
8838
|
+
const reconnectCancellation = client._cancelReconnectAndWait();
|
|
8839
|
+
const connectionCancellation = this.cancelConnectionAttemptAndWait();
|
|
8840
|
+
try {
|
|
8841
|
+
await client._transport.call("auth.logout", {});
|
|
8842
|
+
} catch (err) {
|
|
8843
|
+
client._clientLog.warn(`auth.logout during close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8844
|
+
}
|
|
8845
|
+
await client._transport.close();
|
|
8846
|
+
await Promise.all([pullInvalidation, reconnectCancellation, connectionCancellation]);
|
|
8036
8847
|
await client._saveSeqTrackerState();
|
|
8037
8848
|
this.runtime.lifecycle.setState("closed");
|
|
8849
|
+
this.publishLifecycleStopEvent({ state: client._publicState(client._state) });
|
|
8038
8850
|
client._resetSeqTrackingState();
|
|
8039
|
-
client._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms
|
|
8040
|
-
|
|
8851
|
+
client._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms`);
|
|
8852
|
+
} finally {
|
|
8853
|
+
const closing = client._dispatcher.close();
|
|
8854
|
+
if (!calledFromHandler) await closing;
|
|
8041
8855
|
}
|
|
8042
|
-
const reconnectCancellation = client._cancelReconnectAndWait();
|
|
8043
|
-
const connectionCancellation = this.cancelConnectionAttemptAndWait();
|
|
8044
|
-
try {
|
|
8045
|
-
await client._transport.call("auth.logout", {});
|
|
8046
|
-
} catch (err) {
|
|
8047
|
-
client._clientLog.warn(`auth.logout during close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8048
|
-
}
|
|
8049
|
-
await client._transport.close();
|
|
8050
|
-
await Promise.all([pullInvalidation, reconnectCancellation, connectionCancellation]);
|
|
8051
|
-
await client._saveSeqTrackerState();
|
|
8052
|
-
this.runtime.lifecycle.setState("closed");
|
|
8053
|
-
await this.publishLifecycleStopEvent({ state: client._publicState(client._state) });
|
|
8054
|
-
client._resetSeqTrackingState();
|
|
8055
|
-
client._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms`);
|
|
8056
8856
|
}, "close");
|
|
8057
8857
|
}
|
|
8058
8858
|
};
|
|
@@ -14849,6 +15649,14 @@ var PROTECTED_HEADERS_METHODS = /* @__PURE__ */ new Set([
|
|
|
14849
15649
|
"message.thought.put",
|
|
14850
15650
|
"group.thought.put"
|
|
14851
15651
|
]);
|
|
15652
|
+
function generateMessageId() {
|
|
15653
|
+
if (typeof crypto.randomUUID === "function") return `m-${crypto.randomUUID().replace(/-/g, "")}`;
|
|
15654
|
+
const bytes = new Uint8Array(16);
|
|
15655
|
+
crypto.getRandomValues(bytes);
|
|
15656
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
15657
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
15658
|
+
return `m-${Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join("")}`;
|
|
15659
|
+
}
|
|
14852
15660
|
var SIGNED_METHODS = /* @__PURE__ */ new Set([
|
|
14853
15661
|
"message.send",
|
|
14854
15662
|
"message.v2.put_peer_pk",
|
|
@@ -15111,6 +15919,7 @@ var RpcPipeline = class {
|
|
|
15111
15919
|
__publicField(this, "runtime");
|
|
15112
15920
|
__publicField(this, "pullGateStates", /* @__PURE__ */ new Map());
|
|
15113
15921
|
__publicField(this, "inlineRealtimeScopes", /* @__PURE__ */ new Set());
|
|
15922
|
+
__publicField(this, "pullWorkTokens", /* @__PURE__ */ new Set());
|
|
15114
15923
|
__publicField(this, "pullGeneration", 0);
|
|
15115
15924
|
__publicField(this, "pullInvalidationWait", null);
|
|
15116
15925
|
this.runtime = runtime;
|
|
@@ -15318,6 +16127,9 @@ var RpcPipeline = class {
|
|
|
15318
16127
|
delete p._rpc_background;
|
|
15319
16128
|
if (method === "message.send" || method === "group.send") {
|
|
15320
16129
|
this.normalizeOutboundMessagePayload(p, method);
|
|
16130
|
+
if (!Object.prototype.hasOwnProperty.call(p, "message_id") || p.message_id == null) {
|
|
16131
|
+
p.message_id = generateMessageId();
|
|
16132
|
+
}
|
|
15321
16133
|
}
|
|
15322
16134
|
this.normalizeGroupCallIdentifier(method, p);
|
|
15323
16135
|
this.validateOutboundCall(method, p);
|
|
@@ -15622,6 +16434,28 @@ var RpcPipeline = class {
|
|
|
15622
16434
|
const matches = (job) => job !== null && job.namespace === ns;
|
|
15623
16435
|
return matches(state.active) || state.foreground.some(matches) || state.background.some(matches);
|
|
15624
16436
|
}
|
|
16437
|
+
hasAnyPullActivity() {
|
|
16438
|
+
if (this.pullWorkTokens.size > 0) return true;
|
|
16439
|
+
for (const state of this.pullGateStates.values()) {
|
|
16440
|
+
if (state.active || state.foreground.length > 0 || state.background.length > 0) return true;
|
|
16441
|
+
}
|
|
16442
|
+
return false;
|
|
16443
|
+
}
|
|
16444
|
+
reservePullWork() {
|
|
16445
|
+
const token = Symbol("pull-work");
|
|
16446
|
+
this.pullWorkTokens.add(token);
|
|
16447
|
+
let released = false;
|
|
16448
|
+
return () => {
|
|
16449
|
+
if (released) return;
|
|
16450
|
+
released = true;
|
|
16451
|
+
this.pullWorkTokens.delete(token);
|
|
16452
|
+
this.runtime.client._delivery?.onPullWorkSettled?.();
|
|
16453
|
+
};
|
|
16454
|
+
}
|
|
16455
|
+
releasePullWorkToken(token) {
|
|
16456
|
+
if (!this.pullWorkTokens.delete(token)) return;
|
|
16457
|
+
this.runtime.client._delivery?.onPullWorkSettled?.();
|
|
16458
|
+
}
|
|
15625
16459
|
async tryRunInlineRealtime(ns, seq2, operation) {
|
|
15626
16460
|
const normalized = String(ns ?? "").trim();
|
|
15627
16461
|
if (!normalized.startsWith("p2p:") && !normalized.startsWith("group:") || !Number.isSafeInteger(seq2) || seq2 <= 0 || typeof operation !== "function") {
|
|
@@ -15663,7 +16497,8 @@ var RpcPipeline = class {
|
|
|
15663
16497
|
invalidated: false,
|
|
15664
16498
|
cancel: null,
|
|
15665
16499
|
settled: Promise.resolve(),
|
|
15666
|
-
settledResolve: null
|
|
16500
|
+
settledResolve: null,
|
|
16501
|
+
workToken: Symbol("inline-pull-work")
|
|
15667
16502
|
};
|
|
15668
16503
|
this.inlineRealtimeScopes.add(normalized);
|
|
15669
16504
|
try {
|
|
@@ -15725,6 +16560,7 @@ var RpcPipeline = class {
|
|
|
15725
16560
|
if (job.running) {
|
|
15726
16561
|
waits.push(job.settled);
|
|
15727
16562
|
} else {
|
|
16563
|
+
this.releasePullWorkToken(job.workToken);
|
|
15728
16564
|
job.settledResolve?.();
|
|
15729
16565
|
job.settledResolve = null;
|
|
15730
16566
|
}
|
|
@@ -15737,6 +16573,7 @@ var RpcPipeline = class {
|
|
|
15737
16573
|
this.drainPullGate(state);
|
|
15738
16574
|
}
|
|
15739
16575
|
const wait = Promise.all(waits).then(() => void 0);
|
|
16576
|
+
this.runtime.client._delivery?.onPullWorkSettled?.();
|
|
15740
16577
|
let tracked;
|
|
15741
16578
|
tracked = wait.finally(() => {
|
|
15742
16579
|
if (this.pullInvalidationWait === tracked) this.pullInvalidationWait = null;
|
|
@@ -15751,7 +16588,18 @@ var RpcPipeline = class {
|
|
|
15751
16588
|
if (this.pullInvalidationInProgress()) {
|
|
15752
16589
|
throw new Error("pull invalidated");
|
|
15753
16590
|
}
|
|
15754
|
-
if (!key)
|
|
16591
|
+
if (!key) {
|
|
16592
|
+
this.runtime.client._delivery?.onPullStarted?.("");
|
|
16593
|
+
const releasePullWork = this.reservePullWork();
|
|
16594
|
+
try {
|
|
16595
|
+
return await this.executePullOperation(operation, background);
|
|
16596
|
+
} catch (error) {
|
|
16597
|
+
this.runtime.client._delivery?.onPullAborted?.();
|
|
16598
|
+
throw error;
|
|
16599
|
+
} finally {
|
|
16600
|
+
releasePullWork();
|
|
16601
|
+
}
|
|
16602
|
+
}
|
|
15755
16603
|
const state = this.pullGateState(key);
|
|
15756
16604
|
const namespace = this.pullScopeKey(key);
|
|
15757
16605
|
const candidate = state.byKey.get(key);
|
|
@@ -15765,6 +16613,8 @@ var RpcPipeline = class {
|
|
|
15765
16613
|
}
|
|
15766
16614
|
return await existing.promise;
|
|
15767
16615
|
}
|
|
16616
|
+
const workToken = Symbol("pull-work");
|
|
16617
|
+
this.pullWorkTokens.add(workToken);
|
|
15768
16618
|
let resolve;
|
|
15769
16619
|
let reject;
|
|
15770
16620
|
let settledResolve;
|
|
@@ -15795,7 +16645,8 @@ var RpcPipeline = class {
|
|
|
15795
16645
|
invalidated: false,
|
|
15796
16646
|
cancel: null,
|
|
15797
16647
|
settled,
|
|
15798
|
-
settledResolve
|
|
16648
|
+
settledResolve,
|
|
16649
|
+
workToken
|
|
15799
16650
|
};
|
|
15800
16651
|
(background ? state.background : state.foreground).push(job);
|
|
15801
16652
|
state.byKey.set(key, job);
|
|
@@ -15869,6 +16720,7 @@ var RpcPipeline = class {
|
|
|
15869
16720
|
const job = takeRunnable(state.foreground) ?? takeRunnable(state.background);
|
|
15870
16721
|
if (!job) return;
|
|
15871
16722
|
state.active = job;
|
|
16723
|
+
this.runtime.client._delivery?.onPullStarted?.(job.namespace);
|
|
15872
16724
|
if (job.timer) clearTimeout(job.timer);
|
|
15873
16725
|
job.pullingStartedAt = Date.now();
|
|
15874
16726
|
job.timedOut = false;
|
|
@@ -15882,14 +16734,17 @@ var RpcPipeline = class {
|
|
|
15882
16734
|
return;
|
|
15883
16735
|
}
|
|
15884
16736
|
job.running = true;
|
|
16737
|
+
let failed = false;
|
|
15885
16738
|
void this.executePullOperation(job.operation, job.background).then(
|
|
15886
16739
|
(value) => {
|
|
15887
16740
|
if (!job.timedOut && !job.invalidated) job.resolve(value);
|
|
15888
16741
|
},
|
|
15889
16742
|
(error) => {
|
|
16743
|
+
failed = true;
|
|
15890
16744
|
if (!job.timedOut && !job.invalidated) job.reject(error);
|
|
15891
16745
|
}
|
|
15892
16746
|
).finally(() => {
|
|
16747
|
+
this.pullWorkTokens.delete(job.workToken);
|
|
15893
16748
|
state.lifecycle.set(job.namespace, "idle");
|
|
15894
16749
|
if (state.active === job) state.active = null;
|
|
15895
16750
|
if (state.byKey.get(job.key) === job) state.byKey.delete(job.key);
|
|
@@ -15897,6 +16752,8 @@ var RpcPipeline = class {
|
|
|
15897
16752
|
job.settledResolve?.();
|
|
15898
16753
|
job.settledResolve = null;
|
|
15899
16754
|
if (!job.invalidated) this.runtime.client._delivery?.onPullGateIdle?.(job.namespace);
|
|
16755
|
+
if (failed || job.invalidated) this.runtime.client._delivery?.onPullAborted?.();
|
|
16756
|
+
this.runtime.client._delivery?.onPullWorkSettled?.();
|
|
15900
16757
|
});
|
|
15901
16758
|
}
|
|
15902
16759
|
armQueuedPullTimers(_state) {
|
|
@@ -23793,11 +24650,22 @@ var V2E2EECoordinator = class {
|
|
|
23793
24650
|
const fetchKey = this.pendingSenderIKFetchKey(fromAid, senderDeviceId, groupId);
|
|
23794
24651
|
if (!fromAid || client._v2SenderIKFetching.has(fetchKey)) return;
|
|
23795
24652
|
client._v2SenderIKFetching.add(fetchKey);
|
|
23796
|
-
client.
|
|
24653
|
+
const releasePullWork = client._rpcPipeline?.reservePullWork?.() ?? (() => {
|
|
24654
|
+
});
|
|
24655
|
+
const generation = client._delivery.captureInlineGeneration?.();
|
|
24656
|
+
client._safeAsync(this.resolveSenderIKPending(
|
|
24657
|
+
fromAid,
|
|
24658
|
+
senderDeviceId,
|
|
24659
|
+
groupId,
|
|
24660
|
+
fetchKey,
|
|
24661
|
+
generation
|
|
24662
|
+
).finally(releasePullWork));
|
|
23797
24663
|
}
|
|
23798
|
-
async resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey) {
|
|
24664
|
+
async resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey, generation) {
|
|
23799
24665
|
const client = this.client;
|
|
24666
|
+
const generationCurrent = () => generation === void 0 || client._delivery.isInlineGenerationCurrent?.(generation) !== false;
|
|
23800
24667
|
try {
|
|
24668
|
+
if (!generationCurrent()) return;
|
|
23801
24669
|
const session = client._v2Session;
|
|
23802
24670
|
if (session && fromAid) {
|
|
23803
24671
|
try {
|
|
@@ -23828,17 +24696,21 @@ var V2E2EECoordinator = class {
|
|
|
23828
24696
|
await this.getV2SenderPubDer(fromAid, senderDeviceId);
|
|
23829
24697
|
}
|
|
23830
24698
|
}
|
|
24699
|
+
if (!generationCurrent()) return;
|
|
23831
24700
|
const pendingItems = [...client._v2SenderIKPending.entries()].filter(([, entry]) => entry.fromAid === fromAid && entry.senderDeviceId === senderDeviceId && entry.groupId === groupId);
|
|
23832
24701
|
for (const [key, entry] of pendingItems) {
|
|
24702
|
+
if (!generationCurrent()) return;
|
|
23833
24703
|
let plaintext = null;
|
|
23834
24704
|
const retryStatus = {};
|
|
23835
24705
|
try {
|
|
23836
|
-
plaintext = await client._decryptV2Message(entry.msg, false, true, true, true, retryStatus);
|
|
24706
|
+
plaintext = await client._decryptV2Message(entry.msg, false, true, true, true, retryStatus, false, "pending_retry");
|
|
23837
24707
|
} catch (exc) {
|
|
24708
|
+
if (!generationCurrent()) return;
|
|
23838
24709
|
client._clientLog.warn(`V2 sender IK pending retry raised: key=${key} err=${String(formatE2EEError(exc))}`);
|
|
23839
24710
|
client._v2SenderIKPending.delete(key);
|
|
23840
24711
|
continue;
|
|
23841
24712
|
}
|
|
24713
|
+
if (!generationCurrent()) return;
|
|
23842
24714
|
if (plaintext === null) {
|
|
23843
24715
|
if (retryStatus.deferred) {
|
|
23844
24716
|
client._clientLog.warn(`V2 pending retry still missing key material: key=${key}`);
|
|
@@ -23858,9 +24730,9 @@ var V2E2EECoordinator = class {
|
|
|
23858
24730
|
if (entry.groupId) {
|
|
23859
24731
|
plaintext = normalizeGroupMentionMode(plaintext, entry.msg, entry.msg.envelope_json);
|
|
23860
24732
|
plaintext.group_id = entry.groupId;
|
|
23861
|
-
await client._publishPulledMessage("group.message_created", `group:${entry.groupId}`, seq2, plaintext);
|
|
24733
|
+
await client._publishPulledMessage("group.message_created", `group:${entry.groupId}`, seq2, plaintext, false, "pending_retry");
|
|
23862
24734
|
} else {
|
|
23863
|
-
await client._publishPulledMessage("message.received", `p2p:${client._aid ?? ""}`, seq2, plaintext);
|
|
24735
|
+
await client._publishPulledMessage("message.received", `p2p:${client._aid ?? ""}`, seq2, plaintext, false, "pending_retry");
|
|
23864
24736
|
}
|
|
23865
24737
|
client._clientLog.debug(`V2 sender IK pending retry delivered: key=${key}`);
|
|
23866
24738
|
}
|
|
@@ -23890,6 +24762,7 @@ var V2E2EECoordinator = class {
|
|
|
23890
24762
|
return client.call("message.send", {
|
|
23891
24763
|
to: toAid,
|
|
23892
24764
|
payload: envelope,
|
|
24765
|
+
message_id: opts?.messageId,
|
|
23893
24766
|
encrypt: false,
|
|
23894
24767
|
_skip_send_result_envelope: true
|
|
23895
24768
|
});
|
|
@@ -23936,9 +24809,9 @@ var V2E2EECoordinator = class {
|
|
|
23936
24809
|
let plaintext = null;
|
|
23937
24810
|
if (String(msg.version ?? "") === "v1") {
|
|
23938
24811
|
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
23939
|
-
const payload = legacy.payload;
|
|
24812
|
+
const payload = legacy.payload !== void 0 ? legacy.payload : msg.payload;
|
|
23940
24813
|
const payloadType = isJsonObject(payload) ? String(payload.type ?? "").trim() : "";
|
|
23941
|
-
if (payload !== void 0 && payload !== null && !["e2ee.encrypted", "e2ee.group_encrypted"].includes(payloadType)) {
|
|
24814
|
+
if (payload !== void 0 && payload !== null && !["e2ee.encrypted", "e2ee.group_encrypted", "e2ee.p2p_encrypted"].includes(payloadType)) {
|
|
23942
24815
|
plaintext = {
|
|
23943
24816
|
message_id: String(msg.message_id ?? ""),
|
|
23944
24817
|
from: String(msg.from_aid ?? ""),
|
|
@@ -23950,6 +24823,9 @@ var V2E2EECoordinator = class {
|
|
|
23950
24823
|
encrypted: false
|
|
23951
24824
|
};
|
|
23952
24825
|
attachGatewayProximity(plaintext, msg);
|
|
24826
|
+
} else if (isJsonObject(payload) && ["e2ee.p2p_encrypted", "e2ee.encrypted"].includes(payloadType)) {
|
|
24827
|
+
msg = { ...msg, envelope_json: JSON.stringify(payload) };
|
|
24828
|
+
plaintext = await client._decryptV2Message(msg, false, !history, true, true, void 0, false, source);
|
|
23953
24829
|
}
|
|
23954
24830
|
} else {
|
|
23955
24831
|
plaintext = source === "inline_push" ? await client._decryptV2Message(msg, false, false, false, false) : await client._decryptV2Message(msg, false);
|
|
@@ -23966,7 +24842,8 @@ var V2E2EECoordinator = class {
|
|
|
23966
24842
|
event.event,
|
|
23967
24843
|
client._aid ? `p2p:${client._aid}` : "",
|
|
23968
24844
|
seq2,
|
|
23969
|
-
event.payload
|
|
24845
|
+
event.payload,
|
|
24846
|
+
source
|
|
23970
24847
|
);
|
|
23971
24848
|
} else {
|
|
23972
24849
|
await client._publishAppEvent(event.event, event.payload, source);
|
|
@@ -23980,29 +24857,53 @@ var V2E2EECoordinator = class {
|
|
|
23980
24857
|
let plaintext = null;
|
|
23981
24858
|
if (String(msg.version ?? "") === "v1") {
|
|
23982
24859
|
const payload = msg.payload;
|
|
24860
|
+
const recall = typeof client._delivery?.recallEventFromGroupMessage === "function" ? client._delivery.recallEventFromGroupMessage(msg) : null;
|
|
24861
|
+
if (recall) {
|
|
24862
|
+
const recallPayload = {
|
|
24863
|
+
...recall,
|
|
24864
|
+
group_id: groupId,
|
|
24865
|
+
group_aid: groupAid,
|
|
24866
|
+
seq: seq2
|
|
24867
|
+
};
|
|
24868
|
+
if (publish) {
|
|
24869
|
+
if (orderedPublish) {
|
|
24870
|
+
await client._delivery.publishOrderedMessage(
|
|
24871
|
+
"group.message_recalled",
|
|
24872
|
+
`group:${groupId}`,
|
|
24873
|
+
seq2,
|
|
24874
|
+
recallPayload,
|
|
24875
|
+
source
|
|
24876
|
+
);
|
|
24877
|
+
} else {
|
|
24878
|
+
await client._publishAppEvent("group.message_recalled", recallPayload, source);
|
|
24879
|
+
}
|
|
24880
|
+
}
|
|
24881
|
+
return recallPayload;
|
|
24882
|
+
}
|
|
23983
24883
|
if (payload === void 0 || payload === null) {
|
|
23984
24884
|
if (history) return this.historyDecryptFailure(msg, "legacy payload is missing");
|
|
23985
24885
|
client._clientLog.warn(`Group Tail \u7F3A\u5C11 payload \u7684 V1 \u884C\u5DF2\u8DF3\u8FC7\uFF0C\u8FDE\u7EED\u6027\u8BC1\u660E\u4ECD\u4FDD\u7559: group=${groupId}, seq=${seq2}`);
|
|
23986
24886
|
return null;
|
|
23987
24887
|
}
|
|
23988
|
-
|
|
23989
|
-
|
|
23990
|
-
|
|
23991
|
-
|
|
24888
|
+
const payloadType = isJsonObject(payload) ? String(payload.type ?? "").trim() : "";
|
|
24889
|
+
if (isJsonObject(payload) && ["e2ee.group_encrypted", "e2ee.p2p_encrypted", "e2ee.encrypted"].includes(payloadType)) {
|
|
24890
|
+
msg = { ...msg, envelope_json: JSON.stringify(payload) };
|
|
24891
|
+
plaintext = await client._decryptV2Message(msg, false, !history, true, true, void 0, false, source);
|
|
24892
|
+
} else {
|
|
24893
|
+
plaintext = {
|
|
24894
|
+
message_id: String(msg.message_id ?? ""),
|
|
24895
|
+
from: String(msg.from_aid ?? ""),
|
|
24896
|
+
group_id: groupId,
|
|
24897
|
+
group_aid: groupAid,
|
|
24898
|
+
seq: seq2,
|
|
24899
|
+
type: String(msg.type ?? ""),
|
|
24900
|
+
timestamp: msg.t_server,
|
|
24901
|
+
payload,
|
|
24902
|
+
encrypted: false
|
|
24903
|
+
};
|
|
24904
|
+
plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
|
|
24905
|
+
attachGatewayProximity(plaintext, msg);
|
|
23992
24906
|
}
|
|
23993
|
-
plaintext = {
|
|
23994
|
-
message_id: String(msg.message_id ?? ""),
|
|
23995
|
-
from: String(msg.from_aid ?? ""),
|
|
23996
|
-
group_id: groupId,
|
|
23997
|
-
group_aid: groupAid,
|
|
23998
|
-
seq: seq2,
|
|
23999
|
-
type: String(msg.type ?? ""),
|
|
24000
|
-
timestamp: msg.t_server,
|
|
24001
|
-
payload,
|
|
24002
|
-
encrypted: false
|
|
24003
|
-
};
|
|
24004
|
-
plaintext = normalizeGroupMentionMode(plaintext, msg, msg.envelope_json);
|
|
24005
|
-
attachGatewayProximity(plaintext, msg);
|
|
24006
24907
|
} else {
|
|
24007
24908
|
plaintext = source === "inline_push" ? await client._decryptV2Message(msg, false, false, false, false) : await client._decryptV2Message(msg, false);
|
|
24008
24909
|
if (plaintext) {
|
|
@@ -24025,7 +24926,8 @@ var V2E2EECoordinator = class {
|
|
|
24025
24926
|
"group.message_created",
|
|
24026
24927
|
`group:${groupId}`,
|
|
24027
24928
|
seq2,
|
|
24028
|
-
plaintext
|
|
24929
|
+
plaintext,
|
|
24930
|
+
source
|
|
24029
24931
|
);
|
|
24030
24932
|
} else {
|
|
24031
24933
|
await client._publishAppEvent("group.message_created", plaintext, source);
|
|
@@ -24053,6 +24955,7 @@ var V2E2EECoordinator = class {
|
|
|
24053
24955
|
const afterSeq = strictWindowSeq(params2.after_seq ?? 0, "after_seq");
|
|
24054
24956
|
const limit = strictWindowLimit(params2.limit ?? V2_PULL_DEFAULT_PAGE_LIMIT);
|
|
24055
24957
|
const ns = client._aid ? `p2p:${client._aid}` : "";
|
|
24958
|
+
if (ns) client._delivery.onPullStarted?.(ns);
|
|
24056
24959
|
const result = await client._callRawV2Rpc("message.v2.pull", { window_mode: "tail", after_seq: afterSeq, limit, _rpc_foreground: true });
|
|
24057
24960
|
const floor = Number(result.retention_floor_seq ?? 0);
|
|
24058
24961
|
const previous = ns ? client._seqTracker.getSyncState(ns) : null;
|
|
@@ -24075,6 +24978,17 @@ var V2E2EECoordinator = class {
|
|
|
24075
24978
|
}
|
|
24076
24979
|
const ack = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
24077
24980
|
if (previous && ack > previous.ack) await this.ackV2(ack);
|
|
24981
|
+
if (ns) client._delivery.onPullPage?.(
|
|
24982
|
+
ns,
|
|
24983
|
+
decoded.length,
|
|
24984
|
+
result.has_more === true,
|
|
24985
|
+
page.head,
|
|
24986
|
+
typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
|
|
24987
|
+
page.messages.length,
|
|
24988
|
+
ack,
|
|
24989
|
+
page.head
|
|
24990
|
+
);
|
|
24991
|
+
client._delivery.onPullWorkSettled?.();
|
|
24078
24992
|
return { ...result, messages: decoded, raw_count: page.messages.length };
|
|
24079
24993
|
}
|
|
24080
24994
|
async historyV2Internal(params2) {
|
|
@@ -24089,10 +25003,24 @@ var V2E2EECoordinator = class {
|
|
|
24089
25003
|
}
|
|
24090
25004
|
return { ...result, messages: decoded };
|
|
24091
25005
|
}
|
|
25006
|
+
async runPullWithLifecycle(ns, operation) {
|
|
25007
|
+
if (ns) this.client._delivery.onPullStarted?.(ns);
|
|
25008
|
+
try {
|
|
25009
|
+
return await operation();
|
|
25010
|
+
} catch (error) {
|
|
25011
|
+
this.client._delivery.onPullAborted?.();
|
|
25012
|
+
throw error;
|
|
25013
|
+
}
|
|
25014
|
+
}
|
|
24092
25015
|
async pullV2(afterSeq = 0, limit = 50, opts) {
|
|
25016
|
+
const ns = this.client._aid ? `p2p:${this.client._aid}` : "";
|
|
25017
|
+
return await this.runPullWithLifecycle(ns, () => this.pullV2Impl(afterSeq, limit, opts));
|
|
25018
|
+
}
|
|
25019
|
+
async pullV2Impl(afterSeq = 0, limit = 50, opts) {
|
|
24093
25020
|
const client = this.client;
|
|
24094
25021
|
await client._ensureV2SessionReady("message.pull");
|
|
24095
25022
|
const ns = client._aid ? `p2p:${client._aid}` : "";
|
|
25023
|
+
if (ns) client._delivery.onPullStarted?.(ns);
|
|
24096
25024
|
if (opts?.windowMode === "tail") {
|
|
24097
25025
|
if (!opts.gateLocked) {
|
|
24098
25026
|
const key = pullGateKeyForClient(client, "message.v2.pull", {
|
|
@@ -24100,7 +25028,7 @@ var V2E2EECoordinator = class {
|
|
|
24100
25028
|
after_seq: afterSeq,
|
|
24101
25029
|
limit
|
|
24102
25030
|
}, `${ns}|tail|after=${afterSeq}|limit=${limit}`);
|
|
24103
|
-
return await client._runPullSerialized(key, () => this.
|
|
25031
|
+
return await client._runPullSerialized(key, () => this.pullV2Impl(afterSeq, limit, {
|
|
24104
25032
|
...opts ?? {},
|
|
24105
25033
|
gateLocked: true
|
|
24106
25034
|
}), false);
|
|
@@ -24114,7 +25042,7 @@ var V2E2EECoordinator = class {
|
|
|
24114
25042
|
force: opts?.force === true,
|
|
24115
25043
|
limit
|
|
24116
25044
|
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
24117
|
-
return await client._runPullSerialized(key, () => this.
|
|
25045
|
+
return await client._runPullSerialized(key, () => this.pullV2Impl(afterSeq, limit, {
|
|
24118
25046
|
...opts ?? {},
|
|
24119
25047
|
gateLocked: true
|
|
24120
25048
|
}), true);
|
|
@@ -24171,15 +25099,16 @@ var V2E2EECoordinator = class {
|
|
|
24171
25099
|
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
24172
25100
|
const deferredKeyFetches = /* @__PURE__ */ new Map();
|
|
24173
25101
|
let blockedSeq = 0;
|
|
25102
|
+
const pageDecryptedBefore = decrypted.length;
|
|
24174
25103
|
for (const msg of messages) {
|
|
24175
25104
|
const seq2 = Number(msg.seq ?? 0);
|
|
24176
25105
|
if (!Number.isFinite(seq2) || seq2 <= 0) continue;
|
|
24177
25106
|
const version = String(msg.version ?? "v2");
|
|
24178
25107
|
if (version === "v1") {
|
|
24179
25108
|
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
24180
|
-
const legacyPayload = legacy.payload;
|
|
25109
|
+
const legacyPayload = legacy.payload !== void 0 ? legacy.payload : msg.payload;
|
|
24181
25110
|
const payloadType = isJsonObject(legacyPayload) ? String(legacyPayload.type ?? "").trim() : "";
|
|
24182
|
-
if (legacyPayload !== void 0 && legacyPayload !== null &&
|
|
25111
|
+
if (legacyPayload !== void 0 && legacyPayload !== null && !["e2ee.encrypted", "e2ee.group_encrypted", "e2ee.p2p_encrypted"].includes(payloadType)) {
|
|
24183
25112
|
const v1Msg = {
|
|
24184
25113
|
message_id: String(msg.message_id ?? ""),
|
|
24185
25114
|
from: String(msg.from_aid ?? ""),
|
|
@@ -24193,8 +25122,32 @@ var V2E2EECoordinator = class {
|
|
|
24193
25122
|
attachGatewayProximity(v1Msg, msg);
|
|
24194
25123
|
const appEvent = client._delivery.p2pAppEventForMessage(v1Msg);
|
|
24195
25124
|
if (ns) await client._publishPulledMessage(appEvent.event, ns, seq2, appEvent.payload, false);
|
|
24196
|
-
else await client._publishAppEvent(appEvent.event, appEvent.payload);
|
|
25125
|
+
else await client._publishAppEvent(appEvent.event, appEvent.payload, "pull");
|
|
24197
25126
|
decrypted.push(v1Msg);
|
|
25127
|
+
} else if (isJsonObject(legacyPayload) && ["e2ee.p2p_encrypted", "e2ee.encrypted"].includes(payloadType)) {
|
|
25128
|
+
const deferStatus2 = {};
|
|
25129
|
+
const plaintext2 = await client._decryptV2Message(
|
|
25130
|
+
{ ...msg, envelope_json: JSON.stringify(legacyPayload) },
|
|
25131
|
+
true,
|
|
25132
|
+
true,
|
|
25133
|
+
true,
|
|
25134
|
+
true,
|
|
25135
|
+
deferStatus2,
|
|
25136
|
+
true,
|
|
25137
|
+
"pull"
|
|
25138
|
+
);
|
|
25139
|
+
if (deferStatus2.deferred) {
|
|
25140
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
25141
|
+
if (deferStatus2.fromAid) {
|
|
25142
|
+
const key = `${deferStatus2.fromAid}\0${deferStatus2.senderDeviceId ?? ""}\0${deferStatus2.groupId ?? ""}`;
|
|
25143
|
+
deferredKeyFetches.set(key, { fromAid: deferStatus2.fromAid, senderDeviceId: deferStatus2.senderDeviceId ?? "", groupId: deferStatus2.groupId ?? "" });
|
|
25144
|
+
}
|
|
25145
|
+
}
|
|
25146
|
+
if (plaintext2) {
|
|
25147
|
+
if (ns) await client._publishPulledMessage("message.received", ns, seq2, plaintext2, false);
|
|
25148
|
+
else await client._publishAppEvent("message.received", plaintext2, "pull");
|
|
25149
|
+
decrypted.push(plaintext2);
|
|
25150
|
+
}
|
|
24198
25151
|
} else {
|
|
24199
25152
|
client._clientLog.debug(`message.v2.pull skipping V1 envelope seq=${seq2} payload_type=${payloadType || "<none>"} (V1 E2EE removed)`);
|
|
24200
25153
|
}
|
|
@@ -24209,7 +25162,7 @@ var V2E2EECoordinator = class {
|
|
|
24209
25162
|
client._v2Session.trackOldSPKMaxSeq(msgSpkId, seq2);
|
|
24210
25163
|
}
|
|
24211
25164
|
const deferStatus = {};
|
|
24212
|
-
const plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true);
|
|
25165
|
+
const plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, "pull");
|
|
24213
25166
|
if (deferStatus.deferred) {
|
|
24214
25167
|
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
24215
25168
|
}
|
|
@@ -24226,7 +25179,7 @@ var V2E2EECoordinator = class {
|
|
|
24226
25179
|
await client._publishPulledMessage("message.received", ns, seq2, plaintext, false);
|
|
24227
25180
|
decrypted.push(plaintext);
|
|
24228
25181
|
} else {
|
|
24229
|
-
await client._publishAppEvent("message.received", plaintext);
|
|
25182
|
+
await client._publishAppEvent("message.received", plaintext, "pull");
|
|
24230
25183
|
decrypted.push(plaintext);
|
|
24231
25184
|
}
|
|
24232
25185
|
}
|
|
@@ -24281,7 +25234,7 @@ var V2E2EECoordinator = class {
|
|
|
24281
25234
|
if (ackNeeded) {
|
|
24282
25235
|
this.recordForwardAck(ns, ackSeq);
|
|
24283
25236
|
const nextAfter2 = Math.max(pageMaxSeq, nextAfterSeq);
|
|
24284
|
-
const canContinuePage = messages.length > 0 && nextAfter2 > nextAfterSeq;
|
|
25237
|
+
const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter2 > nextAfterSeq;
|
|
24285
25238
|
if (canContinuePage) {
|
|
24286
25239
|
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
24287
25240
|
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
@@ -24291,6 +25244,16 @@ var V2E2EECoordinator = class {
|
|
|
24291
25244
|
}
|
|
24292
25245
|
}
|
|
24293
25246
|
}
|
|
25247
|
+
if (ns) client._delivery.onPullPage?.(
|
|
25248
|
+
ns,
|
|
25249
|
+
decrypted.length - pageDecryptedBefore,
|
|
25250
|
+
hasMore,
|
|
25251
|
+
pageMaxSeq,
|
|
25252
|
+
typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
|
|
25253
|
+
messages.length,
|
|
25254
|
+
client._seqTracker.getContiguousSeq(ns),
|
|
25255
|
+
pageMaxSeq
|
|
25256
|
+
);
|
|
24294
25257
|
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
24295
25258
|
const rawCount = messages.length;
|
|
24296
25259
|
if (blockedSeq > 0) break;
|
|
@@ -24325,6 +25288,7 @@ var V2E2EECoordinator = class {
|
|
|
24325
25288
|
pendingAckSeq = 0;
|
|
24326
25289
|
}
|
|
24327
25290
|
}
|
|
25291
|
+
client._delivery.onPullWorkSettled?.();
|
|
24328
25292
|
return decrypted;
|
|
24329
25293
|
}
|
|
24330
25294
|
async confirmForwardP2PAck(upToSeq, inlineGeneration) {
|
|
@@ -24402,7 +25366,8 @@ var V2E2EECoordinator = class {
|
|
|
24402
25366
|
});
|
|
24403
25367
|
return client.call("group.v2.send", withExplicitGroupAid({
|
|
24404
25368
|
group_id: gid,
|
|
24405
|
-
envelope
|
|
25369
|
+
envelope,
|
|
25370
|
+
message_id: opts?.messageId
|
|
24406
25371
|
}, groupAid));
|
|
24407
25372
|
};
|
|
24408
25373
|
try {
|
|
@@ -24461,6 +25426,11 @@ var V2E2EECoordinator = class {
|
|
|
24461
25426
|
const afterSeq = strictWindowSeq(params2.after_seq ?? 0, "after_seq");
|
|
24462
25427
|
const limit = strictWindowLimit(params2.limit ?? V2_PULL_DEFAULT_PAGE_LIMIT);
|
|
24463
25428
|
const ns = `group:${groupId}`;
|
|
25429
|
+
const cursorParams = isJsonObject(params2._group_cursor_params) ? params2._group_cursor_params : params2;
|
|
25430
|
+
const requestDeviceId = String(cursorParams.device_id ?? "").trim();
|
|
25431
|
+
const requestSlotId = String(cursorParams.slot_id ?? "").trim();
|
|
25432
|
+
const ownsCursor = (!requestDeviceId || requestDeviceId === String(client._deviceId ?? "")) && (!requestSlotId || requestSlotId === String(client._slotId ?? ""));
|
|
25433
|
+
if (ownsCursor) client._delivery.onPullStarted?.(ns);
|
|
24464
25434
|
const result = await client._callRawV2Rpc("group.v2.pull", withExplicitGroupAid({
|
|
24465
25435
|
group_id: groupId,
|
|
24466
25436
|
window_mode: "tail",
|
|
@@ -24490,6 +25460,17 @@ var V2E2EECoordinator = class {
|
|
|
24490
25460
|
}
|
|
24491
25461
|
const ack = client._seqTracker.getContiguousSeq(ns);
|
|
24492
25462
|
if (ack > previous.ack) await this.ackGroupV2(groupId, ack, groupAid);
|
|
25463
|
+
if (ownsCursor) client._delivery.onPullPage?.(
|
|
25464
|
+
ns,
|
|
25465
|
+
decoded.length,
|
|
25466
|
+
result.has_more === true,
|
|
25467
|
+
page.head,
|
|
25468
|
+
typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
|
|
25469
|
+
page.messages.length,
|
|
25470
|
+
ack,
|
|
25471
|
+
page.head
|
|
25472
|
+
);
|
|
25473
|
+
client._delivery.onPullWorkSettled?.();
|
|
24493
25474
|
return { ...result, messages: decoded, raw_count: page.messages.length };
|
|
24494
25475
|
}
|
|
24495
25476
|
async historyGroupV2Internal(params2) {
|
|
@@ -24522,6 +25503,13 @@ var V2E2EECoordinator = class {
|
|
|
24522
25503
|
});
|
|
24523
25504
|
}
|
|
24524
25505
|
async pullGroupV2(groupId, afterSeq = 0, limit = 50, opts) {
|
|
25506
|
+
const lifecycleNs = `group:${String(groupId ?? "").trim()}`;
|
|
25507
|
+
if (opts?.ownsCursor === false) {
|
|
25508
|
+
return await this.pullGroupV2Impl(groupId, afterSeq, limit, opts);
|
|
25509
|
+
}
|
|
25510
|
+
return await this.runPullWithLifecycle(lifecycleNs, () => this.pullGroupV2Impl(groupId, afterSeq, limit, opts));
|
|
25511
|
+
}
|
|
25512
|
+
async pullGroupV2Impl(groupId, afterSeq = 0, limit = 50, opts) {
|
|
24525
25513
|
const client = this.client;
|
|
24526
25514
|
await client._ensureV2SessionReady("group.pull");
|
|
24527
25515
|
const gid = String(groupId ?? "").trim();
|
|
@@ -24537,12 +25525,13 @@ var V2E2EECoordinator = class {
|
|
|
24537
25525
|
limit,
|
|
24538
25526
|
_group_cursor_params: opts?.cursorParams
|
|
24539
25527
|
}, `${ns}|tail|after=${afterSeq}|limit=${limit}`);
|
|
24540
|
-
return await client._runPullSerialized(key, () => this.
|
|
25528
|
+
return await client._runPullSerialized(key, () => this.pullGroupV2Impl(gid, afterSeq, limit, {
|
|
24541
25529
|
...opts ?? {},
|
|
24542
25530
|
gateLocked: true
|
|
24543
25531
|
}), false);
|
|
24544
25532
|
}
|
|
24545
25533
|
const result = await this.pullGroupV2TailInternal({
|
|
25534
|
+
...opts?.cursorParams ?? {},
|
|
24546
25535
|
group_id: String(opts.wireGroupId ?? gid),
|
|
24547
25536
|
group_aid: groupAid || void 0,
|
|
24548
25537
|
window_mode: "tail",
|
|
@@ -24559,7 +25548,7 @@ var V2E2EECoordinator = class {
|
|
|
24559
25548
|
limit,
|
|
24560
25549
|
_group_cursor_params: opts?.cursorParams
|
|
24561
25550
|
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
24562
|
-
return await client._runPullSerialized(key, () => this.
|
|
25551
|
+
return await client._runPullSerialized(key, () => this.pullGroupV2Impl(gid, afterSeq, limit, {
|
|
24563
25552
|
...opts ?? {},
|
|
24564
25553
|
gateLocked: true
|
|
24565
25554
|
}), true);
|
|
@@ -24568,6 +25557,7 @@ var V2E2EECoordinator = class {
|
|
|
24568
25557
|
const wireGroupId = String(opts?.wireGroupId ?? groupId ?? "").trim() || gid;
|
|
24569
25558
|
const cursorParams = opts?.cursorParams ?? {};
|
|
24570
25559
|
const ownsCursor = opts?.ownsCursor !== false;
|
|
25560
|
+
if (ownsCursor) client._delivery.onPullStarted?.(ns);
|
|
24571
25561
|
let pullGateKey = ownsCursor ? pullGateKeyForClient(client, "group.v2.pull", {
|
|
24572
25562
|
group_id: gid,
|
|
24573
25563
|
after_seq: afterSeq,
|
|
@@ -24628,26 +25618,28 @@ var V2E2EECoordinator = class {
|
|
|
24628
25618
|
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
24629
25619
|
const deferredKeyFetches = /* @__PURE__ */ new Map();
|
|
24630
25620
|
let blockedSeq = 0;
|
|
25621
|
+
const pageDecryptedBefore = decrypted.length;
|
|
24631
25622
|
for (const msg of messages) {
|
|
24632
25623
|
const seq2 = Number(msg.seq ?? 0);
|
|
24633
25624
|
if (!Number.isFinite(seq2) || seq2 <= 0) continue;
|
|
24634
25625
|
const version = String(msg.version ?? "v2");
|
|
24635
25626
|
if (version === "v1") {
|
|
24636
|
-
const
|
|
25627
|
+
const legacy = isJsonObject(msg.legacy_v1) ? msg.legacy_v1 : {};
|
|
25628
|
+
const payload = msg.payload !== void 0 ? msg.payload : legacy.payload;
|
|
24637
25629
|
const payloadObj = isJsonObject(payload) ? payload : null;
|
|
24638
|
-
if (client._delivery.recallEventFromGroupMessage(msg)) {
|
|
25630
|
+
if (typeof client._delivery?.recallEventFromGroupMessage === "function" && client._delivery.recallEventFromGroupMessage(msg)) {
|
|
24639
25631
|
await client._delivery.publishGroupRecallTombstone(gid, seq2, {
|
|
24640
25632
|
...msg,
|
|
24641
25633
|
group_id: gid,
|
|
24642
25634
|
group_aid: eventGroupAid
|
|
24643
|
-
});
|
|
25635
|
+
}, "pull");
|
|
24644
25636
|
client._markPublishedSeq(ns, seq2);
|
|
24645
25637
|
client._clientLog.debug(`group.v2.pull recall tombstone delivered: group=${gid}, seq=${seq2}`);
|
|
24646
25638
|
continue;
|
|
24647
25639
|
}
|
|
24648
25640
|
if (payloadObj) {
|
|
24649
25641
|
const payloadType = String(payloadObj.type ?? "").trim();
|
|
24650
|
-
if (payloadType !== "e2ee.encrypted" && payloadType !== "e2ee.group_encrypted") {
|
|
25642
|
+
if (payloadType !== "e2ee.encrypted" && payloadType !== "e2ee.group_encrypted" && payloadType !== "e2ee.p2p_encrypted") {
|
|
24651
25643
|
let v1Msg = {
|
|
24652
25644
|
message_id: String(msg.message_id ?? ""),
|
|
24653
25645
|
from: String(msg.from_aid ?? ""),
|
|
@@ -24683,6 +25675,32 @@ var V2E2EECoordinator = class {
|
|
|
24683
25675
|
decrypted.push(v1Msg);
|
|
24684
25676
|
continue;
|
|
24685
25677
|
}
|
|
25678
|
+
if (payloadObj && ["e2ee.group_encrypted", "e2ee.p2p_encrypted", "e2ee.encrypted"].includes(String(payloadObj.type ?? "").trim())) {
|
|
25679
|
+
const deferStatus2 = {};
|
|
25680
|
+
const plaintext2 = await client._decryptV2Message(
|
|
25681
|
+
{ ...msg, envelope_json: JSON.stringify(payloadObj) },
|
|
25682
|
+
true,
|
|
25683
|
+
true,
|
|
25684
|
+
true,
|
|
25685
|
+
true,
|
|
25686
|
+
deferStatus2,
|
|
25687
|
+
true,
|
|
25688
|
+
"pull"
|
|
25689
|
+
);
|
|
25690
|
+
if (deferStatus2.deferred) {
|
|
25691
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
25692
|
+
if (deferStatus2.fromAid) {
|
|
25693
|
+
const key = `${deferStatus2.fromAid}\0${deferStatus2.senderDeviceId ?? ""}\0${deferStatus2.groupId ?? ""}`;
|
|
25694
|
+
deferredKeyFetches.set(key, { fromAid: deferStatus2.fromAid, senderDeviceId: deferStatus2.senderDeviceId ?? "", groupId: deferStatus2.groupId ?? "" });
|
|
25695
|
+
}
|
|
25696
|
+
}
|
|
25697
|
+
if (plaintext2) {
|
|
25698
|
+
plaintext2.group_id = gid;
|
|
25699
|
+
plaintext2.group_aid = eventGroupAid;
|
|
25700
|
+
await client._publishPulledMessage("group.message_created", ns, seq2, plaintext2, false);
|
|
25701
|
+
decrypted.push(plaintext2);
|
|
25702
|
+
}
|
|
25703
|
+
}
|
|
24686
25704
|
client._clientLog.debug(`group.v2.pull skipping V1 envelope group=${gid} seq=${seq2} payload_type=${payloadObj ? String(payloadObj.type ?? "") : "<none>"} (V1 E2EE removed)`);
|
|
24687
25705
|
continue;
|
|
24688
25706
|
}
|
|
@@ -24691,7 +25709,7 @@ var V2E2EECoordinator = class {
|
|
|
24691
25709
|
continue;
|
|
24692
25710
|
}
|
|
24693
25711
|
const deferStatus = {};
|
|
24694
|
-
let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true);
|
|
25712
|
+
let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true, "pull");
|
|
24695
25713
|
if (deferStatus.deferred) {
|
|
24696
25714
|
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
24697
25715
|
}
|
|
@@ -24763,6 +25781,16 @@ var V2E2EECoordinator = class {
|
|
|
24763
25781
|
for (const fetch2 of deferredKeyFetches.values()) {
|
|
24764
25782
|
this.scheduleSenderIKFetch(fetch2.fromAid, fetch2.senderDeviceId, fetch2.groupId);
|
|
24765
25783
|
}
|
|
25784
|
+
client._delivery.onPullPage?.(
|
|
25785
|
+
ns,
|
|
25786
|
+
decrypted.length - pageDecryptedBefore,
|
|
25787
|
+
hasMore,
|
|
25788
|
+
pageMaxSeq,
|
|
25789
|
+
typeof result.remaining === "number" && Number.isSafeInteger(result.remaining) && result.remaining >= 0 ? result.remaining : null,
|
|
25790
|
+
messages.length,
|
|
25791
|
+
client._seqTracker.getContiguousSeq(ns),
|
|
25792
|
+
pageMaxSeq
|
|
25793
|
+
);
|
|
24766
25794
|
if (ownsCursor && parsedCursorCurrentSeq !== null && cursorCurrentSeq > ackSeq) {
|
|
24767
25795
|
this.recordForwardCursor(ns, cursorCurrentSeq, ackSeq);
|
|
24768
25796
|
}
|
|
@@ -24771,7 +25799,7 @@ var V2E2EECoordinator = class {
|
|
|
24771
25799
|
if (ackNeeded) {
|
|
24772
25800
|
this.recordForwardAck(ns, ackSeq);
|
|
24773
25801
|
const nextAfter2 = Math.max(pageMaxSeq, nextAfterSeq);
|
|
24774
|
-
const canContinuePage = messages.length > 0 && nextAfter2 > nextAfterSeq && ownsCursor;
|
|
25802
|
+
const canContinuePage = blockedSeq <= 0 && messages.length > 0 && nextAfter2 > nextAfterSeq && ownsCursor;
|
|
24775
25803
|
if (canContinuePage) {
|
|
24776
25804
|
pendingAckSeq = Math.max(pendingAckSeq, ackSeq);
|
|
24777
25805
|
lastAutoAckSeq = Math.max(lastAutoAckSeq, ackSeq);
|
|
@@ -24817,6 +25845,7 @@ var V2E2EECoordinator = class {
|
|
|
24817
25845
|
pendingAckSeq = 0;
|
|
24818
25846
|
}
|
|
24819
25847
|
}
|
|
25848
|
+
client._delivery.onPullWorkSettled?.();
|
|
24820
25849
|
return decrypted;
|
|
24821
25850
|
}
|
|
24822
25851
|
async confirmForwardGroupAck(groupId, upToSeq, groupAid) {
|
|
@@ -25326,7 +26355,7 @@ var V2E2EECoordinator = class {
|
|
|
25326
26355
|
}
|
|
25327
26356
|
async decryptV2PushMessage(data) {
|
|
25328
26357
|
if (!isJsonObject(data)) return null;
|
|
25329
|
-
return await this.client._decryptV2Message(data);
|
|
26358
|
+
return await this.client._decryptV2Message(data, false, false, false, false, void 0, false, "inline_push");
|
|
25330
26359
|
}
|
|
25331
26360
|
async buildV2P2PEnvelope(opts) {
|
|
25332
26361
|
const client = this.client;
|
|
@@ -25838,7 +26867,7 @@ var GroupStateCoordinator = class {
|
|
|
25838
26867
|
if (!isJsonObject(data) || !client._v2Session) return;
|
|
25839
26868
|
const groupId = groupIdFromRecord(data);
|
|
25840
26869
|
if (!groupId) return;
|
|
25841
|
-
|
|
26870
|
+
client._dispatcher.enqueue("group.v2.state_proposed", data);
|
|
25842
26871
|
try {
|
|
25843
26872
|
await client._v2ConfirmPendingProposal(groupId);
|
|
25844
26873
|
} catch (exc) {
|
|
@@ -25850,7 +26879,7 @@ var GroupStateCoordinator = class {
|
|
|
25850
26879
|
if (!isJsonObject(data) || !client._v2Session) return;
|
|
25851
26880
|
const groupId = groupIdFromRecord(data);
|
|
25852
26881
|
if (!groupId) return;
|
|
25853
|
-
|
|
26882
|
+
client._dispatcher.enqueue("group.v2.state_retry_needed", data);
|
|
25854
26883
|
try {
|
|
25855
26884
|
await client._v2AutoProposeState(groupId, { leaderDelay: true });
|
|
25856
26885
|
} catch (exc) {
|
|
@@ -25868,7 +26897,7 @@ var GroupStateCoordinator = class {
|
|
|
25868
26897
|
}
|
|
25869
26898
|
client._v2AutoProposeLastSnapshot?.delete?.(groupId);
|
|
25870
26899
|
}
|
|
25871
|
-
|
|
26900
|
+
client._dispatcher.enqueue("group.v2.state_confirmed", data);
|
|
25872
26901
|
}
|
|
25873
26902
|
async publishV2GroupSecurityLevel(groupId, bootstrap) {
|
|
25874
26903
|
const client = this.client;
|
|
@@ -25879,7 +26908,7 @@ var GroupStateCoordinator = class {
|
|
|
25879
26908
|
const previous = securityLevels.get(gid);
|
|
25880
26909
|
if (previous === level) return;
|
|
25881
26910
|
securityLevels.set(gid, level);
|
|
25882
|
-
|
|
26911
|
+
client._dispatcher.enqueue("group.v2.security_level", {
|
|
25883
26912
|
group_id: gid,
|
|
25884
26913
|
level,
|
|
25885
26914
|
warning: String(bootstrap.e2ee_security_warning ?? ""),
|
|
@@ -25974,7 +27003,7 @@ var GroupStateCoordinator = class {
|
|
|
25974
27003
|
} catch {
|
|
25975
27004
|
}
|
|
25976
27005
|
client._clientLog.warn(`V2 state chain fork detected: group=${gid} local_chain=${localChain.slice(0, 16)}... server_chain=${serverChain.slice(0, 16)}...`);
|
|
25977
|
-
|
|
27006
|
+
client._dispatcher.enqueue("group.v2.fork_detected", {
|
|
25978
27007
|
group_id: gid,
|
|
25979
27008
|
local_chain: localChain,
|
|
25980
27009
|
server_chain: serverChain
|
|
@@ -26508,7 +27537,7 @@ var GroupStateCoordinator = class {
|
|
|
26508
27537
|
}
|
|
26509
27538
|
if (mode !== "open" && mode !== "invite_code" && mode !== "invite_only") {
|
|
26510
27539
|
client._clientLog.warn(`V2 state tamper detected: group=${groupId} pending_extra=${extra.sort().join(",")} mode=${mode}`);
|
|
26511
|
-
|
|
27540
|
+
client._dispatcher.enqueue("group.v2.state_tampered", {
|
|
26512
27541
|
group_id: groupId,
|
|
26513
27542
|
pending_extra: extra.sort(),
|
|
26514
27543
|
mode
|
|
@@ -27458,6 +28487,7 @@ function buildDefaultAgentMd(aid, options = {}) {
|
|
|
27458
28487
|
].join("\n");
|
|
27459
28488
|
}
|
|
27460
28489
|
var HEAD_HTTP_TIMEOUT_MS = 15e3;
|
|
28490
|
+
var AGENT_MD_NEGATIVE_CACHE_TTL_MS = 6e4;
|
|
27461
28491
|
var noopLogger = {
|
|
27462
28492
|
error: () => {
|
|
27463
28493
|
},
|
|
@@ -27798,6 +28828,24 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
27798
28828
|
ttl_days: Number(ttlDays) || 0
|
|
27799
28829
|
};
|
|
27800
28830
|
}
|
|
28831
|
+
const remoteMissingCached = String(before.remote_status ?? "").trim().toLowerCase() === "missing";
|
|
28832
|
+
if (!localFound && !remoteEtagCached && remoteMissingCached && _AgentMdManager.checkedAtFresh(checkedAtCached, ttlDays)) {
|
|
28833
|
+
return {
|
|
28834
|
+
aid: target,
|
|
28835
|
+
local_found: false,
|
|
28836
|
+
remote_found: false,
|
|
28837
|
+
local_etag: "",
|
|
28838
|
+
remote_etag: "",
|
|
28839
|
+
in_sync: false,
|
|
28840
|
+
needs_update: false,
|
|
28841
|
+
last_modified: "",
|
|
28842
|
+
status: 404,
|
|
28843
|
+
cached: true,
|
|
28844
|
+
verify_status: "",
|
|
28845
|
+
verify_error: "",
|
|
28846
|
+
ttl_days: Number(ttlDays) || 0
|
|
28847
|
+
};
|
|
28848
|
+
}
|
|
27801
28849
|
const now = Date.now();
|
|
27802
28850
|
let remote;
|
|
27803
28851
|
try {
|
|
@@ -28287,14 +29335,16 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
28287
29335
|
async _scheduleFetchIfMissing(aid, record, source = "") {
|
|
28288
29336
|
const target = String(aid ?? "").trim();
|
|
28289
29337
|
if (!target || await this._hasLocalContent(target, record)) return;
|
|
29338
|
+
const cached = record ?? await this.loadRecord(target) ?? {};
|
|
29339
|
+
const checkedAt = Number(cached.checked_at ?? 0) || 0;
|
|
29340
|
+
if (String(cached.remote_status ?? "").trim().toLowerCase() === "missing" && checkedAt > 0 && Date.now() - checkedAt <= AGENT_MD_NEGATIVE_CACHE_TTL_MS) return;
|
|
28290
29341
|
if (this._fetchInflight.has(target)) return;
|
|
28291
29342
|
this._fetchInflight.add(target);
|
|
28292
29343
|
try {
|
|
28293
29344
|
await this.download(target);
|
|
28294
29345
|
} catch (err) {
|
|
28295
29346
|
await this.saveRecord(target, {
|
|
28296
|
-
last_error: err instanceof Error ? err.message : String(err)
|
|
28297
|
-
remote_status: "found"
|
|
29347
|
+
last_error: err instanceof Error ? err.message : String(err)
|
|
28298
29348
|
});
|
|
28299
29349
|
this._log.debug(`agent.md auto fetch failed: aid=${target} source=${source || "-"} err=${err instanceof Error ? err.message : String(err)}`);
|
|
28300
29350
|
} finally {
|
|
@@ -28718,7 +29768,6 @@ var _AUNClient = class _AUNClient {
|
|
|
28718
29768
|
__publicField(this, "_reconnectActive", false);
|
|
28719
29769
|
__publicField(this, "_reconnectAbort", null);
|
|
28720
29770
|
__publicField(this, "_reconnectTask", null);
|
|
28721
|
-
__publicField(this, "_reconnectEventDispatchDepth", 0);
|
|
28722
29771
|
__publicField(this, "_serverKicked", false);
|
|
28723
29772
|
// 重连状态追踪(对齐 Python client.py)
|
|
28724
29773
|
__publicField(this, "_nextRetryAt", null);
|
|
@@ -28898,7 +29947,7 @@ var _AUNClient = class _AUNClient {
|
|
|
28898
29947
|
});
|
|
28899
29948
|
for (const evt of ["message.ack", "storage.object_changed", "stream/created", "stream/closed"]) {
|
|
28900
29949
|
this._dispatcher.subscribe(`_raw.${evt}`, (data) => {
|
|
28901
|
-
this._dispatcher.
|
|
29950
|
+
this._dispatcher.enqueue(evt, data);
|
|
28902
29951
|
});
|
|
28903
29952
|
}
|
|
28904
29953
|
this._dispatcher.subscribe("_raw.gateway.disconnect", async (data) => {
|
|
@@ -29352,6 +30401,53 @@ var _AUNClient = class _AUNClient {
|
|
|
29352
30401
|
await this._agentMdManager.upload(await this._agentMdManager.readContent(target) ?? buildDefaultAgentMd(target));
|
|
29353
30402
|
return true;
|
|
29354
30403
|
}
|
|
30404
|
+
async _checkIdentityAdmissionAfterConnect() {
|
|
30405
|
+
const aid = String(this._aid ?? this._currentAid?.aid ?? "").trim();
|
|
30406
|
+
if (!aid) return;
|
|
30407
|
+
try {
|
|
30408
|
+
const checked = await this._agentMdManager.check(aid);
|
|
30409
|
+
if (!checked.remote_found) {
|
|
30410
|
+
const content = await this._agentMdManager.readContent(aid) ?? buildDefaultAgentMd(aid);
|
|
30411
|
+
await this._agentMdManager.upload(content);
|
|
30412
|
+
}
|
|
30413
|
+
} catch (exc) {
|
|
30414
|
+
this._clientLog.warn(`post-connect agent.md check failed: ${String(exc)}`);
|
|
30415
|
+
}
|
|
30416
|
+
const store = this._aidStore;
|
|
30417
|
+
if (!store) return;
|
|
30418
|
+
let listed;
|
|
30419
|
+
try {
|
|
30420
|
+
listed = await this.call("group.list_my", {});
|
|
30421
|
+
} catch (exc) {
|
|
30422
|
+
this._clientLog.warn(`post-connect group identity check failed: ${String(exc)}`);
|
|
30423
|
+
return;
|
|
30424
|
+
}
|
|
30425
|
+
const result = isJsonObject(listed) ? listed : {};
|
|
30426
|
+
const groups = Array.isArray(result.groups) ? result.groups : Array.isArray(result.items) ? result.items : [];
|
|
30427
|
+
for (const raw of groups) {
|
|
30428
|
+
if (!isJsonObject(raw)) continue;
|
|
30429
|
+
const role = String(raw.role ?? raw.my_role ?? "").trim().toLowerCase();
|
|
30430
|
+
if (role !== "owner") continue;
|
|
30431
|
+
const groupId = String(raw.group_id ?? raw.groupId ?? raw.group_aid ?? raw.groupAid ?? "").trim();
|
|
30432
|
+
const groupAid = String(raw.group_aid ?? raw.groupAid ?? groupId).trim();
|
|
30433
|
+
if (!groupId || !groupAid) continue;
|
|
30434
|
+
try {
|
|
30435
|
+
await this._runGroupIdentityOperation(groupId, async () => {
|
|
30436
|
+
const loaded = await store.load(groupAid);
|
|
30437
|
+
if (!loaded.ok || !loaded.data?.aid?.isPrivateKeyValid()) {
|
|
30438
|
+
await this.bindGroupAid({ group_id: groupId, group_aid: groupAid }, { aidStore: store });
|
|
30439
|
+
return;
|
|
30440
|
+
}
|
|
30441
|
+
const checked = await this._agentMdManager.check(groupAid);
|
|
30442
|
+
if (!checked.remote_found) {
|
|
30443
|
+
await this._uploadGroupAgentMd(store, groupAid, {}, {}, aid);
|
|
30444
|
+
}
|
|
30445
|
+
});
|
|
30446
|
+
} catch (exc) {
|
|
30447
|
+
this._clientLog.warn(`post-connect group identity check failed: group=${groupId} err=${String(exc)}`);
|
|
30448
|
+
}
|
|
30449
|
+
}
|
|
30450
|
+
}
|
|
29355
30451
|
async _runGroupIdentityOperation(groupId, operation) {
|
|
29356
30452
|
const aid = String(this._aid ?? this._currentAid?.aid ?? "").trim();
|
|
29357
30453
|
const dot = aid.indexOf(".");
|
|
@@ -29985,8 +31081,8 @@ var _AUNClient = class _AUNClient {
|
|
|
29985
31081
|
_markPublishedSeq(ns, seq2) {
|
|
29986
31082
|
this._delivery.markPublishedSeq(ns, seq2);
|
|
29987
31083
|
}
|
|
29988
|
-
async _publishAppEvent(event, payload) {
|
|
29989
|
-
await this._delivery.publishAppEvent(event, payload);
|
|
31084
|
+
async _publishAppEvent(event, payload, source = "direct", ns = "", batch) {
|
|
31085
|
+
await this._delivery.publishAppEvent(event, payload, source, ns, batch);
|
|
29990
31086
|
}
|
|
29991
31087
|
_echoTimestamp() {
|
|
29992
31088
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -30024,11 +31120,11 @@ var _AUNClient = class _AUNClient {
|
|
|
30024
31120
|
async _drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true) {
|
|
30025
31121
|
await this._delivery.drainOrderedMessages(ns, beforeSeq, pullResponse, persist);
|
|
30026
31122
|
}
|
|
30027
|
-
async _publishOrderedMessage(event, ns, seq2, payload) {
|
|
30028
|
-
return this._delivery.publishOrderedMessage(event, ns, seq2, payload);
|
|
31123
|
+
async _publishOrderedMessage(event, ns, seq2, payload, source = "push", batch) {
|
|
31124
|
+
return this._delivery.publishOrderedMessage(event, ns, seq2, payload, source, batch);
|
|
30029
31125
|
}
|
|
30030
|
-
async _publishPulledMessage(event, ns, seq2, payload, persist = true) {
|
|
30031
|
-
return this._delivery.publishPulledMessage(event, ns, seq2, payload, persist);
|
|
31126
|
+
async _publishPulledMessage(event, ns, seq2, payload, persist = true, source = "pull", batch) {
|
|
31127
|
+
return this._delivery.publishPulledMessage(event, ns, seq2, payload, persist, source, batch);
|
|
30032
31128
|
}
|
|
30033
31129
|
_extractGroupIdFromResult(result) {
|
|
30034
31130
|
const group = isJsonObject(result.group) ? result.group : null;
|
|
@@ -30059,7 +31155,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30059
31155
|
const groupId = d.group_id ?? d.group_aid ?? "";
|
|
30060
31156
|
await this._delivery.handleGroupChangedEventSeq(d, groupId);
|
|
30061
31157
|
} else {
|
|
30062
|
-
|
|
31158
|
+
this._dispatcher.enqueue("group.changed", data);
|
|
30063
31159
|
}
|
|
30064
31160
|
this._clientLog.debug(`_onRawGroupChanged exit: elapsed=${Date.now() - tStart}ms group_id=${groupIdInit}`);
|
|
30065
31161
|
} catch (err) {
|
|
@@ -30132,7 +31228,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30132
31228
|
const ok = await ecdsaVerifyDer(pubKey, sigBytes, signData);
|
|
30133
31229
|
if (!ok) {
|
|
30134
31230
|
this._clientLog.warn(`group event sig verify failed aid=%s method=%s${sigAid} ${method}`);
|
|
30135
|
-
this._dispatcher.
|
|
31231
|
+
this._dispatcher.enqueue("signature.verification_failed", {
|
|
30136
31232
|
aid: sigAid,
|
|
30137
31233
|
method,
|
|
30138
31234
|
error: "ECDSA verification failed"
|
|
@@ -30141,7 +31237,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30141
31237
|
return ok;
|
|
30142
31238
|
} catch (exc) {
|
|
30143
31239
|
this._clientLog.warn(`group event sig verify exception:${String(exc)}`);
|
|
30144
|
-
this._dispatcher.
|
|
31240
|
+
this._dispatcher.enqueue("signature.verification_failed", {
|
|
30145
31241
|
aid: sigAid,
|
|
30146
31242
|
method,
|
|
30147
31243
|
error: String(exc)
|
|
@@ -30359,7 +31455,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30359
31455
|
throw new StateError("connection attempt superseded");
|
|
30360
31456
|
}
|
|
30361
31457
|
} else {
|
|
30362
|
-
|
|
31458
|
+
this._dispatcher.enqueue("state_change", statePayload);
|
|
30363
31459
|
}
|
|
30364
31460
|
this._assertReconnectOwner(reconnectOwner);
|
|
30365
31461
|
this._lifecycle.assertConnectionAttemptOwner(connectionOwner);
|
|
@@ -30383,6 +31479,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30383
31479
|
if (backgroundSyncEnabled) {
|
|
30384
31480
|
this._safeAsync(this._fillP2pGap());
|
|
30385
31481
|
}
|
|
31482
|
+
this._safeAsync(this._checkIdentityAdmissionAfterConnect());
|
|
30386
31483
|
this._clientLog.debug(`_connectOnce exit: elapsed=${Date.now() - tStart}ms aid=${this._aid ?? "-"}`);
|
|
30387
31484
|
} catch (err) {
|
|
30388
31485
|
this._clientLog.debug(`_connectOnce exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -30676,7 +31773,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30676
31773
|
if (this._sessionParams && identity.access_token) {
|
|
30677
31774
|
this._sessionParams.access_token = identity.access_token;
|
|
30678
31775
|
}
|
|
30679
|
-
|
|
31776
|
+
this._dispatcher.enqueue("token.refreshed", {
|
|
30680
31777
|
aid: identity.aid,
|
|
30681
31778
|
expires_at: identity.access_token_expires_at
|
|
30682
31779
|
});
|
|
@@ -30685,7 +31782,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30685
31782
|
if (exc instanceof AuthError) {
|
|
30686
31783
|
if (authErrorRequiresRelogin(exc)) {
|
|
30687
31784
|
this._clientLog.warn(`token refresh requires relogin, stopping refresh loop and triggering reconnect: ${exc.message}`);
|
|
30688
|
-
|
|
31785
|
+
this._dispatcher.enqueue("token.refresh_exhausted", {
|
|
30689
31786
|
aid: this._identity?.aid ?? null,
|
|
30690
31787
|
consecutive_failures: 1,
|
|
30691
31788
|
last_error: String(exc),
|
|
@@ -30698,7 +31795,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30698
31795
|
this._tokenRefreshFailures++;
|
|
30699
31796
|
if (this._tokenRefreshFailures >= 3) {
|
|
30700
31797
|
this._clientLog.warn(`token refresh failed ${this._tokenRefreshFailures} consecutive times, stopping refresh loop and triggering reconnect`);
|
|
30701
|
-
|
|
31798
|
+
this._dispatcher.enqueue("token.refresh_exhausted", {
|
|
30702
31799
|
aid: this._identity?.aid ?? null,
|
|
30703
31800
|
consecutive_failures: this._tokenRefreshFailures,
|
|
30704
31801
|
last_error: String(exc)
|
|
@@ -30709,7 +31806,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30709
31806
|
}
|
|
30710
31807
|
this._clientLog.warn(`token refresh failed (${this._tokenRefreshFailures}/3), next retry: ${String(exc)}`);
|
|
30711
31808
|
} else {
|
|
30712
|
-
this._dispatcher.
|
|
31809
|
+
this._dispatcher.enqueue("connection.error", { error: formatCaughtError2(exc) });
|
|
30713
31810
|
}
|
|
30714
31811
|
}
|
|
30715
31812
|
scheduleRefresh();
|
|
@@ -30738,7 +31835,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30738
31835
|
this._serverKicked = !retryable;
|
|
30739
31836
|
this._lastDisconnectInfo = { code, reason, detail };
|
|
30740
31837
|
try {
|
|
30741
|
-
|
|
31838
|
+
this._dispatcher.enqueue("gateway.disconnect", { code, reason, detail });
|
|
30742
31839
|
} catch (exc) {
|
|
30743
31840
|
this._clientLog.debug(`publish gateway.disconnect failed: ${exc?.message ?? exc}`);
|
|
30744
31841
|
}
|
|
@@ -30757,7 +31854,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30757
31854
|
} catch (exc) {
|
|
30758
31855
|
this._clientLog.debug(`transport cleanup skipped: ${formatCaughtError2(exc)}`);
|
|
30759
31856
|
}
|
|
30760
|
-
|
|
31857
|
+
this._dispatcher.enqueue("state_change", {
|
|
30761
31858
|
state: this._publicState(this._state),
|
|
30762
31859
|
error
|
|
30763
31860
|
});
|
|
@@ -30793,7 +31890,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30793
31890
|
if (disconnectInfo.code !== void 0 && disconnectInfo.code !== null) {
|
|
30794
31891
|
eventPayload.code = disconnectInfo.code;
|
|
30795
31892
|
}
|
|
30796
|
-
|
|
31893
|
+
this._dispatcher.enqueue("state_change", eventPayload);
|
|
30797
31894
|
if (this._reconnectAbort === reconnectAbort) {
|
|
30798
31895
|
this._reconnectAbort = null;
|
|
30799
31896
|
this._reconnectActive = false;
|
|
@@ -30824,12 +31921,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30824
31921
|
}
|
|
30825
31922
|
async _publishReconnectEvent(owner, event, payload) {
|
|
30826
31923
|
if (!this._ownsReconnect(owner)) return false;
|
|
30827
|
-
this.
|
|
30828
|
-
try {
|
|
30829
|
-
await this._dispatcher.publish(event, payload);
|
|
30830
|
-
} finally {
|
|
30831
|
-
this._reconnectEventDispatchDepth -= 1;
|
|
30832
|
-
}
|
|
31924
|
+
this._dispatcher.enqueue(event, payload);
|
|
30833
31925
|
return this._ownsReconnect(owner);
|
|
30834
31926
|
}
|
|
30835
31927
|
async _cancelReconnectAndWait() {
|
|
@@ -30842,7 +31934,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30842
31934
|
} catch (exc) {
|
|
30843
31935
|
this._clientLog.debug(`reconnect cancellation transport cleanup skipped: ${formatCaughtError2(exc)}`);
|
|
30844
31936
|
}
|
|
30845
|
-
if (task
|
|
31937
|
+
if (task) {
|
|
30846
31938
|
try {
|
|
30847
31939
|
await task;
|
|
30848
31940
|
} catch (exc) {
|
|
@@ -31274,7 +32366,7 @@ var _AUNClient = class _AUNClient {
|
|
|
31274
32366
|
* undecryptable 事件、成功消费后触发 SPK 轮换。inline Push 在游标提交和
|
|
31275
32367
|
* Head/正文绑定校验前调用本入口时必须显式关闭这些副作用。
|
|
31276
32368
|
*/
|
|
31277
|
-
async _decryptV2Message(msg, allowPending = true, emitUndecryptable = true, rotateKeys = true, observeAgentMd = true, deferStatus, deferKeyFetch = false) {
|
|
32369
|
+
async _decryptV2Message(msg, allowPending = true, emitUndecryptable = true, rotateKeys = true, observeAgentMd = true, deferStatus, deferKeyFetch = false, source = "pull") {
|
|
31278
32370
|
const session = this._v2Session;
|
|
31279
32371
|
if (!session) return null;
|
|
31280
32372
|
const envJson = msg.envelope_json;
|
|
@@ -31362,10 +32454,11 @@ var _AUNClient = class _AUNClient {
|
|
|
31362
32454
|
_decrypt_stage: "spk_lookup",
|
|
31363
32455
|
_envelope_type: String(envelope.type ?? ""),
|
|
31364
32456
|
_suite: String(envelope.suite ?? ""),
|
|
31365
|
-
_spk_id: spkId
|
|
32457
|
+
_spk_id: spkId,
|
|
32458
|
+
source
|
|
31366
32459
|
};
|
|
31367
32460
|
attachV2EnvelopeMetadata2(event, e2eeMeta);
|
|
31368
|
-
|
|
32461
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
31369
32462
|
} catch {
|
|
31370
32463
|
}
|
|
31371
32464
|
}
|
|
@@ -31403,10 +32496,11 @@ var _AUNClient = class _AUNClient {
|
|
|
31403
32496
|
_decrypt_error: "sender_ik_not_found",
|
|
31404
32497
|
_decrypt_stage: "sender_ik",
|
|
31405
32498
|
_envelope_type: String(envelope.type ?? ""),
|
|
31406
|
-
_suite: String(envelope.suite ?? "")
|
|
32499
|
+
_suite: String(envelope.suite ?? ""),
|
|
32500
|
+
source
|
|
31407
32501
|
};
|
|
31408
32502
|
attachV2EnvelopeMetadata2(event, e2eeMeta);
|
|
31409
|
-
|
|
32503
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
31410
32504
|
} catch {
|
|
31411
32505
|
}
|
|
31412
32506
|
}
|
|
@@ -31439,10 +32533,11 @@ var _AUNClient = class _AUNClient {
|
|
|
31439
32533
|
_decrypt_error: String(exc),
|
|
31440
32534
|
_decrypt_stage: "decrypt",
|
|
31441
32535
|
_envelope_type: String(envelope.type ?? ""),
|
|
31442
|
-
_suite: String(envelope.suite ?? "")
|
|
32536
|
+
_suite: String(envelope.suite ?? ""),
|
|
32537
|
+
source
|
|
31443
32538
|
};
|
|
31444
32539
|
attachV2EnvelopeMetadata2(event, e2eeMeta);
|
|
31445
|
-
|
|
32540
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
31446
32541
|
} catch {
|
|
31447
32542
|
}
|
|
31448
32543
|
}
|
|
@@ -32017,7 +33112,7 @@ var RegisterFlow = class _RegisterFlow {
|
|
|
32017
33112
|
return new Promise((resolve, reject) => {
|
|
32018
33113
|
let ws;
|
|
32019
33114
|
try {
|
|
32020
|
-
ws =
|
|
33115
|
+
ws = createTransportWebSocket(gatewayUrl);
|
|
32021
33116
|
} catch {
|
|
32022
33117
|
reject(new AuthError(`WebSocket \u8FDE\u63A5\u5931\u8D25: ${gatewayUrl}`));
|
|
32023
33118
|
return;
|
|
@@ -32051,7 +33146,19 @@ var RegisterFlow = class _RegisterFlow {
|
|
|
32051
33146
|
if (!receivedChallenge) {
|
|
32052
33147
|
if (!isJsonObject(msg) || msg.method !== "challenge") return;
|
|
32053
33148
|
receivedChallenge = true;
|
|
32054
|
-
ws.send(JSON.stringify({ jsonrpc: "2.0", id: requestId, method, params: params2 }));
|
|
33149
|
+
const sendResult = ws.send(JSON.stringify({ jsonrpc: "2.0", id: requestId, method, params: params2 }));
|
|
33150
|
+
if (sendResult && typeof sendResult.then === "function") {
|
|
33151
|
+
void Promise.resolve(sendResult).catch((error) => {
|
|
33152
|
+
if (settled) return;
|
|
33153
|
+
settled = true;
|
|
33154
|
+
globalThis.clearTimeout(timeout);
|
|
33155
|
+
try {
|
|
33156
|
+
ws.close();
|
|
33157
|
+
} catch {
|
|
33158
|
+
}
|
|
33159
|
+
reject(error instanceof Error ? error : new AuthError(String(error)));
|
|
33160
|
+
});
|
|
33161
|
+
}
|
|
32055
33162
|
return;
|
|
32056
33163
|
}
|
|
32057
33164
|
if (!isJsonObject(msg) || msg.id !== requestId) return;
|