@agentunion/fastaun-browser 0.5.8 → 0.5.10
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 +62 -0
- package/_packed_docs/CHANGELOG.md +62 -0
- package/_packed_docs/agent.md/examples//347/276/244/347/273/204-/345/274/200/345/217/221/345/233/242/351/230/237.md +21 -0
- 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 +43 -8
- package/_packed_docs/sdk/09-group-rpc-manual.md +18 -3
- package/_packed_docs/sdk/09-message-rpc-manual.md +34 -10
- package/dist/agent-md.d.ts.map +1 -1
- package/dist/agent-md.js +24 -1
- package/dist/agent-md.js.map +1 -1
- package/dist/aid-store.d.ts.map +1 -1
- package/dist/aid-store.js +1 -3
- package/dist/aid-store.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 +1247 -269
- package/dist/client/delivery.d.ts +30 -7
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +354 -92
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +8 -8
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/lifecycle.d.ts.map +1 -1
- package/dist/client/lifecycle.js +41 -34
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +12 -0
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +205 -36
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts +1 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +72 -18
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +0 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +128 -45
- 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/facades.d.ts.map +1 -1
- package/dist/facades.js +7 -3
- package/dist/facades.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 +283 -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/_packed_docs//345/217/221/345/270/203/346/212/245/345/221/212-0.5.6.md +0 -260
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.10";
|
|
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
|
-
|
|
838
|
-
}
|
|
972
|
+
this._synchronousHandlerDepth += 1;
|
|
973
|
+
result = handler(payload);
|
|
974
|
+
} catch (exc) {
|
|
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;
|
|
839
983
|
} catch (exc) {
|
|
840
984
|
this._log.warn(`event ${event} handler execution exception:`, exc);
|
|
985
|
+
} finally {
|
|
986
|
+
this._handlerDepth -= 1;
|
|
841
987
|
}
|
|
842
988
|
}
|
|
843
989
|
}
|
|
@@ -1156,6 +1302,154 @@ var _noopLog3 = { error: () => {
|
|
|
1156
1302
|
}, debug: () => {
|
|
1157
1303
|
} };
|
|
1158
1304
|
var _rpcIdCounter = 0;
|
|
1305
|
+
var WORKER_WEBSOCKET_SOURCE = `
|
|
1306
|
+
let socket = null;
|
|
1307
|
+
self.onmessage = (event) => {
|
|
1308
|
+
const command = event.data || {};
|
|
1309
|
+
if (command.type === 'connect') {
|
|
1310
|
+
try {
|
|
1311
|
+
socket = new WebSocket(command.url);
|
|
1312
|
+
socket.onopen = () => self.postMessage({ type: 'open' });
|
|
1313
|
+
socket.onmessage = (message) => self.postMessage({ type: 'message', data: message.data });
|
|
1314
|
+
socket.onerror = () => self.postMessage({ type: 'error', message: 'websocket error' });
|
|
1315
|
+
socket.onclose = (close) => self.postMessage({
|
|
1316
|
+
type: 'close', code: close.code, reason: close.reason || '', wasClean: close.wasClean === true,
|
|
1317
|
+
});
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
self.postMessage({ type: 'error', message: error instanceof Error ? error.message : String(error) });
|
|
1320
|
+
}
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
if (command.type === 'send') {
|
|
1324
|
+
try {
|
|
1325
|
+
if (!socket || socket.readyState !== WebSocket.OPEN) throw new Error('websocket is not open');
|
|
1326
|
+
socket.send(command.data);
|
|
1327
|
+
self.postMessage({ type: 'send_result', id: command.id, ok: true });
|
|
1328
|
+
} catch (error) {
|
|
1329
|
+
self.postMessage({
|
|
1330
|
+
type: 'send_result', id: command.id, ok: false,
|
|
1331
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1332
|
+
});
|
|
1333
|
+
}
|
|
1334
|
+
return;
|
|
1335
|
+
}
|
|
1336
|
+
if (command.type === 'close' && socket) socket.close(command.code, command.reason);
|
|
1337
|
+
};
|
|
1338
|
+
`;
|
|
1339
|
+
var _WorkerWebSocketProxy = class _WorkerWebSocketProxy {
|
|
1340
|
+
constructor(url) {
|
|
1341
|
+
__publicField(this, "readyState", _WorkerWebSocketProxy.CONNECTING);
|
|
1342
|
+
__publicField(this, "onopen", null);
|
|
1343
|
+
__publicField(this, "onmessage", null);
|
|
1344
|
+
__publicField(this, "onerror", null);
|
|
1345
|
+
__publicField(this, "onclose", null);
|
|
1346
|
+
__publicField(this, "_worker");
|
|
1347
|
+
__publicField(this, "_workerUrl");
|
|
1348
|
+
__publicField(this, "_listeners", /* @__PURE__ */ new Map());
|
|
1349
|
+
__publicField(this, "_pendingSends", /* @__PURE__ */ new Map());
|
|
1350
|
+
__publicField(this, "_sendSeq", 0);
|
|
1351
|
+
this._workerUrl = URL.createObjectURL(new Blob([WORKER_WEBSOCKET_SOURCE], { type: "text/javascript" }));
|
|
1352
|
+
try {
|
|
1353
|
+
this._worker = new Worker(this._workerUrl);
|
|
1354
|
+
} catch (error) {
|
|
1355
|
+
URL.revokeObjectURL(this._workerUrl);
|
|
1356
|
+
throw error;
|
|
1357
|
+
}
|
|
1358
|
+
this._worker.onmessage = (event) => this._handleWorkerMessage(event.data);
|
|
1359
|
+
this._worker.onerror = (event) => this._emitError(event.message || "websocket worker error");
|
|
1360
|
+
this._worker.postMessage({ type: "connect", url });
|
|
1361
|
+
}
|
|
1362
|
+
send(data) {
|
|
1363
|
+
if (this.readyState !== _WorkerWebSocketProxy.OPEN) return Promise.reject(new Error("websocket is not open"));
|
|
1364
|
+
const id = ++this._sendSeq;
|
|
1365
|
+
return new Promise((resolve, reject) => {
|
|
1366
|
+
this._pendingSends.set(id, { resolve, reject });
|
|
1367
|
+
try {
|
|
1368
|
+
this._worker.postMessage({ type: "send", id, data });
|
|
1369
|
+
} catch (error) {
|
|
1370
|
+
this._pendingSends.delete(id);
|
|
1371
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
1372
|
+
}
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
close(code, reason) {
|
|
1376
|
+
if (this.readyState === _WorkerWebSocketProxy.CLOSED) return;
|
|
1377
|
+
this.readyState = _WorkerWebSocketProxy.CLOSING;
|
|
1378
|
+
this._worker.postMessage({ type: "close", code, reason });
|
|
1379
|
+
}
|
|
1380
|
+
addEventListener(type, listener) {
|
|
1381
|
+
const callback = typeof listener === "function" ? listener : (event) => listener.handleEvent(event);
|
|
1382
|
+
const listeners = this._listeners.get(type) ?? /* @__PURE__ */ new Set();
|
|
1383
|
+
listeners.add(callback);
|
|
1384
|
+
this._listeners.set(type, listeners);
|
|
1385
|
+
}
|
|
1386
|
+
removeEventListener(type, listener) {
|
|
1387
|
+
if (typeof listener === "function") this._listeners.get(type)?.delete(listener);
|
|
1388
|
+
}
|
|
1389
|
+
_handleWorkerMessage(reply) {
|
|
1390
|
+
if (reply.type === "send_result" && reply.id !== void 0) {
|
|
1391
|
+
const pending = this._pendingSends.get(reply.id);
|
|
1392
|
+
if (!pending) return;
|
|
1393
|
+
this._pendingSends.delete(reply.id);
|
|
1394
|
+
if (reply.ok) pending.resolve();
|
|
1395
|
+
else pending.reject(new Error(reply.error || "websocket send failed"));
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
if (reply.type === "open") {
|
|
1399
|
+
this.readyState = _WorkerWebSocketProxy.OPEN;
|
|
1400
|
+
const event = new Event("open");
|
|
1401
|
+
this.onopen?.(event);
|
|
1402
|
+
this._emit("open", event);
|
|
1403
|
+
return;
|
|
1404
|
+
}
|
|
1405
|
+
if (reply.type === "message") {
|
|
1406
|
+
const event = new MessageEvent("message", { data: reply.data });
|
|
1407
|
+
this.onmessage?.(event);
|
|
1408
|
+
this._emit("message", event);
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
if (reply.type === "error") {
|
|
1412
|
+
this._emitError(reply.message || "websocket worker error");
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
if (reply.type === "close") {
|
|
1416
|
+
this.readyState = _WorkerWebSocketProxy.CLOSED;
|
|
1417
|
+
for (const pending of this._pendingSends.values()) pending.reject(new Error("websocket closed"));
|
|
1418
|
+
this._pendingSends.clear();
|
|
1419
|
+
const event = new CloseEvent("close", {
|
|
1420
|
+
code: reply.code ?? 1006,
|
|
1421
|
+
reason: reply.reason ?? "",
|
|
1422
|
+
wasClean: reply.wasClean === true
|
|
1423
|
+
});
|
|
1424
|
+
this.onclose?.(event);
|
|
1425
|
+
this._emit("close", event);
|
|
1426
|
+
this._worker.terminate();
|
|
1427
|
+
URL.revokeObjectURL(this._workerUrl);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
_emitError(message) {
|
|
1431
|
+
const event = new ErrorEvent("error", { message });
|
|
1432
|
+
this.onerror?.(event);
|
|
1433
|
+
this._emit("error", event);
|
|
1434
|
+
}
|
|
1435
|
+
_emit(type, event) {
|
|
1436
|
+
for (const listener of [...this._listeners.get(type) ?? []]) listener(event);
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
__publicField(_WorkerWebSocketProxy, "CONNECTING", 0);
|
|
1440
|
+
__publicField(_WorkerWebSocketProxy, "OPEN", 1);
|
|
1441
|
+
__publicField(_WorkerWebSocketProxy, "CLOSING", 2);
|
|
1442
|
+
__publicField(_WorkerWebSocketProxy, "CLOSED", 3);
|
|
1443
|
+
var WorkerWebSocketProxy = _WorkerWebSocketProxy;
|
|
1444
|
+
function createTransportWebSocket(url) {
|
|
1445
|
+
if (typeof Worker === "function" && typeof Blob === "function" && typeof URL !== "undefined" && typeof URL.createObjectURL === "function" && typeof URL.revokeObjectURL === "function") {
|
|
1446
|
+
try {
|
|
1447
|
+
return new WorkerWebSocketProxy(url);
|
|
1448
|
+
} catch {
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
return new WebSocket(url);
|
|
1452
|
+
}
|
|
1159
1453
|
var TRACE_SPAN_DETAIL_FIELDS = [
|
|
1160
1454
|
"method",
|
|
1161
1455
|
"route",
|
|
@@ -1426,6 +1720,10 @@ var RPCTransport = class {
|
|
|
1426
1720
|
__publicField(this, "_traceMode", "off");
|
|
1427
1721
|
// Trace observer:observer(traceInfo) 在每次 RPC/事件携带 _trace 时调用
|
|
1428
1722
|
__publicField(this, "_traceObserver", null);
|
|
1723
|
+
// 每个 transport 实例一个轻量级网络 actor。命令只在此处启动网络操作,
|
|
1724
|
+
// 不等待 RPC 响应,避免应用回调中的 send 反向占住收包路径。
|
|
1725
|
+
__publicField(this, "_actorTail", Promise.resolve());
|
|
1726
|
+
__publicField(this, "_actorBusy", false);
|
|
1429
1727
|
this._dispatcher = opts.eventDispatcher;
|
|
1430
1728
|
this._timeout = opts.timeout ?? 10;
|
|
1431
1729
|
this._connectTimeout = opts.timeout ?? 10;
|
|
@@ -1455,15 +1753,18 @@ var RPCTransport = class {
|
|
|
1455
1753
|
setMetaObserver(observer) {
|
|
1456
1754
|
this._metaObserver = observer;
|
|
1457
1755
|
}
|
|
1458
|
-
|
|
1459
|
-
|
|
1756
|
+
_notifyMetaObserver(message) {
|
|
1757
|
+
const observer = this._metaObserver;
|
|
1758
|
+
if (observer === null) return;
|
|
1460
1759
|
const meta = message._meta;
|
|
1461
1760
|
if (!isJsonObject(meta)) return;
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1761
|
+
this._dispatcher.enqueueTask(async () => {
|
|
1762
|
+
try {
|
|
1763
|
+
await observer(meta);
|
|
1764
|
+
} catch (exc) {
|
|
1765
|
+
this._log.debug(`meta_observer raised: ${String(exc)}`);
|
|
1766
|
+
}
|
|
1767
|
+
});
|
|
1467
1768
|
}
|
|
1468
1769
|
/** 设置 trace 模式:off / log / diag */
|
|
1469
1770
|
setTraceMode(mode) {
|
|
@@ -1499,7 +1800,10 @@ var RPCTransport = class {
|
|
|
1499
1800
|
* 连接到 WebSocket URL。
|
|
1500
1801
|
* 等待首条消息,若为 challenge 则返回,否则进入消息路由。
|
|
1501
1802
|
*/
|
|
1502
|
-
|
|
1803
|
+
connect(url) {
|
|
1804
|
+
return this._enqueueActorStart(() => this._connectImpl(url));
|
|
1805
|
+
}
|
|
1806
|
+
async _connectImpl(url) {
|
|
1503
1807
|
const tStart = Date.now();
|
|
1504
1808
|
this._log.debug(`connect enter: url=${url}`);
|
|
1505
1809
|
const setup = await this._withConnectionSetup(async () => {
|
|
@@ -1508,7 +1812,7 @@ var RPCTransport = class {
|
|
|
1508
1812
|
this._lastCloseCode = null;
|
|
1509
1813
|
this._lastCloseReason = "";
|
|
1510
1814
|
const handshake = new Promise((resolve, reject) => {
|
|
1511
|
-
const ws =
|
|
1815
|
+
const ws = createTransportWebSocket(url);
|
|
1512
1816
|
this._ws = ws;
|
|
1513
1817
|
this._closed = false;
|
|
1514
1818
|
let initialResolved = false;
|
|
@@ -1637,7 +1941,10 @@ var RPCTransport = class {
|
|
|
1637
1941
|
return setup.handshake;
|
|
1638
1942
|
}
|
|
1639
1943
|
/** 关闭连接 */
|
|
1640
|
-
|
|
1944
|
+
close() {
|
|
1945
|
+
return this._enqueueActor(() => this._closeImpl());
|
|
1946
|
+
}
|
|
1947
|
+
async _closeImpl() {
|
|
1641
1948
|
await this._withConnectionSetup(() => this._closeUnlocked());
|
|
1642
1949
|
}
|
|
1643
1950
|
/** 已持有连接建立串行权时关闭当前 WebSocket。 */
|
|
@@ -1708,7 +2015,13 @@ var RPCTransport = class {
|
|
|
1708
2015
|
* 发起 JSON-RPC 2.0 调用。
|
|
1709
2016
|
* 返回 result 字段的值;若有 error 字段则抛出映射后的错误。
|
|
1710
2017
|
*/
|
|
1711
|
-
|
|
2018
|
+
call(method, params2, timeout, trace, background = false) {
|
|
2019
|
+
return this._enqueueActorStart(
|
|
2020
|
+
() => this._callImpl(method, params2, timeout, trace, background),
|
|
2021
|
+
() => new TimeoutError(`rpc cancelled: ${method}`, { retryable: true })
|
|
2022
|
+
);
|
|
2023
|
+
}
|
|
2024
|
+
_callImpl(method, params2, timeout, trace, background = false) {
|
|
1712
2025
|
if (this._closed || !this._ws) {
|
|
1713
2026
|
throw this._notConnectedError();
|
|
1714
2027
|
}
|
|
@@ -1749,7 +2062,7 @@ var RPCTransport = class {
|
|
|
1749
2062
|
this._drainRpcQueue();
|
|
1750
2063
|
}, effectiveTimeout);
|
|
1751
2064
|
const pending = {
|
|
1752
|
-
resolve:
|
|
2065
|
+
resolve: (response) => {
|
|
1753
2066
|
clearTimeout(timer);
|
|
1754
2067
|
const elapsed = Date.now() - tStart;
|
|
1755
2068
|
if (response.error !== void 0) {
|
|
@@ -1767,7 +2080,7 @@ var RPCTransport = class {
|
|
|
1767
2080
|
if (traceId) {
|
|
1768
2081
|
this._log.info(`[trace=${traceId}] rpc_recv method=${method} rpc_id=${rpcId} duration_ms=${elapsed} status=ok`);
|
|
1769
2082
|
}
|
|
1770
|
-
|
|
2083
|
+
this._notifyMetaObserver(response);
|
|
1771
2084
|
const respTrace = response._trace;
|
|
1772
2085
|
if (respTrace && typeof respTrace === "object" && !Array.isArray(respTrace)) {
|
|
1773
2086
|
this._handleResponseTrace(method, "ok", elapsed, respTrace);
|
|
@@ -1815,7 +2128,10 @@ var RPCTransport = class {
|
|
|
1815
2128
|
return Object.assign(promise, { cancel: () => cancelRpc?.() });
|
|
1816
2129
|
}
|
|
1817
2130
|
/** 发送 JSON-RPC 2.0 Notification,不分配 id,也不等待响应。 */
|
|
1818
|
-
|
|
2131
|
+
notify(method, params2) {
|
|
2132
|
+
return this._enqueueActorStart(() => this._notifyImpl(method, params2));
|
|
2133
|
+
}
|
|
2134
|
+
async _notifyImpl(method, params2) {
|
|
1819
2135
|
if (this._closed || !this._ws) {
|
|
1820
2136
|
throw this._notConnectedError();
|
|
1821
2137
|
}
|
|
@@ -1844,6 +2160,58 @@ var RPCTransport = class {
|
|
|
1844
2160
|
});
|
|
1845
2161
|
return run;
|
|
1846
2162
|
}
|
|
2163
|
+
_enqueueActor(operation) {
|
|
2164
|
+
this._actorBusy = true;
|
|
2165
|
+
const run = this._actorTail.then(async () => {
|
|
2166
|
+
return await operation();
|
|
2167
|
+
}, async () => {
|
|
2168
|
+
return await operation();
|
|
2169
|
+
});
|
|
2170
|
+
const tail = run.then(() => void 0, () => void 0);
|
|
2171
|
+
this._actorTail = tail;
|
|
2172
|
+
void tail.then(() => {
|
|
2173
|
+
if (this._actorTail === tail) this._actorBusy = false;
|
|
2174
|
+
});
|
|
2175
|
+
return run;
|
|
2176
|
+
}
|
|
2177
|
+
_enqueueActorStart(operation, cancellationError) {
|
|
2178
|
+
let resolveResult;
|
|
2179
|
+
let rejectResult;
|
|
2180
|
+
const result = new Promise((resolve, reject) => {
|
|
2181
|
+
resolveResult = resolve;
|
|
2182
|
+
rejectResult = reject;
|
|
2183
|
+
});
|
|
2184
|
+
let cancel;
|
|
2185
|
+
let cancelRequested = false;
|
|
2186
|
+
const launch = () => {
|
|
2187
|
+
if (cancelRequested) return;
|
|
2188
|
+
try {
|
|
2189
|
+
const inner = operation();
|
|
2190
|
+
cancel = inner.cancel;
|
|
2191
|
+
inner.then(resolveResult, rejectResult);
|
|
2192
|
+
} catch (err) {
|
|
2193
|
+
rejectResult(err);
|
|
2194
|
+
}
|
|
2195
|
+
};
|
|
2196
|
+
if (this._actorBusy) {
|
|
2197
|
+
const gate = this._actorTail.then(launch, launch);
|
|
2198
|
+
const tail = gate.then(() => void 0, () => void 0);
|
|
2199
|
+
this._actorTail = tail;
|
|
2200
|
+
void tail.then(() => {
|
|
2201
|
+
if (this._actorTail === tail) this._actorBusy = false;
|
|
2202
|
+
});
|
|
2203
|
+
} else {
|
|
2204
|
+
launch();
|
|
2205
|
+
}
|
|
2206
|
+
return Object.assign(result, {
|
|
2207
|
+
cancel: () => {
|
|
2208
|
+
if (cancelRequested) return;
|
|
2209
|
+
cancelRequested = true;
|
|
2210
|
+
if (cancel) cancel();
|
|
2211
|
+
else rejectResult(cancellationError?.() ?? new TimeoutError("rpc cancelled", { retryable: true }));
|
|
2212
|
+
}
|
|
2213
|
+
});
|
|
2214
|
+
}
|
|
1847
2215
|
_sendText(payload, context, beforeSend) {
|
|
1848
2216
|
return this._enqueueSend(async () => {
|
|
1849
2217
|
if (beforeSend && !beforeSend()) {
|
|
@@ -1854,7 +2222,7 @@ var RPCTransport = class {
|
|
|
1854
2222
|
}
|
|
1855
2223
|
const ws = this._ws;
|
|
1856
2224
|
try {
|
|
1857
|
-
ws.send(payload);
|
|
2225
|
+
await Promise.resolve(ws.send(payload));
|
|
1858
2226
|
} catch (err) {
|
|
1859
2227
|
throw await this._sendFailureError(context, err, ws);
|
|
1860
2228
|
}
|
|
@@ -1978,7 +2346,14 @@ var RPCTransport = class {
|
|
|
1978
2346
|
const enriched = { ...respTrace, spans };
|
|
1979
2347
|
this._log.info(traceDisplay(method, status, elapsedMs, respTrace, spans));
|
|
1980
2348
|
if (this._traceObserver !== null) {
|
|
1981
|
-
this._traceObserver
|
|
2349
|
+
const observer = this._traceObserver;
|
|
2350
|
+
this._dispatcher.enqueueTask(async () => {
|
|
2351
|
+
try {
|
|
2352
|
+
await observer({ type: "rpc", method, trace: enriched, status, duration_ms: elapsedMs });
|
|
2353
|
+
} catch (err) {
|
|
2354
|
+
this._log.debug(`trace observer raised: ${err instanceof Error ? err.message : String(err)}`);
|
|
2355
|
+
}
|
|
2356
|
+
});
|
|
1982
2357
|
}
|
|
1983
2358
|
} catch (err) {
|
|
1984
2359
|
this._log.debug(`trace handling raised: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -2019,10 +2394,17 @@ var RPCTransport = class {
|
|
|
2019
2394
|
this._backgroundRpcQueue = [];
|
|
2020
2395
|
if (!wasClosed) {
|
|
2021
2396
|
const error = new ConnectionError(`websocket closed: code=${event.code} reason=${event.reason}`);
|
|
2022
|
-
this._dispatcher.publish("connection.error", { error });
|
|
2023
2397
|
if (this._onDisconnect) {
|
|
2024
|
-
|
|
2398
|
+
const onDisconnect = this._onDisconnect;
|
|
2399
|
+
this._dispatcher.enqueueTask(async () => {
|
|
2400
|
+
try {
|
|
2401
|
+
await onDisconnect(error, event.code);
|
|
2402
|
+
} catch (exc) {
|
|
2403
|
+
this._log.warn("[aun_core.transport] disconnect callback exception:", exc);
|
|
2404
|
+
}
|
|
2405
|
+
});
|
|
2025
2406
|
}
|
|
2407
|
+
this._dispatcher.enqueue("connection.error", { error });
|
|
2026
2408
|
}
|
|
2027
2409
|
}
|
|
2028
2410
|
_notConnectedError() {
|
|
@@ -2050,39 +2432,43 @@ var RPCTransport = class {
|
|
|
2050
2432
|
if (method === "challenge") {
|
|
2051
2433
|
this._challenge = message;
|
|
2052
2434
|
this._log.debug("challenge received");
|
|
2053
|
-
this._dispatcher.
|
|
2435
|
+
this._dispatcher.enqueue("connection.challenge", message.params ?? {});
|
|
2054
2436
|
return;
|
|
2055
2437
|
}
|
|
2056
2438
|
if (method.startsWith("event/")) {
|
|
2057
2439
|
const protocolEvent = method.slice(6);
|
|
2058
2440
|
const sdkEvent = EVENT_NAME_MAP[protocolEvent] ?? protocolEvent;
|
|
2059
2441
|
this._log.debug(`event recv: event=${sdkEvent} ${summarizeDict(message.params, DIAG_RESULT_FIELDS)}`);
|
|
2060
|
-
|
|
2442
|
+
this._notifyMetaObserver(message);
|
|
2061
2443
|
const params2 = message.params ?? {};
|
|
2062
2444
|
if ("_trace" in params2) {
|
|
2063
2445
|
const eventTrace = params2._trace;
|
|
2064
2446
|
delete params2._trace;
|
|
2065
2447
|
if (eventTrace && typeof eventTrace === "object" && !Array.isArray(eventTrace)) {
|
|
2066
2448
|
if (this._traceObserver !== null) {
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2449
|
+
const observer = this._traceObserver;
|
|
2450
|
+
const tracePayload = eventTrace;
|
|
2451
|
+
this._dispatcher.enqueueTask(async () => {
|
|
2452
|
+
try {
|
|
2453
|
+
await observer({ type: "event", event: sdkEvent, trace: tracePayload });
|
|
2454
|
+
} catch {
|
|
2455
|
+
}
|
|
2456
|
+
});
|
|
2071
2457
|
}
|
|
2072
2458
|
const traceObj = eventTrace;
|
|
2073
2459
|
this._log.info(`[trace=${String(traceObj.trace_id ?? "")}] event_recv event=${sdkEvent}`);
|
|
2074
2460
|
}
|
|
2075
2461
|
}
|
|
2076
2462
|
if (sdkEvent.startsWith("app.")) {
|
|
2077
|
-
this._dispatcher.
|
|
2463
|
+
this._dispatcher.enqueue(sdkEvent, params2);
|
|
2078
2464
|
return;
|
|
2079
2465
|
}
|
|
2080
|
-
this._dispatcher.
|
|
2466
|
+
this._dispatcher.enqueue(`_raw.${sdkEvent}`, params2);
|
|
2081
2467
|
return;
|
|
2082
2468
|
}
|
|
2083
|
-
|
|
2469
|
+
this._notifyMetaObserver(message);
|
|
2084
2470
|
this._log.debug(`notification recv: method=${method || "<no-method>"}`);
|
|
2085
|
-
this._dispatcher.
|
|
2471
|
+
this._dispatcher.enqueue("notification", message);
|
|
2086
2472
|
}
|
|
2087
2473
|
_decodeMessage(raw) {
|
|
2088
2474
|
if (isJsonObject(raw)) {
|
|
@@ -2806,7 +3192,7 @@ var _AuthFlow = class _AuthFlow {
|
|
|
2806
3192
|
return new Promise((resolve, reject) => {
|
|
2807
3193
|
let ws;
|
|
2808
3194
|
try {
|
|
2809
|
-
ws =
|
|
3195
|
+
ws = createTransportWebSocket(gatewayUrl);
|
|
2810
3196
|
} catch (e) {
|
|
2811
3197
|
reject(new AuthError(`WebSocket \u8FDE\u63A5\u5931\u8D25: ${gatewayUrl}`));
|
|
2812
3198
|
return;
|
|
@@ -2849,7 +3235,19 @@ var _AuthFlow = class _AuthFlow {
|
|
|
2849
3235
|
params: params2
|
|
2850
3236
|
});
|
|
2851
3237
|
this._log.debug(`short RPC request full: ${JSON.stringify(redactRpcLogPayload(JSON.parse(requestPayload)))}`);
|
|
2852
|
-
ws.send(requestPayload);
|
|
3238
|
+
const sendResult = ws.send(requestPayload);
|
|
3239
|
+
if (sendResult && typeof sendResult.then === "function") {
|
|
3240
|
+
void Promise.resolve(sendResult).catch((error) => {
|
|
3241
|
+
if (settled) return;
|
|
3242
|
+
settled = true;
|
|
3243
|
+
globalThis.clearTimeout(timeout);
|
|
3244
|
+
try {
|
|
3245
|
+
ws.close();
|
|
3246
|
+
} catch {
|
|
3247
|
+
}
|
|
3248
|
+
reject(error instanceof Error ? error : new AuthError(String(error)));
|
|
3249
|
+
});
|
|
3250
|
+
}
|
|
2853
3251
|
return;
|
|
2854
3252
|
}
|
|
2855
3253
|
if (!isJsonObject(msg) || msg.id !== requestId) return;
|
|
@@ -4507,10 +4905,15 @@ function p2pAppEventFromPlainPullMessage(message) {
|
|
|
4507
4905
|
var MessageDeliveryEngine = class {
|
|
4508
4906
|
constructor(runtime) {
|
|
4509
4907
|
__publicField(this, "runtime");
|
|
4908
|
+
__publicField(this, "pendingPullDeliveryChanges", null);
|
|
4909
|
+
__publicField(this, "deliveryGeneration", 0);
|
|
4510
4910
|
__publicField(this, "realtimeTailResults", null);
|
|
4511
4911
|
__publicField(this, "realtimeSyncing", null);
|
|
4512
4912
|
__publicField(this, "pendingP2pPullUpper", null);
|
|
4513
4913
|
__publicField(this, "pendingGroupPullUpper", null);
|
|
4914
|
+
__publicField(this, "pendingPullNoProgressAcks", null);
|
|
4915
|
+
__publicField(this, "onlineUnreadHintTargets", null);
|
|
4916
|
+
__publicField(this, "onlineUnreadHintOwners", null);
|
|
4514
4917
|
__publicField(this, "realtimeAcking", null);
|
|
4515
4918
|
__publicField(this, "pendingP2PInlineAcks", null);
|
|
4516
4919
|
__publicField(this, "pendingGroupInlineAcks", null);
|
|
@@ -4523,9 +4926,15 @@ var MessageDeliveryEngine = class {
|
|
|
4523
4926
|
}
|
|
4524
4927
|
resetInlineAckState() {
|
|
4525
4928
|
this.inlineGeneration += 1;
|
|
4929
|
+
this.deliveryGeneration += 1;
|
|
4930
|
+
this.pendingPullDeliveryChanges = null;
|
|
4931
|
+
void this.runtime.client._rpcPipeline?.invalidatePulls?.();
|
|
4526
4932
|
this.realtimeSyncing = null;
|
|
4527
4933
|
this.pendingP2pPullUpper = null;
|
|
4528
4934
|
this.pendingGroupPullUpper = null;
|
|
4935
|
+
this.pendingPullNoProgressAcks = null;
|
|
4936
|
+
this.onlineUnreadHintTargets = null;
|
|
4937
|
+
this.onlineUnreadHintOwners = null;
|
|
4529
4938
|
this.realtimeTailResults = null;
|
|
4530
4939
|
this.realtimeAcking = null;
|
|
4531
4940
|
this.pendingP2PInlineAcks = null;
|
|
@@ -4535,6 +4944,16 @@ var MessageDeliveryEngine = class {
|
|
|
4535
4944
|
isInlineGenerationCurrent(generation) {
|
|
4536
4945
|
return generation === this.inlineGeneration;
|
|
4537
4946
|
}
|
|
4947
|
+
isPullOperationCurrent() {
|
|
4948
|
+
const client = this.runtime.client;
|
|
4949
|
+
const generation = client._pullOperationGeneration;
|
|
4950
|
+
if (generation === void 0) return true;
|
|
4951
|
+
const pipeline = client._rpcPipeline;
|
|
4952
|
+
return typeof pipeline?.isPullGenerationCurrent === "function" ? pipeline.isPullGenerationCurrent(generation) : true;
|
|
4953
|
+
}
|
|
4954
|
+
ensurePullOperationCurrent() {
|
|
4955
|
+
if (!this.isPullOperationCurrent()) throw new Error("pull invalidated");
|
|
4956
|
+
}
|
|
4538
4957
|
captureInlineGeneration() {
|
|
4539
4958
|
return this.inlineGeneration;
|
|
4540
4959
|
}
|
|
@@ -4556,6 +4975,67 @@ var MessageDeliveryEngine = class {
|
|
|
4556
4975
|
if (!ns) return;
|
|
4557
4976
|
this.schedulePendingPullIfNeeded(ns, "pull-gate-idle");
|
|
4558
4977
|
}
|
|
4978
|
+
onPullWorkSettled() {
|
|
4979
|
+
const pipeline = this.runtime.client._rpcPipeline;
|
|
4980
|
+
if (pipeline?.hasAnyPullActivity?.() === true) return;
|
|
4981
|
+
void this.flushPullDeliveryChanges();
|
|
4982
|
+
}
|
|
4983
|
+
deliveryChangeNamespace(event, payload, ns = "") {
|
|
4984
|
+
if (event === "message.received") return "p2p";
|
|
4985
|
+
if (event !== "group.message_created") return "";
|
|
4986
|
+
if (!isJsonObject(payload)) return "";
|
|
4987
|
+
const aid = String(this.runtime.client._aid ?? "").trim().toLowerCase();
|
|
4988
|
+
const dot = aid.indexOf(".");
|
|
4989
|
+
const localIssuer = dot > 0 ? aid.slice(dot + 1) : "";
|
|
4990
|
+
const fallback = ns.startsWith("group:") ? ns.slice("group:".length) : "";
|
|
4991
|
+
const groupAid = normalizeGroupAid(payload.group_aid ?? payload.group_id ?? fallback, { localIssuer });
|
|
4992
|
+
return groupAid.includes(".") ? `group:${groupAid}` : "";
|
|
4993
|
+
}
|
|
4994
|
+
isDeliverableMessageBody(event, payload) {
|
|
4995
|
+
if (event !== "message.received" && event !== "group.message_created" || !isJsonObject(payload)) return false;
|
|
4996
|
+
const messageId = typeof payload.message_id === "string" && payload.message_id.trim().length > 0;
|
|
4997
|
+
const seq2 = typeof payload.seq === "number" && Number.isSafeInteger(payload.seq) && payload.seq > 0;
|
|
4998
|
+
return messageId || seq2;
|
|
4999
|
+
}
|
|
5000
|
+
recordDeliveryChange(changes, event, payload, ns, generation) {
|
|
5001
|
+
if (generation !== this.deliveryGeneration || !this.isDeliverableMessageBody(event, payload)) return;
|
|
5002
|
+
const namespace = this.deliveryChangeNamespace(event, payload, ns);
|
|
5003
|
+
if (!namespace) return;
|
|
5004
|
+
changes.set(namespace, (changes.get(namespace) ?? 0) + 1);
|
|
5005
|
+
}
|
|
5006
|
+
deliveryChangesPayload(changes) {
|
|
5007
|
+
return [...changes.entries()].filter(([, deliveredCount]) => deliveredCount > 0).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([namespace, deliveredCount]) => ({ namespace, delivered_count: deliveredCount }));
|
|
5008
|
+
}
|
|
5009
|
+
createDeliveryChangeBatch(trigger) {
|
|
5010
|
+
return { generation: this.deliveryGeneration, trigger, changes: /* @__PURE__ */ new Map(), closed: false };
|
|
5011
|
+
}
|
|
5012
|
+
async flushRealtimeDeliveryChanges(batch) {
|
|
5013
|
+
if (batch.closed) return;
|
|
5014
|
+
batch.closed = true;
|
|
5015
|
+
if (batch.generation !== this.deliveryGeneration) return;
|
|
5016
|
+
if (this.deliveryChangesPayload(batch.changes).length === 0) return;
|
|
5017
|
+
const pendingPull = this.pendingPullDeliveryChanges;
|
|
5018
|
+
this.pendingPullDeliveryChanges = null;
|
|
5019
|
+
if (pendingPull) {
|
|
5020
|
+
for (const [namespace, count] of pendingPull) {
|
|
5021
|
+
batch.changes.set(namespace, (batch.changes.get(namespace) ?? 0) + count);
|
|
5022
|
+
}
|
|
5023
|
+
}
|
|
5024
|
+
if (batch.generation !== this.deliveryGeneration) return;
|
|
5025
|
+
const changes = this.deliveryChangesPayload(batch.changes);
|
|
5026
|
+
if (changes.length === 0) return;
|
|
5027
|
+
this.runtime.client._dispatcher.enqueue("delivery.changed", { trigger: batch.trigger, changes });
|
|
5028
|
+
}
|
|
5029
|
+
async flushPullDeliveryChanges() {
|
|
5030
|
+
const generation = this.deliveryGeneration;
|
|
5031
|
+
const pending = this.pendingPullDeliveryChanges;
|
|
5032
|
+
if (!pending || pending.size === 0 || generation !== this.deliveryGeneration) return;
|
|
5033
|
+
this.pendingPullDeliveryChanges = null;
|
|
5034
|
+
if (generation !== this.deliveryGeneration) return;
|
|
5035
|
+
const changes = this.deliveryChangesPayload(pending);
|
|
5036
|
+
if (changes.length === 0) return;
|
|
5037
|
+
this.runtime.client._dispatcher.enqueue("delivery.changed", { trigger: "pull_drained", changes });
|
|
5038
|
+
}
|
|
4559
5039
|
recordPendingPull(ns, seq2) {
|
|
4560
5040
|
if (!ns || !Number.isSafeInteger(seq2) || seq2 <= 0) return;
|
|
4561
5041
|
const pending = ns.startsWith("p2p:") ? this.pendingP2pPullUpper ?? /* @__PURE__ */ new Map() : this.pendingGroupPullUpper ?? /* @__PURE__ */ new Map();
|
|
@@ -4565,7 +5045,15 @@ var MessageDeliveryEngine = class {
|
|
|
4565
5045
|
}
|
|
4566
5046
|
consumePendingPull(ns, throughSeq) {
|
|
4567
5047
|
const pending = ns.startsWith("p2p:") ? this.pendingP2pPullUpper : this.pendingGroupPullUpper;
|
|
4568
|
-
if ((pending?.get(ns) ?? 0) <= throughSeq)
|
|
5048
|
+
if ((pending?.get(ns) ?? 0) <= throughSeq) {
|
|
5049
|
+
pending?.delete(ns);
|
|
5050
|
+
this.pendingPullNoProgressAcks?.delete(ns);
|
|
5051
|
+
}
|
|
5052
|
+
}
|
|
5053
|
+
markPendingPullNoProgress(ns, ack) {
|
|
5054
|
+
const blocked = this.pendingPullNoProgressAcks ?? /* @__PURE__ */ new Map();
|
|
5055
|
+
this.pendingPullNoProgressAcks = blocked;
|
|
5056
|
+
blocked.set(ns, ack);
|
|
4569
5057
|
}
|
|
4570
5058
|
schedulePendingPullIfNeeded(ns, reason) {
|
|
4571
5059
|
const pending = ns.startsWith("p2p:") ? this.pendingP2pPullUpper : this.pendingGroupPullUpper;
|
|
@@ -4573,15 +5061,20 @@ var MessageDeliveryEngine = class {
|
|
|
4573
5061
|
const upper = pending.get(ns) ?? 0;
|
|
4574
5062
|
if (upper <= 0) {
|
|
4575
5063
|
pending.delete(ns);
|
|
5064
|
+
this.pendingPullNoProgressAcks?.delete(ns);
|
|
4576
5065
|
return false;
|
|
4577
5066
|
}
|
|
4578
5067
|
const client = this.runtime.client;
|
|
4579
5068
|
const contiguous = client._seqTracker.getContiguousSeq(ns);
|
|
4580
5069
|
if (upper <= contiguous) {
|
|
4581
5070
|
pending.delete(ns);
|
|
5071
|
+
this.pendingPullNoProgressAcks?.delete(ns);
|
|
4582
5072
|
client._clientLog?.debug(`pending pull upper already covered: ns=${ns}, upper_seq=${upper}, contiguous=${contiguous}, reason=${reason}`);
|
|
4583
5073
|
return false;
|
|
4584
5074
|
}
|
|
5075
|
+
const blockedAck = this.pendingPullNoProgressAcks?.get(ns);
|
|
5076
|
+
if (blockedAck !== void 0 && contiguous <= blockedAck) return false;
|
|
5077
|
+
this.pendingPullNoProgressAcks?.delete(ns);
|
|
4585
5078
|
if (client.state !== "ready" /* READY */ || client._closing || this.realtimeSyncing?.has(ns) || client._rpcPipeline?.hasPullActivity?.(ns, false)) return false;
|
|
4586
5079
|
pending.delete(ns);
|
|
4587
5080
|
client._clientLog?.debug(`pending push follow-up pull scheduled: ns=${ns}, upper_seq=${upper}, reason=${reason}`);
|
|
@@ -4947,6 +5440,15 @@ var MessageDeliveryEngine = class {
|
|
|
4947
5440
|
value = row.effective_ack_seq;
|
|
4948
5441
|
} else if (Object.prototype.hasOwnProperty.call(row, "ack_seq")) {
|
|
4949
5442
|
value = row.ack_seq;
|
|
5443
|
+
} else if (Object.prototype.hasOwnProperty.call(row, "cursor")) {
|
|
5444
|
+
const cursor = row.cursor;
|
|
5445
|
+
const cursorRow = isJsonObject(cursor) ? cursor : null;
|
|
5446
|
+
if (cursorRow) {
|
|
5447
|
+
if (!Object.prototype.hasOwnProperty.call(cursorRow, "current_seq")) return 0;
|
|
5448
|
+
value = cursorRow.current_seq;
|
|
5449
|
+
} else {
|
|
5450
|
+
value = cursor;
|
|
5451
|
+
}
|
|
4950
5452
|
} else {
|
|
4951
5453
|
return requestedSeq;
|
|
4952
5454
|
}
|
|
@@ -4961,6 +5463,7 @@ var MessageDeliveryEngine = class {
|
|
|
4961
5463
|
}
|
|
4962
5464
|
async confirmPlainForwardAck(ns, method, ackSeq, groupId = "") {
|
|
4963
5465
|
const client = this.runtime.client;
|
|
5466
|
+
this.ensurePullOperationCurrent();
|
|
4964
5467
|
const coordinator = this.forwardCoordinator();
|
|
4965
5468
|
const generation = this.captureInlineGeneration();
|
|
4966
5469
|
coordinator.recordForwardAck(ns, ackSeq);
|
|
@@ -4977,6 +5480,7 @@ var MessageDeliveryEngine = class {
|
|
|
4977
5480
|
_rpc_background: true
|
|
4978
5481
|
};
|
|
4979
5482
|
const result = await client._rpcPipeline.rawCall(method, params2, { background: true });
|
|
5483
|
+
this.ensurePullOperationCurrent();
|
|
4980
5484
|
const actualAckSeq = this.resolveForwardAckSeq(result, ackSeq);
|
|
4981
5485
|
if (actualAckSeq < ackSeq) {
|
|
4982
5486
|
throw new Error(`${method} server ACK watermark ${actualAckSeq} is below requested ${ackSeq}`);
|
|
@@ -4993,6 +5497,7 @@ var MessageDeliveryEngine = class {
|
|
|
4993
5497
|
throw new Error(`${method} response must be an object`);
|
|
4994
5498
|
}
|
|
4995
5499
|
const client = this.runtime.client;
|
|
5500
|
+
this.ensurePullOperationCurrent();
|
|
4996
5501
|
const response = result;
|
|
4997
5502
|
const messages = deduplicatePlainForwardPageMessages(response.messages, afterSeq);
|
|
4998
5503
|
const coordinator = this.forwardCoordinator();
|
|
@@ -5020,24 +5525,29 @@ var MessageDeliveryEngine = class {
|
|
|
5020
5525
|
let committed = false;
|
|
5021
5526
|
try {
|
|
5022
5527
|
for (const rawMessage of messages) {
|
|
5528
|
+
this.ensurePullOperationCurrent();
|
|
5023
5529
|
const seq2 = positiveSafeSequenceHint(rawMessage.seq);
|
|
5024
5530
|
if (method === "message.pull") {
|
|
5025
5531
|
const appEvent = p2pAppEventFromPlainPullMessage(rawMessage);
|
|
5026
5532
|
if (await this.publishPulledMessage(appEvent.event, ns, seq2, appEvent.payload, false)) {
|
|
5027
5533
|
publishedCount += 1;
|
|
5028
5534
|
}
|
|
5535
|
+
this.ensurePullOperationCurrent();
|
|
5029
5536
|
continue;
|
|
5030
5537
|
}
|
|
5031
5538
|
const message = normalizeGroupMentionMode(rawMessage);
|
|
5032
5539
|
if (this.recallEventFromGroupMessage(message)) {
|
|
5033
5540
|
if (await this.publishGroupRecallTombstone(groupId, seq2, message)) {
|
|
5541
|
+
this.ensurePullOperationCurrent();
|
|
5034
5542
|
this.markPublishedSeq(ns, seq2);
|
|
5035
5543
|
publishedCount += 1;
|
|
5036
5544
|
}
|
|
5037
5545
|
} else if (await this.publishPulledMessage("group.message_created", ns, seq2, message, false)) {
|
|
5546
|
+
this.ensurePullOperationCurrent();
|
|
5038
5547
|
publishedCount += 1;
|
|
5039
5548
|
}
|
|
5040
5549
|
}
|
|
5550
|
+
this.ensurePullOperationCurrent();
|
|
5041
5551
|
if (messages.length > 0) client._seqTracker.onPullResult(ns, messages, afterSeq);
|
|
5042
5552
|
const commitTarget = Math.max(
|
|
5043
5553
|
client._seqTracker.getContiguousSeq(ns),
|
|
@@ -5049,17 +5559,20 @@ var MessageDeliveryEngine = class {
|
|
|
5049
5559
|
client._seqTracker.forceContiguousSeq(ns, commitTarget);
|
|
5050
5560
|
}
|
|
5051
5561
|
if (client._seqTracker.getContiguousSeq(ns) !== pageContigBefore) {
|
|
5562
|
+
this.ensurePullOperationCurrent();
|
|
5052
5563
|
await this.drainOrderedMessages(ns, void 0, false, false);
|
|
5564
|
+
this.ensurePullOperationCurrent();
|
|
5053
5565
|
await client._commitSeqTrackerState(ns);
|
|
5054
5566
|
}
|
|
5055
5567
|
committed = true;
|
|
5056
5568
|
} catch (exc) {
|
|
5057
|
-
if (!committed && typeof client._seqTracker.restoreNamespaceSnapshot === "function") {
|
|
5569
|
+
if (this.isPullOperationCurrent() && !committed && typeof client._seqTracker.restoreNamespaceSnapshot === "function") {
|
|
5058
5570
|
client._seqTracker.restoreNamespaceSnapshot(ns, pageTrackerSnapshot);
|
|
5059
5571
|
this.dropSeqTrackerPending(ns);
|
|
5060
5572
|
}
|
|
5061
5573
|
throw exc;
|
|
5062
5574
|
}
|
|
5575
|
+
this.ensurePullOperationCurrent();
|
|
5063
5576
|
const committedAck = client._seqTracker.getContiguousSeq(ns);
|
|
5064
5577
|
if (deferredServerCursor > 0 && committedAck >= deferredServerCursor) {
|
|
5065
5578
|
coordinator.clearForwardCursor(ns, committedAck);
|
|
@@ -5092,18 +5605,19 @@ var MessageDeliveryEngine = class {
|
|
|
5092
5605
|
if (clampedAckSeq < pendingAckSeq) {
|
|
5093
5606
|
throw new Error(`${ackMethod} cannot confirm pending Forward watermark ${pendingAckSeq}`);
|
|
5094
5607
|
}
|
|
5608
|
+
this.ensurePullOperationCurrent();
|
|
5095
5609
|
await this.confirmPlainForwardAck(ns, ackMethod, pendingAckSeq, groupId);
|
|
5096
5610
|
}
|
|
5097
5611
|
return { rawCount: messages.length, publishedCount };
|
|
5098
5612
|
}
|
|
5099
|
-
enqueueOrderedMessage(ns, event, seq2, payload) {
|
|
5613
|
+
enqueueOrderedMessage(ns, event, seq2, payload, source = "push") {
|
|
5100
5614
|
const client = this.runtime.client;
|
|
5101
5615
|
let queue = client._pendingOrderedMsgs.get(ns);
|
|
5102
5616
|
if (!queue) {
|
|
5103
5617
|
queue = /* @__PURE__ */ new Map();
|
|
5104
5618
|
client._pendingOrderedMsgs.set(ns, queue);
|
|
5105
5619
|
}
|
|
5106
|
-
queue.set(seq2, { event, payload });
|
|
5620
|
+
queue.set(seq2, { event, payload, source });
|
|
5107
5621
|
if (queue.size > PENDING_ORDERED_LIMIT) {
|
|
5108
5622
|
const drop = [...queue.keys()].sort((a, b) => a - b).slice(0, queue.size - PENDING_ORDERED_LIMIT);
|
|
5109
5623
|
for (const oldSeq of drop) queue.delete(oldSeq);
|
|
@@ -5112,7 +5626,7 @@ var MessageDeliveryEngine = class {
|
|
|
5112
5626
|
isGroupEventNamespace(ns) {
|
|
5113
5627
|
return ns.startsWith("group_event:");
|
|
5114
5628
|
}
|
|
5115
|
-
async publishOrderedQueueItem(ns, event, seq2, payload, pullResponse = false) {
|
|
5629
|
+
async publishOrderedQueueItem(ns, event, seq2, payload, pullResponse = false, source = "push", batch) {
|
|
5116
5630
|
const client = this.runtime.client;
|
|
5117
5631
|
if (event === "group.changed" && this.isGroupEventNamespace(ns)) {
|
|
5118
5632
|
await this.publishOrderedGroupChanged(payload);
|
|
@@ -5123,10 +5637,10 @@ var MessageDeliveryEngine = class {
|
|
|
5123
5637
|
return;
|
|
5124
5638
|
}
|
|
5125
5639
|
if (pullResponse) {
|
|
5126
|
-
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload));
|
|
5640
|
+
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source, ns, batch));
|
|
5127
5641
|
return;
|
|
5128
5642
|
}
|
|
5129
|
-
await client._publishAppEvent(event, payload);
|
|
5643
|
+
await client._publishAppEvent(event, payload, source, ns, batch);
|
|
5130
5644
|
}
|
|
5131
5645
|
async publishOrderedGroupChanged(payload) {
|
|
5132
5646
|
const client = this.runtime.client;
|
|
@@ -5269,6 +5783,7 @@ var MessageDeliveryEngine = class {
|
|
|
5269
5783
|
setIfPresent("type", firstValue(body.type, params2.type, params2.message_type, params2.payload_type));
|
|
5270
5784
|
setIfPresent("kind", firstValue(body.kind, params2.kind));
|
|
5271
5785
|
setIfPresent("version", firstValue(body.version, params2.version));
|
|
5786
|
+
setIfPresent("message_id", firstValue(params2.message_id, body.message_id, resultObj.message_id));
|
|
5272
5787
|
setIfPresent("timestamp", firstValue(params2.timestamp, resultObj.timestamp, resultObj.created_at, resultObj.t_server, Date.now()));
|
|
5273
5788
|
envelope.encrypted = Boolean(encrypted);
|
|
5274
5789
|
const context = this.envelopeMetadata(params2.context);
|
|
@@ -5546,8 +6061,9 @@ var MessageDeliveryEngine = class {
|
|
|
5546
6061
|
}
|
|
5547
6062
|
if (contig !== contigBefore) this.persistSeq(ns);
|
|
5548
6063
|
}
|
|
5549
|
-
async publishAppEvent(event, payload) {
|
|
6064
|
+
async publishAppEvent(event, payload, source = "", ns = "", batch) {
|
|
5550
6065
|
const client = this.runtime.client;
|
|
6066
|
+
const generation = this.deliveryGeneration;
|
|
5551
6067
|
if ((event === "message.received" || event === "group.message_created") && isJsonObject(payload)) {
|
|
5552
6068
|
client._maybeAppendEchoTraceReceive(payload);
|
|
5553
6069
|
}
|
|
@@ -5566,7 +6082,19 @@ var MessageDeliveryEngine = class {
|
|
|
5566
6082
|
client._clientLog.debug(`agent_md etag inject skipped: ${String(exc)}`);
|
|
5567
6083
|
}
|
|
5568
6084
|
}
|
|
5569
|
-
|
|
6085
|
+
client._dispatcher.enqueue(event, this.normalizePublishedMessagePayload(event, payload));
|
|
6086
|
+
if (generation !== this.deliveryGeneration) return;
|
|
6087
|
+
if (source !== "pull" && source !== "tail" && source !== "pending_retry" && source !== "push" && source !== "inline_push") return;
|
|
6088
|
+
if (source === "push" || source === "inline_push") {
|
|
6089
|
+
const localBatch = batch ?? this.createDeliveryChangeBatch(source);
|
|
6090
|
+
this.recordDeliveryChange(localBatch.changes, event, payload, ns, generation);
|
|
6091
|
+
if (!batch) await this.flushRealtimeDeliveryChanges(localBatch);
|
|
6092
|
+
} else {
|
|
6093
|
+
const changes = this.pendingPullDeliveryChanges ?? /* @__PURE__ */ new Map();
|
|
6094
|
+
this.pendingPullDeliveryChanges = changes;
|
|
6095
|
+
this.recordDeliveryChange(changes, event, payload, ns, generation);
|
|
6096
|
+
if (client._rpcPipeline?.hasAnyPullActivity?.() !== true) await this.flushPullDeliveryChanges();
|
|
6097
|
+
}
|
|
5570
6098
|
}
|
|
5571
6099
|
messageTargetsCurrentInstance(message) {
|
|
5572
6100
|
if (!isJsonObject(message)) return true;
|
|
@@ -5699,7 +6227,7 @@ var MessageDeliveryEngine = class {
|
|
|
5699
6227
|
const client = this.runtime.client;
|
|
5700
6228
|
try {
|
|
5701
6229
|
if (!isJsonObject(data)) {
|
|
5702
|
-
await client._publishAppEvent("message.received", data);
|
|
6230
|
+
await client._publishAppEvent("message.received", data, "push");
|
|
5703
6231
|
return;
|
|
5704
6232
|
}
|
|
5705
6233
|
const msg = { ...data };
|
|
@@ -5744,7 +6272,7 @@ var MessageDeliveryEngine = class {
|
|
|
5744
6272
|
await client._publishEncryptedPushMessage("message.received", "message.undecryptable", "", seq2 ?? 0, msg, false);
|
|
5745
6273
|
return;
|
|
5746
6274
|
}
|
|
5747
|
-
await client._publishAppEvent("message.received", msg);
|
|
6275
|
+
await client._publishAppEvent("message.received", msg, "push");
|
|
5748
6276
|
}
|
|
5749
6277
|
} catch (exc) {
|
|
5750
6278
|
client._clientLog.warn(`P2P push processing failed:${String(exc)}`);
|
|
@@ -5783,7 +6311,7 @@ var MessageDeliveryEngine = class {
|
|
|
5783
6311
|
const client = this.runtime.client;
|
|
5784
6312
|
try {
|
|
5785
6313
|
if (!isJsonObject(data)) {
|
|
5786
|
-
await client._publishAppEvent("group.message_created", data);
|
|
6314
|
+
await client._publishAppEvent("group.message_created", data, "push");
|
|
5787
6315
|
return;
|
|
5788
6316
|
}
|
|
5789
6317
|
const msg = { ...data };
|
|
@@ -5855,7 +6383,7 @@ var MessageDeliveryEngine = class {
|
|
|
5855
6383
|
await client._publishEncryptedPushMessage("group.message_created", "group.message_undecryptable", "", seq2 ?? 0, msg, true);
|
|
5856
6384
|
return;
|
|
5857
6385
|
}
|
|
5858
|
-
await client._publishAppEvent("group.message_created", msg);
|
|
6386
|
+
await client._publishAppEvent("group.message_created", msg, "push");
|
|
5859
6387
|
}
|
|
5860
6388
|
} catch (exc) {
|
|
5861
6389
|
client._clientLog.warn(`group push processing failed:${String(exc)}`);
|
|
@@ -5939,11 +6467,16 @@ var MessageDeliveryEngine = class {
|
|
|
5939
6467
|
P2P_GAP_FILL_RETRY_MAX_MS
|
|
5940
6468
|
);
|
|
5941
6469
|
client._gapFillDone.add(retryKey);
|
|
6470
|
+
const releasePullWork = client._rpcPipeline?.reservePullWork?.() ?? (() => {
|
|
6471
|
+
});
|
|
5942
6472
|
client._clientLog.debug(`P2P message gap-fill retry scheduled: ns=${ns} attempt=${retryAttempt} delay_ms=${delayMs}`);
|
|
5943
6473
|
globalThis.setTimeout(() => {
|
|
5944
6474
|
client._gapFillDone.delete(retryKey);
|
|
5945
|
-
if (client.state !== "ready" /* READY */ || client._closing)
|
|
5946
|
-
|
|
6475
|
+
if (client.state !== "ready" /* READY */ || client._closing) {
|
|
6476
|
+
releasePullWork();
|
|
6477
|
+
return;
|
|
6478
|
+
}
|
|
6479
|
+
client._safeAsync(this.fillP2pGap(retryAttempt).finally(releasePullWork));
|
|
5947
6480
|
}, delayMs);
|
|
5948
6481
|
}
|
|
5949
6482
|
async fillP2pGap(retryAttempt = 0) {
|
|
@@ -6234,9 +6767,30 @@ var MessageDeliveryEngine = class {
|
|
|
6234
6767
|
const groupId = String(data.group_id ?? "").trim();
|
|
6235
6768
|
if (!groupId) return;
|
|
6236
6769
|
const ns = `group:${groupId}`;
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
|
|
6770
|
+
const targets = this.onlineUnreadHintTargets ?? /* @__PURE__ */ new Map();
|
|
6771
|
+
const owners = this.onlineUnreadHintOwners ?? /* @__PURE__ */ new Set();
|
|
6772
|
+
this.onlineUnreadHintTargets = targets;
|
|
6773
|
+
this.onlineUnreadHintOwners = owners;
|
|
6774
|
+
targets.set(ns, Math.max(targets.get(ns) ?? 0, positiveSafeSequenceHint(data.seq)));
|
|
6775
|
+
if (owners.has(ns)) return;
|
|
6776
|
+
owners.add(ns);
|
|
6777
|
+
client._safeAsync((async () => {
|
|
6778
|
+
try {
|
|
6779
|
+
while (true) {
|
|
6780
|
+
const throughSeq = targets.get(ns) ?? 0;
|
|
6781
|
+
const ackBefore = this.syncState(ns).ack;
|
|
6782
|
+
await this.runGroupForwardRecovery(groupId, ns, 50, true, throughSeq);
|
|
6783
|
+
const ackAfter = this.syncState(ns).ack;
|
|
6784
|
+
const target = targets.get(ns) ?? 0;
|
|
6785
|
+
if (ackAfter >= target || ackAfter <= ackBefore) return;
|
|
6786
|
+
}
|
|
6787
|
+
} catch (exc) {
|
|
6788
|
+
client._clientLog?.debug(`online unread hint background Forward failed: ns=${ns} err=${formatDeliveryError(exc)}`);
|
|
6789
|
+
} finally {
|
|
6790
|
+
if (this.onlineUnreadHintTargets === targets) targets.delete(ns);
|
|
6791
|
+
if (this.onlineUnreadHintOwners === owners) owners.delete(ns);
|
|
6792
|
+
}
|
|
6793
|
+
})());
|
|
6240
6794
|
}
|
|
6241
6795
|
enqueueOnlineUnreadEventHint(data) {
|
|
6242
6796
|
const client = this.runtime.client;
|
|
@@ -6266,17 +6820,19 @@ var MessageDeliveryEngine = class {
|
|
|
6266
6820
|
const key = pipeline.pullGateKeyForCall("message.pull", request);
|
|
6267
6821
|
return await pipeline.runPullSerialized(key, invoke, background);
|
|
6268
6822
|
}
|
|
6269
|
-
async runGroupForwardRecovery(groupId, ns, pageLimit, background = false) {
|
|
6823
|
+
async runGroupForwardRecovery(groupId, ns, pageLimit, background = false, throughSeq = 0) {
|
|
6270
6824
|
const client = this.runtime.client;
|
|
6825
|
+
const state = this.syncState(ns);
|
|
6826
|
+
const maxPages = this.forwardMaxPages(state.ack, throughSeq, pageLimit);
|
|
6271
6827
|
const request = {
|
|
6272
6828
|
group_id: groupId,
|
|
6273
|
-
after_seq:
|
|
6829
|
+
after_seq: state.ack,
|
|
6274
6830
|
limit: pageLimit,
|
|
6275
|
-
max_pages:
|
|
6831
|
+
max_pages: maxPages
|
|
6276
6832
|
};
|
|
6277
6833
|
const invoke = async () => {
|
|
6278
6834
|
const after = this.syncState(ns).ack;
|
|
6279
|
-
const messages = await client._pullGroupV2(groupId, after, pageLimit, { gateLocked: true, maxPages
|
|
6835
|
+
const messages = await client._pullGroupV2(groupId, after, pageLimit, { gateLocked: true, maxPages });
|
|
6280
6836
|
return { messages, raw_count: messages.length };
|
|
6281
6837
|
};
|
|
6282
6838
|
const pipeline = client._rpcPipeline;
|
|
@@ -6290,6 +6846,10 @@ var MessageDeliveryEngine = class {
|
|
|
6290
6846
|
const ack = Number(tracker.getContiguousSeq(ns) || 0);
|
|
6291
6847
|
return { ack, tail: ack, head: Math.max(ack, Number(tracker.getMaxSeenSeq?.(ns) || 0)) };
|
|
6292
6848
|
}
|
|
6849
|
+
forwardMaxPages(ack, throughSeq, pageLimit) {
|
|
6850
|
+
const limit = Number.isSafeInteger(pageLimit) && pageLimit > 0 ? pageLimit : 1;
|
|
6851
|
+
return Math.max(1, Math.ceil(Math.max(0, throughSeq - ack) / limit));
|
|
6852
|
+
}
|
|
6293
6853
|
inlineMessage(data) {
|
|
6294
6854
|
if (!isJsonObject(data)) return null;
|
|
6295
6855
|
const inline = data.inline_message;
|
|
@@ -6469,7 +7029,7 @@ var MessageDeliveryEngine = class {
|
|
|
6469
7029
|
const tracker = client._seqTracker;
|
|
6470
7030
|
const snapshot = typeof tracker.snapshotNamespace === "function" ? tracker.snapshotNamespace(ns) : null;
|
|
6471
7031
|
try {
|
|
6472
|
-
const published = await this.publishPulledMessage(event, ns, seq2, payload);
|
|
7032
|
+
const published = await this.publishPulledMessage(event, ns, seq2, payload, true, "inline_push");
|
|
6473
7033
|
if (!this.isInlineGenerationCurrent(generation)) return false;
|
|
6474
7034
|
if (!published) return false;
|
|
6475
7035
|
const needsPull = Boolean(tracker.onMessageSeq(ns, seq2));
|
|
@@ -6621,17 +7181,28 @@ var MessageDeliveryEngine = class {
|
|
|
6621
7181
|
if (forceTail || order[0] === "tail") result = await run(true, false, void 0, false, forceTail);
|
|
6622
7182
|
const state = this.syncState(ns);
|
|
6623
7183
|
const tailMissedDuringPull = tailCompleted && Number(client._seqTracker.getMaxSeenSeq(ns) || 0) > state.head;
|
|
6624
|
-
const
|
|
7184
|
+
const maxSeen = Number(client._seqTracker.getMaxSeenSeq(ns) || 0);
|
|
7185
|
+
const pendingThroughSeq = tailMissedDuringPull ? maxSeen : 0;
|
|
6625
7186
|
const tailForward = tailCompleted && (state.ack < state.tail - 1 || tailMissedDuringPull);
|
|
6626
7187
|
const backgroundWindowForward = order[0] === "forward" && !headForward;
|
|
6627
7188
|
if (headForward || tailForward || client._sessionOptions?.background_sync !== false && backgroundWindowForward) {
|
|
7189
|
+
const forwardTarget = Math.max(
|
|
7190
|
+
headForward ? Math.max(pushSeq, maxSeen) : 0,
|
|
7191
|
+
tailForward || backgroundWindowForward ? state.tail - 1 : 0,
|
|
7192
|
+
tailMissedDuringPull ? maxSeen : 0
|
|
7193
|
+
);
|
|
7194
|
+
const forwardMaxPages = this.forwardMaxPages(state.ack, forwardTarget, pageLimit);
|
|
6628
7195
|
result = await run(
|
|
6629
7196
|
false,
|
|
6630
7197
|
!(headForward || tailForward),
|
|
6631
|
-
|
|
7198
|
+
forwardMaxPages,
|
|
6632
7199
|
headForward || tailMissedDuringPull
|
|
6633
7200
|
);
|
|
6634
|
-
if (pendingThroughSeq > 0)
|
|
7201
|
+
if (pendingThroughSeq > 0) {
|
|
7202
|
+
const committedAck = this.syncState(ns).ack;
|
|
7203
|
+
if (committedAck >= pendingThroughSeq) this.consumePendingPull(ns, pendingThroughSeq);
|
|
7204
|
+
else if (committedAck <= state.ack) this.markPendingPullNoProgress(ns, committedAck);
|
|
7205
|
+
}
|
|
6635
7206
|
}
|
|
6636
7207
|
const finalState = this.syncState(ns);
|
|
6637
7208
|
if (tailCompleted && finalState.ack < finalState.tail - 1) {
|
|
@@ -6678,17 +7249,28 @@ var MessageDeliveryEngine = class {
|
|
|
6678
7249
|
if (forceTail || order[0] === "tail") result = await run(true, false, void 0, false, forceTail);
|
|
6679
7250
|
const state = this.syncState(ns);
|
|
6680
7251
|
const tailMissedDuringPull = tailCompleted && Number(client._seqTracker.getMaxSeenSeq(ns) || 0) > state.head;
|
|
6681
|
-
const
|
|
7252
|
+
const maxSeen = Number(client._seqTracker.getMaxSeenSeq(ns) || 0);
|
|
7253
|
+
const pendingThroughSeq = tailMissedDuringPull ? maxSeen : 0;
|
|
6682
7254
|
const tailForward = tailCompleted && (state.ack < state.tail - 1 || tailMissedDuringPull);
|
|
6683
7255
|
const backgroundWindowForward = order[0] === "forward" && !headForward;
|
|
6684
7256
|
if (headForward || tailForward || client._sessionOptions?.background_sync !== false && backgroundWindowForward) {
|
|
7257
|
+
const forwardTarget = Math.max(
|
|
7258
|
+
headForward ? Math.max(pushSeq, maxSeen) : 0,
|
|
7259
|
+
tailForward || backgroundWindowForward ? state.tail - 1 : 0,
|
|
7260
|
+
tailMissedDuringPull ? maxSeen : 0
|
|
7261
|
+
);
|
|
7262
|
+
const forwardMaxPages = this.forwardMaxPages(state.ack, forwardTarget, pageLimit);
|
|
6685
7263
|
result = await run(
|
|
6686
7264
|
false,
|
|
6687
7265
|
!(headForward || tailForward),
|
|
6688
|
-
|
|
7266
|
+
forwardMaxPages,
|
|
6689
7267
|
headForward || tailMissedDuringPull
|
|
6690
7268
|
);
|
|
6691
|
-
if (pendingThroughSeq > 0)
|
|
7269
|
+
if (pendingThroughSeq > 0) {
|
|
7270
|
+
const committedAck = this.syncState(ns).ack;
|
|
7271
|
+
if (committedAck >= pendingThroughSeq) this.consumePendingPull(ns, pendingThroughSeq);
|
|
7272
|
+
else if (committedAck <= state.ack) this.markPendingPullNoProgress(ns, committedAck);
|
|
7273
|
+
}
|
|
6692
7274
|
}
|
|
6693
7275
|
const finalState = this.syncState(ns);
|
|
6694
7276
|
if (tailCompleted && finalState.ack < finalState.tail - 1) {
|
|
@@ -6935,13 +7517,12 @@ var MessageDeliveryEngine = class {
|
|
|
6935
7517
|
}
|
|
6936
7518
|
}
|
|
6937
7519
|
} catch (exc) {
|
|
6938
|
-
client._dispatcher.
|
|
7520
|
+
client._dispatcher.enqueue("seq_tracker.persist_error", {
|
|
6939
7521
|
phase: "restore",
|
|
6940
7522
|
aid,
|
|
6941
7523
|
device_id: deviceId,
|
|
6942
7524
|
slot_id: slotId,
|
|
6943
7525
|
error: String(exc)
|
|
6944
|
-
}).catch(() => {
|
|
6945
7526
|
});
|
|
6946
7527
|
}
|
|
6947
7528
|
}
|
|
@@ -7132,13 +7713,12 @@ var MessageDeliveryEngine = class {
|
|
|
7132
7713
|
} catch (exc) {
|
|
7133
7714
|
const error = formatDeliveryError(exc);
|
|
7134
7715
|
client._clientLog.warn(`save SeqTracker state failed: ${error}`);
|
|
7135
|
-
client._dispatcher.
|
|
7716
|
+
client._dispatcher.enqueue("seq_tracker.persist_error", {
|
|
7136
7717
|
phase: "save",
|
|
7137
7718
|
aid,
|
|
7138
7719
|
device_id: deviceId,
|
|
7139
7720
|
slot_id: slotId,
|
|
7140
7721
|
error: String(error)
|
|
7141
|
-
}).catch(() => {
|
|
7142
7722
|
});
|
|
7143
7723
|
if (throwOnError) throw exc;
|
|
7144
7724
|
}
|
|
@@ -7243,88 +7823,137 @@ var MessageDeliveryEngine = class {
|
|
|
7243
7823
|
}
|
|
7244
7824
|
return params2;
|
|
7245
7825
|
}
|
|
7246
|
-
async drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true) {
|
|
7826
|
+
async drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true, batch, source = "pull") {
|
|
7247
7827
|
const client = this.runtime.client;
|
|
7828
|
+
this.ensurePullOperationCurrent();
|
|
7248
7829
|
const queue = client._pendingOrderedMsgs.get(ns);
|
|
7249
7830
|
if (!queue || queue.size === 0) return;
|
|
7250
7831
|
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
7251
7832
|
const ready = [...queue.keys()].filter((seq2) => seq2 <= contig && (beforeSeq === void 0 || seq2 < beforeSeq)).sort((a, b) => a - b);
|
|
7252
7833
|
let delivered = false;
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7834
|
+
const drainBatches = /* @__PURE__ */ new Map();
|
|
7835
|
+
try {
|
|
7836
|
+
for (const seq2 of ready) {
|
|
7837
|
+
this.ensurePullOperationCurrent();
|
|
7838
|
+
const item = queue.get(seq2);
|
|
7839
|
+
queue.delete(seq2);
|
|
7840
|
+
if (!item || client._pushedSeqs.get(ns)?.has(seq2)) continue;
|
|
7841
|
+
const itemSource = item.source ?? source;
|
|
7842
|
+
let itemBatch = batch;
|
|
7843
|
+
if ((itemSource === "push" || itemSource === "inline_push") && (!batch || batch.trigger !== itemSource)) {
|
|
7844
|
+
itemBatch = drainBatches.get(itemSource);
|
|
7845
|
+
if (!itemBatch) {
|
|
7846
|
+
itemBatch = this.createDeliveryChangeBatch(itemSource);
|
|
7847
|
+
drainBatches.set(itemSource, itemBatch);
|
|
7848
|
+
}
|
|
7849
|
+
}
|
|
7850
|
+
await this.publishOrderedQueueItem(
|
|
7851
|
+
ns,
|
|
7852
|
+
item.event,
|
|
7853
|
+
seq2,
|
|
7854
|
+
item.payload,
|
|
7855
|
+
pullResponse,
|
|
7856
|
+
itemSource,
|
|
7857
|
+
itemBatch
|
|
7858
|
+
);
|
|
7859
|
+
this.ensurePullOperationCurrent();
|
|
7860
|
+
this.markPublishedSeq(ns, seq2);
|
|
7861
|
+
delivered = true;
|
|
7862
|
+
}
|
|
7863
|
+
} finally {
|
|
7864
|
+
for (const drainBatch of drainBatches.values()) {
|
|
7865
|
+
await this.flushRealtimeDeliveryChanges(drainBatch);
|
|
7866
|
+
}
|
|
7260
7867
|
}
|
|
7261
7868
|
if (queue.size === 0) {
|
|
7262
7869
|
client._pendingOrderedMsgs.delete(ns);
|
|
7263
|
-
if (delivered && persist)
|
|
7870
|
+
if (delivered && persist) {
|
|
7871
|
+
this.ensurePullOperationCurrent();
|
|
7872
|
+
await this.saveSeqTrackerState();
|
|
7873
|
+
}
|
|
7264
7874
|
}
|
|
7265
7875
|
}
|
|
7266
|
-
async publishOrderedMessage(event, ns, seq2, payload) {
|
|
7876
|
+
async publishOrderedMessage(event, ns, seq2, payload, source = "push", operationBatch) {
|
|
7267
7877
|
const client = this.runtime.client;
|
|
7268
|
-
const
|
|
7269
|
-
|
|
7270
|
-
|
|
7878
|
+
const ownsBatch = !operationBatch && (source === "push" || source === "inline_push");
|
|
7879
|
+
const batch = operationBatch ?? (ownsBatch ? this.createDeliveryChangeBatch(source) : void 0);
|
|
7880
|
+
try {
|
|
7881
|
+
const seqNum = Number(seq2);
|
|
7882
|
+
if (!Number.isFinite(seqNum) || !Number.isInteger(seqNum) || seqNum <= 0) {
|
|
7883
|
+
await this.publishOrderedQueueItem(ns, event, seqNum, payload, false, source, batch);
|
|
7884
|
+
return true;
|
|
7885
|
+
}
|
|
7886
|
+
if (client._pushedSeqs.get(ns)?.has(seqNum)) {
|
|
7887
|
+
const queue2 = client._pendingOrderedMsgs.get(ns);
|
|
7888
|
+
queue2?.delete(seqNum);
|
|
7889
|
+
if (queue2 && queue2.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7890
|
+
return false;
|
|
7891
|
+
}
|
|
7892
|
+
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
7893
|
+
if (seqNum > contig) {
|
|
7894
|
+
this.enqueueOrderedMessage(ns, event, seqNum, payload, source);
|
|
7895
|
+
return false;
|
|
7896
|
+
}
|
|
7897
|
+
await this.drainOrderedMessages(ns, seqNum, true, true, batch, source);
|
|
7898
|
+
if (client._pushedSeqs.get(ns)?.has(seqNum)) return false;
|
|
7899
|
+
const queue = client._pendingOrderedMsgs.get(ns);
|
|
7900
|
+
queue?.delete(seqNum);
|
|
7901
|
+
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7902
|
+
await this.publishOrderedQueueItem(ns, event, seqNum, payload, false, source, batch);
|
|
7903
|
+
this.markPublishedSeq(ns, seqNum);
|
|
7904
|
+
await this.drainOrderedMessages(ns, void 0, false, true, batch, source);
|
|
7905
|
+
if (!client._pendingOrderedMsgs.get(ns)) await this.saveSeqTrackerState();
|
|
7271
7906
|
return true;
|
|
7907
|
+
} finally {
|
|
7908
|
+
if (ownsBatch && batch) await this.flushRealtimeDeliveryChanges(batch);
|
|
7272
7909
|
}
|
|
7273
|
-
if (client._pushedSeqs.get(ns)?.has(seqNum)) {
|
|
7274
|
-
const queue2 = client._pendingOrderedMsgs.get(ns);
|
|
7275
|
-
queue2?.delete(seqNum);
|
|
7276
|
-
if (queue2 && queue2.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7277
|
-
return false;
|
|
7278
|
-
}
|
|
7279
|
-
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
7280
|
-
if (seqNum > contig) {
|
|
7281
|
-
this.enqueueOrderedMessage(ns, event, seqNum, payload);
|
|
7282
|
-
return false;
|
|
7283
|
-
}
|
|
7284
|
-
await this.drainOrderedMessages(ns, seqNum, true);
|
|
7285
|
-
if (client._pushedSeqs.get(ns)?.has(seqNum)) return false;
|
|
7286
|
-
const queue = client._pendingOrderedMsgs.get(ns);
|
|
7287
|
-
queue?.delete(seqNum);
|
|
7288
|
-
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7289
|
-
await this.publishOrderedQueueItem(ns, event, seqNum, payload);
|
|
7290
|
-
this.markPublishedSeq(ns, seqNum);
|
|
7291
|
-
await this.drainOrderedMessages(ns);
|
|
7292
|
-
if (!client._pendingOrderedMsgs.get(ns)) await this.saveSeqTrackerState();
|
|
7293
|
-
return true;
|
|
7294
7910
|
}
|
|
7295
|
-
async publishPulledMessage(event, ns, seq2, payload, persist = true) {
|
|
7911
|
+
async publishPulledMessage(event, ns, seq2, payload, persist = true, source = "pull", operationBatch) {
|
|
7296
7912
|
const client = this.runtime.client;
|
|
7297
|
-
const
|
|
7298
|
-
|
|
7913
|
+
const ownsBatch = !operationBatch && (source === "push" || source === "inline_push");
|
|
7914
|
+
const batch = operationBatch ?? (ownsBatch ? this.createDeliveryChangeBatch(source) : void 0);
|
|
7915
|
+
try {
|
|
7916
|
+
this.ensurePullOperationCurrent();
|
|
7917
|
+
const seqNum = Number(seq2);
|
|
7918
|
+
if (!Number.isFinite(seqNum) || !Number.isInteger(seqNum) || seqNum <= 0 || !ns) {
|
|
7919
|
+
if (event === "message.recalled") {
|
|
7920
|
+
const published = await client._withPullResponseProcessing(
|
|
7921
|
+
ns,
|
|
7922
|
+
() => this.publishMessageRecallTombstone(seq2, payload)
|
|
7923
|
+
);
|
|
7924
|
+
this.ensurePullOperationCurrent();
|
|
7925
|
+
return published;
|
|
7926
|
+
}
|
|
7927
|
+
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source, ns, batch));
|
|
7928
|
+
this.ensurePullOperationCurrent();
|
|
7929
|
+
return true;
|
|
7930
|
+
}
|
|
7931
|
+
const queue = client._pendingOrderedMsgs.get(ns);
|
|
7932
|
+
if (client._pushedSeqs.get(ns)?.has(seqNum)) {
|
|
7933
|
+
queue?.delete(seqNum);
|
|
7934
|
+
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7935
|
+
return false;
|
|
7936
|
+
}
|
|
7937
|
+
await this.drainOrderedMessages(ns, seqNum, false, persist, batch, source);
|
|
7938
|
+
this.ensurePullOperationCurrent();
|
|
7939
|
+
queue?.delete(seqNum);
|
|
7940
|
+
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7299
7941
|
if (event === "message.recalled") {
|
|
7300
|
-
|
|
7942
|
+
const published = await client._withPullResponseProcessing(
|
|
7301
7943
|
ns,
|
|
7302
|
-
() => this.publishMessageRecallTombstone(
|
|
7944
|
+
() => this.publishMessageRecallTombstone(seqNum, payload)
|
|
7303
7945
|
);
|
|
7946
|
+
this.ensurePullOperationCurrent();
|
|
7947
|
+
this.markPublishedSeq(ns, seqNum);
|
|
7948
|
+
return published;
|
|
7304
7949
|
}
|
|
7305
|
-
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload));
|
|
7306
|
-
|
|
7307
|
-
}
|
|
7308
|
-
const queue = client._pendingOrderedMsgs.get(ns);
|
|
7309
|
-
if (client._pushedSeqs.get(ns)?.has(seqNum)) {
|
|
7310
|
-
queue?.delete(seqNum);
|
|
7311
|
-
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7312
|
-
return false;
|
|
7313
|
-
}
|
|
7314
|
-
await this.drainOrderedMessages(ns, seqNum, false, persist);
|
|
7315
|
-
queue?.delete(seqNum);
|
|
7316
|
-
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
7317
|
-
if (event === "message.recalled") {
|
|
7318
|
-
const published = await client._withPullResponseProcessing(
|
|
7319
|
-
ns,
|
|
7320
|
-
() => this.publishMessageRecallTombstone(seqNum, payload)
|
|
7321
|
-
);
|
|
7950
|
+
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload, source, ns, batch));
|
|
7951
|
+
this.ensurePullOperationCurrent();
|
|
7322
7952
|
this.markPublishedSeq(ns, seqNum);
|
|
7323
|
-
return
|
|
7953
|
+
return true;
|
|
7954
|
+
} finally {
|
|
7955
|
+
if (ownsBatch && batch) await this.flushRealtimeDeliveryChanges(batch);
|
|
7324
7956
|
}
|
|
7325
|
-
await client._withPullResponseProcessing(ns, () => client._publishAppEvent(event, payload));
|
|
7326
|
-
this.markPublishedSeq(ns, seqNum);
|
|
7327
|
-
return true;
|
|
7328
7957
|
}
|
|
7329
7958
|
};
|
|
7330
7959
|
|
|
@@ -7513,12 +8142,7 @@ var LifecycleController = class {
|
|
|
7513
8142
|
async publishConnectionEvent(owner, event, payload) {
|
|
7514
8143
|
const client = this.runtime.client;
|
|
7515
8144
|
this.assertConnectionAttemptOwner(owner);
|
|
7516
|
-
client.
|
|
7517
|
-
try {
|
|
7518
|
-
await client._dispatcher.publish(event, payload);
|
|
7519
|
-
} finally {
|
|
7520
|
-
client._connectEventDispatchDepth -= 1;
|
|
7521
|
-
}
|
|
8145
|
+
client._dispatcher.enqueue(event, payload);
|
|
7522
8146
|
return this.ownsConnectionAttempt(owner);
|
|
7523
8147
|
}
|
|
7524
8148
|
async cancelConnectionAttemptAndWait() {
|
|
@@ -7826,14 +8450,9 @@ var LifecycleController = class {
|
|
|
7826
8450
|
client._clientLog.warn(`reentrant close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
7827
8451
|
});
|
|
7828
8452
|
}
|
|
7829
|
-
|
|
8453
|
+
publishLifecycleStopEvent(payload) {
|
|
7830
8454
|
const client = this.runtime.client;
|
|
7831
|
-
client.
|
|
7832
|
-
try {
|
|
7833
|
-
await client._dispatcher.publish("state_change", payload);
|
|
7834
|
-
} finally {
|
|
7835
|
-
client._lifecycleStopEventDispatchDepth -= 1;
|
|
7836
|
-
}
|
|
8455
|
+
client._dispatcher.enqueue("state_change", payload);
|
|
7837
8456
|
}
|
|
7838
8457
|
async withLifecycleStop(operation, kind) {
|
|
7839
8458
|
const client = this.runtime.client;
|
|
@@ -7884,14 +8503,17 @@ var LifecycleController = class {
|
|
|
7884
8503
|
return;
|
|
7885
8504
|
}
|
|
7886
8505
|
client._delivery.resetInlineAckState();
|
|
8506
|
+
const pullInvalidation = client._rpcPipeline?.invalidatePulls?.();
|
|
8507
|
+
client._rpcPipeline?.stopPullGateWatchdogs?.();
|
|
8508
|
+
await client._transport.close();
|
|
8509
|
+
await pullInvalidation;
|
|
7887
8510
|
await client._cancelReconnectAndWait();
|
|
7888
8511
|
await this.cancelConnectionAttemptAndWait();
|
|
7889
8512
|
await client._saveSeqTrackerState();
|
|
7890
8513
|
client._stopBackgroundTasks();
|
|
7891
|
-
await client._transport.close();
|
|
7892
8514
|
if (client._closing) return;
|
|
7893
8515
|
this.runtime.lifecycle.resetForDisconnect("standby");
|
|
7894
|
-
|
|
8516
|
+
this.publishLifecycleStopEvent({ state: client._publicState(client._state) });
|
|
7895
8517
|
client._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms`);
|
|
7896
8518
|
}, "disconnect");
|
|
7897
8519
|
} finally {
|
|
@@ -7901,6 +8523,7 @@ var LifecycleController = class {
|
|
|
7901
8523
|
async close() {
|
|
7902
8524
|
const client = this.runtime.client;
|
|
7903
8525
|
const tStart = Date.now();
|
|
8526
|
+
const calledFromHandler = client._dispatcher.isDispatchingHandler();
|
|
7904
8527
|
client._clientLog.debug(`close enter: state=${client._state}`);
|
|
7905
8528
|
this.runtime.lifecycle.setClosing(true);
|
|
7906
8529
|
client._delivery.resetInlineAckState();
|
|
@@ -7909,26 +8532,39 @@ var LifecycleController = class {
|
|
|
7909
8532
|
return;
|
|
7910
8533
|
}
|
|
7911
8534
|
return this.withLifecycleStop(async () => {
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
8535
|
+
try {
|
|
8536
|
+
const pullInvalidation = client._rpcPipeline?.invalidatePulls?.();
|
|
8537
|
+
client._rpcPipeline?.stopPullGateWatchdogs?.();
|
|
8538
|
+
client._stopBackgroundTasks();
|
|
8539
|
+
if (client._state === "idle" || client._state === "closed") {
|
|
8540
|
+
const reconnectCancellation2 = client._cancelReconnectAndWait();
|
|
8541
|
+
const connectionCancellation2 = this.cancelConnectionAttemptAndWait();
|
|
8542
|
+
await Promise.all([reconnectCancellation2, connectionCancellation2]);
|
|
8543
|
+
await pullInvalidation;
|
|
8544
|
+
await client._saveSeqTrackerState();
|
|
8545
|
+
this.runtime.lifecycle.setState("closed");
|
|
8546
|
+
client._resetSeqTrackingState();
|
|
8547
|
+
client._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms reason=already_idle`);
|
|
8548
|
+
return;
|
|
8549
|
+
}
|
|
8550
|
+
const reconnectCancellation = client._cancelReconnectAndWait();
|
|
8551
|
+
const connectionCancellation = this.cancelConnectionAttemptAndWait();
|
|
8552
|
+
try {
|
|
8553
|
+
await client._transport.call("auth.logout", {});
|
|
8554
|
+
} catch (err) {
|
|
8555
|
+
client._clientLog.warn(`auth.logout during close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8556
|
+
}
|
|
8557
|
+
await client._transport.close();
|
|
8558
|
+
await Promise.all([pullInvalidation, reconnectCancellation, connectionCancellation]);
|
|
8559
|
+
await client._saveSeqTrackerState();
|
|
7917
8560
|
this.runtime.lifecycle.setState("closed");
|
|
8561
|
+
this.publishLifecycleStopEvent({ state: client._publicState(client._state) });
|
|
7918
8562
|
client._resetSeqTrackingState();
|
|
7919
|
-
client._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms
|
|
7920
|
-
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
await client._transport.call("auth.logout", {});
|
|
7924
|
-
} catch (err) {
|
|
7925
|
-
client._clientLog.warn(`auth.logout during close failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
8563
|
+
client._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms`);
|
|
8564
|
+
} finally {
|
|
8565
|
+
const closing = client._dispatcher.close();
|
|
8566
|
+
if (!calledFromHandler) await closing;
|
|
7926
8567
|
}
|
|
7927
|
-
await client._transport.close();
|
|
7928
|
-
this.runtime.lifecycle.setState("closed");
|
|
7929
|
-
await this.publishLifecycleStopEvent({ state: client._publicState(client._state) });
|
|
7930
|
-
client._resetSeqTrackingState();
|
|
7931
|
-
client._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms`);
|
|
7932
8568
|
}, "close");
|
|
7933
8569
|
}
|
|
7934
8570
|
};
|
|
@@ -14725,6 +15361,14 @@ var PROTECTED_HEADERS_METHODS = /* @__PURE__ */ new Set([
|
|
|
14725
15361
|
"message.thought.put",
|
|
14726
15362
|
"group.thought.put"
|
|
14727
15363
|
]);
|
|
15364
|
+
function generateMessageId() {
|
|
15365
|
+
if (typeof crypto.randomUUID === "function") return `m-${crypto.randomUUID().replace(/-/g, "")}`;
|
|
15366
|
+
const bytes = new Uint8Array(16);
|
|
15367
|
+
crypto.getRandomValues(bytes);
|
|
15368
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
15369
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
15370
|
+
return `m-${Array.from(bytes, (value) => value.toString(16).padStart(2, "0")).join("")}`;
|
|
15371
|
+
}
|
|
14728
15372
|
var SIGNED_METHODS = /* @__PURE__ */ new Set([
|
|
14729
15373
|
"message.send",
|
|
14730
15374
|
"message.v2.put_peer_pk",
|
|
@@ -14829,6 +15473,7 @@ var SIGNED_METHODS = /* @__PURE__ */ new Set([
|
|
|
14829
15473
|
"group.resume"
|
|
14830
15474
|
]);
|
|
14831
15475
|
var SIGNING_KEY_CACHE_MAX = 32;
|
|
15476
|
+
var IDENTITY_ADMISSION_RETRY_DELAYS_MS = [0, 50, 100, 200];
|
|
14832
15477
|
var signingKeyCache = /* @__PURE__ */ new Map();
|
|
14833
15478
|
var certFingerprintCache = /* @__PURE__ */ new Map();
|
|
14834
15479
|
function cacheGet(cache, key) {
|
|
@@ -14883,6 +15528,13 @@ async function signingCertFingerprint(certPem) {
|
|
|
14883
15528
|
}
|
|
14884
15529
|
var PULL_GATE_STALE_MS = 3e4;
|
|
14885
15530
|
var PULL_GATE_OPERATION_TIMEOUT_MS = 3e3;
|
|
15531
|
+
function sameIdentityAdmissionRejection(error, original) {
|
|
15532
|
+
const errorCode2 = Number(error?.code);
|
|
15533
|
+
const originalCode = Number(original?.code);
|
|
15534
|
+
const errorMessage3 = String(error instanceof Error ? error.message : error?.message ?? "").trim().toLowerCase();
|
|
15535
|
+
const originalMessage = String(original instanceof Error ? original.message : original?.message ?? "").trim().toLowerCase();
|
|
15536
|
+
return errorCode2 === -32003 && originalCode === -32003 && errorMessage3 === originalMessage;
|
|
15537
|
+
}
|
|
14886
15538
|
var NON_IDEMPOTENT_TIMEOUT = 35;
|
|
14887
15539
|
var NON_IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
|
|
14888
15540
|
"message.send",
|
|
@@ -14979,6 +15631,9 @@ var RpcPipeline = class {
|
|
|
14979
15631
|
__publicField(this, "runtime");
|
|
14980
15632
|
__publicField(this, "pullGateStates", /* @__PURE__ */ new Map());
|
|
14981
15633
|
__publicField(this, "inlineRealtimeScopes", /* @__PURE__ */ new Set());
|
|
15634
|
+
__publicField(this, "pullWorkTokens", /* @__PURE__ */ new Set());
|
|
15635
|
+
__publicField(this, "pullGeneration", 0);
|
|
15636
|
+
__publicField(this, "pullInvalidationWait", null);
|
|
14982
15637
|
this.runtime = runtime;
|
|
14983
15638
|
}
|
|
14984
15639
|
async call(method, params2) {
|
|
@@ -15184,6 +15839,9 @@ var RpcPipeline = class {
|
|
|
15184
15839
|
delete p._rpc_background;
|
|
15185
15840
|
if (method === "message.send" || method === "group.send") {
|
|
15186
15841
|
this.normalizeOutboundMessagePayload(p, method);
|
|
15842
|
+
if (!Object.prototype.hasOwnProperty.call(p, "message_id") || p.message_id == null) {
|
|
15843
|
+
p.message_id = generateMessageId();
|
|
15844
|
+
}
|
|
15187
15845
|
}
|
|
15188
15846
|
this.normalizeGroupCallIdentifier(method, p);
|
|
15189
15847
|
this.validateOutboundCall(method, p);
|
|
@@ -15348,19 +16006,21 @@ var RpcPipeline = class {
|
|
|
15348
16006
|
if (method === "message.pull" || method === "message.v2.pull" || method === "message.history") {
|
|
15349
16007
|
if (!client._aid) return "";
|
|
15350
16008
|
const mode = method === "message.history" ? "history" : params2.window_mode === "tail" ? "tail" : "forward";
|
|
15351
|
-
const cursor = mode === "history" ? `before=${String(params2.before_seq)}` : `after=${String(params2.after_seq ?? 0)}
|
|
15352
|
-
return `p2p:${client._aid}
|
|
16009
|
+
const cursor = mode === "history" ? `before=${String(params2.before_seq)}` : `after=${String(params2.after_seq ?? 0)}`;
|
|
16010
|
+
return `p2p:${client._aid}|mode=${mode}|cursor=${cursor}|force=${String(Boolean(params2.force))}|limit=${String(params2.limit)}`;
|
|
15353
16011
|
}
|
|
15354
16012
|
if (method === "group.pull" || method === "group.v2.pull" || method === "group.history") {
|
|
15355
16013
|
const gid = String(params2.group_id ?? params2.group_aid ?? "").trim();
|
|
15356
16014
|
if (!gid) return "";
|
|
15357
16015
|
const mode = method === "group.history" ? "history" : params2.window_mode === "tail" ? "tail" : "forward";
|
|
15358
|
-
const cursor = mode === "history" ? `before=${String(params2.before_seq)}` : `after=${String(params2.after_seq ?? params2.after_message_seq ?? 0)}
|
|
15359
|
-
|
|
16016
|
+
const cursor = mode === "history" ? `before=${String(params2.before_seq)}` : `after=${String(params2.after_seq ?? params2.after_message_seq ?? 0)}`;
|
|
16017
|
+
const explicitCursor = this.explicitGroupCursorParams(params2);
|
|
16018
|
+
const cursorSuffix = Object.keys(explicitCursor).length > 0 ? `|cursor_params=${stableStringify(explicitCursor)}` : "";
|
|
16019
|
+
return `group:${gid}|mode=${mode}|cursor=${cursor}${cursorSuffix}|force=${String(Boolean(params2.force))}|limit=${String(params2.limit)}`;
|
|
15360
16020
|
}
|
|
15361
16021
|
if (method === "group.pull_events") {
|
|
15362
16022
|
const gid = String(params2.group_id ?? params2.group_aid ?? "").trim();
|
|
15363
|
-
return gid ? `group_event:${gid}|forward|after=${String(params2.after_event_seq ?? 0)}|limit=${String(params2.limit)}` : "";
|
|
16023
|
+
return gid ? `group_event:${gid}|mode=forward|cursor=after=${String(params2.after_event_seq ?? 0)}|force=${String(Boolean(params2.force))}|limit=${String(params2.limit)}` : "";
|
|
15364
16024
|
}
|
|
15365
16025
|
return "";
|
|
15366
16026
|
}
|
|
@@ -15380,8 +16040,8 @@ var RpcPipeline = class {
|
|
|
15380
16040
|
if (!state) {
|
|
15381
16041
|
state = { active: null, foreground: [], background: [], byKey: /* @__PURE__ */ new Map(), lifecycle: /* @__PURE__ */ new Map(), watchdog: null };
|
|
15382
16042
|
this.pullGateStates.set(name, state);
|
|
15383
|
-
this.startPullGateWatchdog(state, name);
|
|
15384
16043
|
}
|
|
16044
|
+
this.startPullGateWatchdog(state, name);
|
|
15385
16045
|
return state;
|
|
15386
16046
|
}
|
|
15387
16047
|
pullGateOperationTimeoutMs() {
|
|
@@ -15398,6 +16058,13 @@ var RpcPipeline = class {
|
|
|
15398
16058
|
state.watchdog = timer;
|
|
15399
16059
|
timer.unref?.();
|
|
15400
16060
|
}
|
|
16061
|
+
stopPullGateWatchdogs() {
|
|
16062
|
+
for (const state of this.pullGateStates.values()) {
|
|
16063
|
+
if (state.watchdog === null) continue;
|
|
16064
|
+
clearInterval(state.watchdog);
|
|
16065
|
+
state.watchdog = null;
|
|
16066
|
+
}
|
|
16067
|
+
}
|
|
15401
16068
|
checkPullGate(state, name) {
|
|
15402
16069
|
const active = state.active;
|
|
15403
16070
|
if (!active) {
|
|
@@ -15407,17 +16074,12 @@ var RpcPipeline = class {
|
|
|
15407
16074
|
if (!active.pullingStartedAt || Date.now() - active.pullingStartedAt < this.pullGateOperationTimeoutMs()) return;
|
|
15408
16075
|
if (active.timedOut) return;
|
|
15409
16076
|
active.timedOut = true;
|
|
16077
|
+
active.invalidated = true;
|
|
16078
|
+
this.pullGeneration += 1;
|
|
15410
16079
|
active.cancel?.();
|
|
15411
16080
|
const err = new TimeoutError(`pull gate timeout: ${active.namespace}`, { retryable: true });
|
|
15412
16081
|
this.runtime.client._clientLog?.warn(`pull gate watchdog timeout: gate=${name} key=${active.key}`);
|
|
15413
|
-
state.lifecycle.set(active.namespace, "idle");
|
|
15414
|
-
if (state.active === active) state.active = null;
|
|
15415
|
-
if (state.byKey.get(active.key) === active) state.byKey.delete(active.key);
|
|
15416
|
-
active.resolve = () => {
|
|
15417
|
-
};
|
|
15418
16082
|
active.reject(err);
|
|
15419
|
-
this.drainPullGate(state);
|
|
15420
|
-
this.runtime.client._delivery?.onPullGateIdle?.(active.namespace);
|
|
15421
16083
|
}
|
|
15422
16084
|
realtimePullNamespace(key) {
|
|
15423
16085
|
for (const marker of ["|tail|", "|forward|"]) {
|
|
@@ -15436,7 +16098,7 @@ var RpcPipeline = class {
|
|
|
15436
16098
|
if (typeof cancel !== "function" || !key) return request;
|
|
15437
16099
|
const state = this.pullGateStates.get(this.pullGateName(key));
|
|
15438
16100
|
const active = state?.active;
|
|
15439
|
-
if (!active || active.
|
|
16101
|
+
if (!active || active.key !== key || active.timedOut || active.invalidated) return request;
|
|
15440
16102
|
const boundCancel = () => cancel.call(request);
|
|
15441
16103
|
active.cancel = boundCancel;
|
|
15442
16104
|
return request.finally(() => {
|
|
@@ -15484,6 +16146,28 @@ var RpcPipeline = class {
|
|
|
15484
16146
|
const matches = (job) => job !== null && job.namespace === ns;
|
|
15485
16147
|
return matches(state.active) || state.foreground.some(matches) || state.background.some(matches);
|
|
15486
16148
|
}
|
|
16149
|
+
hasAnyPullActivity() {
|
|
16150
|
+
if (this.pullWorkTokens.size > 0) return true;
|
|
16151
|
+
for (const state of this.pullGateStates.values()) {
|
|
16152
|
+
if (state.active || state.foreground.length > 0 || state.background.length > 0) return true;
|
|
16153
|
+
}
|
|
16154
|
+
return false;
|
|
16155
|
+
}
|
|
16156
|
+
reservePullWork() {
|
|
16157
|
+
const token = Symbol("pull-work");
|
|
16158
|
+
this.pullWorkTokens.add(token);
|
|
16159
|
+
let released = false;
|
|
16160
|
+
return () => {
|
|
16161
|
+
if (released) return;
|
|
16162
|
+
released = true;
|
|
16163
|
+
this.pullWorkTokens.delete(token);
|
|
16164
|
+
this.runtime.client._delivery?.onPullWorkSettled?.();
|
|
16165
|
+
};
|
|
16166
|
+
}
|
|
16167
|
+
releasePullWorkToken(token) {
|
|
16168
|
+
if (!this.pullWorkTokens.delete(token)) return;
|
|
16169
|
+
this.runtime.client._delivery?.onPullWorkSettled?.();
|
|
16170
|
+
}
|
|
15487
16171
|
async tryRunInlineRealtime(ns, seq2, operation) {
|
|
15488
16172
|
const normalized = String(ns ?? "").trim();
|
|
15489
16173
|
if (!normalized.startsWith("p2p:") && !normalized.startsWith("group:") || !Number.isSafeInteger(seq2) || seq2 <= 0 || typeof operation !== "function") {
|
|
@@ -15522,7 +16206,11 @@ var RpcPipeline = class {
|
|
|
15522
16206
|
resumeResolve: null,
|
|
15523
16207
|
pullingStartedAt: 0,
|
|
15524
16208
|
timedOut: false,
|
|
15525
|
-
|
|
16209
|
+
invalidated: false,
|
|
16210
|
+
cancel: null,
|
|
16211
|
+
settled: Promise.resolve(),
|
|
16212
|
+
settledResolve: null,
|
|
16213
|
+
workToken: Symbol("inline-pull-work")
|
|
15526
16214
|
};
|
|
15527
16215
|
this.inlineRealtimeScopes.add(normalized);
|
|
15528
16216
|
try {
|
|
@@ -15562,14 +16250,68 @@ var RpcPipeline = class {
|
|
|
15562
16250
|
gate.inflight = false;
|
|
15563
16251
|
gate.startedAt = 0;
|
|
15564
16252
|
}
|
|
16253
|
+
isPullGenerationCurrent(generation) {
|
|
16254
|
+
return generation === this.pullGeneration;
|
|
16255
|
+
}
|
|
16256
|
+
invalidatePulls() {
|
|
16257
|
+
if (this.pullInvalidationWait) return this.pullInvalidationWait;
|
|
16258
|
+
this.pullGeneration += 1;
|
|
16259
|
+
const error = new Error("pull invalidated");
|
|
16260
|
+
const waits = [];
|
|
16261
|
+
for (const state of this.pullGateStates.values()) {
|
|
16262
|
+
const jobs = /* @__PURE__ */ new Set();
|
|
16263
|
+
if (state.active) jobs.add(state.active);
|
|
16264
|
+
for (const job of state.foreground) jobs.add(job);
|
|
16265
|
+
for (const job of state.background) jobs.add(job);
|
|
16266
|
+
for (const job of jobs) {
|
|
16267
|
+
job.invalidated = true;
|
|
16268
|
+
if (!job.timedOut) job.reject(error);
|
|
16269
|
+
job.cancel?.();
|
|
16270
|
+
job.resumeResolve?.();
|
|
16271
|
+
job.resumeResolve = null;
|
|
16272
|
+
if (job.running) {
|
|
16273
|
+
waits.push(job.settled);
|
|
16274
|
+
} else {
|
|
16275
|
+
this.releasePullWorkToken(job.workToken);
|
|
16276
|
+
job.settledResolve?.();
|
|
16277
|
+
job.settledResolve = null;
|
|
16278
|
+
}
|
|
16279
|
+
}
|
|
16280
|
+
state.foreground = state.foreground.filter((job) => !jobs.has(job));
|
|
16281
|
+
state.background = state.background.filter((job) => !jobs.has(job));
|
|
16282
|
+
for (const [key, job] of state.byKey.entries()) {
|
|
16283
|
+
if (jobs.has(job) && !job.running) state.byKey.delete(key);
|
|
16284
|
+
}
|
|
16285
|
+
this.drainPullGate(state);
|
|
16286
|
+
}
|
|
16287
|
+
const wait = Promise.all(waits).then(() => void 0);
|
|
16288
|
+
this.runtime.client._delivery?.onPullWorkSettled?.();
|
|
16289
|
+
let tracked;
|
|
16290
|
+
tracked = wait.finally(() => {
|
|
16291
|
+
if (this.pullInvalidationWait === tracked) this.pullInvalidationWait = null;
|
|
16292
|
+
});
|
|
16293
|
+
this.pullInvalidationWait = tracked;
|
|
16294
|
+
return tracked;
|
|
16295
|
+
}
|
|
16296
|
+
pullInvalidationInProgress() {
|
|
16297
|
+
return this.pullInvalidationWait !== null;
|
|
16298
|
+
}
|
|
15565
16299
|
async runPullSerialized(key, operation, background = false) {
|
|
15566
|
-
if (
|
|
16300
|
+
if (this.pullInvalidationInProgress()) {
|
|
16301
|
+
throw new Error("pull invalidated");
|
|
16302
|
+
}
|
|
16303
|
+
if (!key) {
|
|
16304
|
+
const releasePullWork = this.reservePullWork();
|
|
16305
|
+
try {
|
|
16306
|
+
return await this.executePullOperation(operation, background);
|
|
16307
|
+
} finally {
|
|
16308
|
+
releasePullWork();
|
|
16309
|
+
}
|
|
16310
|
+
}
|
|
15567
16311
|
const state = this.pullGateState(key);
|
|
15568
16312
|
const namespace = this.pullScopeKey(key);
|
|
15569
|
-
const
|
|
15570
|
-
const
|
|
15571
|
-
const queuedBackground = state.background.find((job2) => job2.namespace === namespace);
|
|
15572
|
-
const existing = background ? active ?? queuedForeground ?? queuedBackground : queuedForeground ?? (active && !active.background ? active : null) ?? queuedBackground;
|
|
16313
|
+
const candidate = state.byKey.get(key);
|
|
16314
|
+
const existing = candidate && !candidate.timedOut && !candidate.invalidated ? candidate : null;
|
|
15573
16315
|
if (existing) {
|
|
15574
16316
|
if (!background && existing.background && existing !== state.active) {
|
|
15575
16317
|
const index = state.background.indexOf(existing);
|
|
@@ -15579,12 +16321,18 @@ var RpcPipeline = class {
|
|
|
15579
16321
|
}
|
|
15580
16322
|
return await existing.promise;
|
|
15581
16323
|
}
|
|
16324
|
+
const workToken = Symbol("pull-work");
|
|
16325
|
+
this.pullWorkTokens.add(workToken);
|
|
15582
16326
|
let resolve;
|
|
15583
16327
|
let reject;
|
|
16328
|
+
let settledResolve;
|
|
15584
16329
|
const promise = new Promise((res, rej) => {
|
|
15585
16330
|
resolve = res;
|
|
15586
16331
|
reject = rej;
|
|
15587
16332
|
});
|
|
16333
|
+
const settled = new Promise((resolveSettled) => {
|
|
16334
|
+
settledResolve = resolveSettled;
|
|
16335
|
+
});
|
|
15588
16336
|
void promise.catch(() => {
|
|
15589
16337
|
});
|
|
15590
16338
|
const job = {
|
|
@@ -15602,7 +16350,11 @@ var RpcPipeline = class {
|
|
|
15602
16350
|
resumeResolve: null,
|
|
15603
16351
|
pullingStartedAt: 0,
|
|
15604
16352
|
timedOut: false,
|
|
15605
|
-
|
|
16353
|
+
invalidated: false,
|
|
16354
|
+
cancel: null,
|
|
16355
|
+
settled,
|
|
16356
|
+
settledResolve,
|
|
16357
|
+
workToken
|
|
15606
16358
|
};
|
|
15607
16359
|
(background ? state.background : state.foreground).push(job);
|
|
15608
16360
|
state.byKey.set(key, job);
|
|
@@ -15611,14 +16363,30 @@ var RpcPipeline = class {
|
|
|
15611
16363
|
return await promise;
|
|
15612
16364
|
}
|
|
15613
16365
|
async executePullOperation(operation, background) {
|
|
15614
|
-
|
|
15615
|
-
|
|
16366
|
+
const client = this.runtime.client;
|
|
16367
|
+
const hadPrevious = Object.prototype.hasOwnProperty.call(client, "_pullOperationGeneration");
|
|
16368
|
+
const previous = client._pullOperationGeneration;
|
|
16369
|
+
client._pullOperationGeneration = this.pullGeneration;
|
|
16370
|
+
try {
|
|
16371
|
+
if (background) return await client._withBackgroundRpc(operation);
|
|
16372
|
+
return await operation();
|
|
16373
|
+
} finally {
|
|
16374
|
+
if (hadPrevious) client._pullOperationGeneration = previous;
|
|
16375
|
+
else delete client._pullOperationGeneration;
|
|
16376
|
+
}
|
|
16377
|
+
}
|
|
16378
|
+
pullOperationIsCurrent() {
|
|
16379
|
+
const generation = this.runtime.client._pullOperationGeneration;
|
|
16380
|
+
return generation === void 0 || this.isPullGenerationCurrent(generation);
|
|
16381
|
+
}
|
|
16382
|
+
throwIfPullInvalidated() {
|
|
16383
|
+
if (!this.pullOperationIsCurrent()) throw new Error("pull invalidated");
|
|
15616
16384
|
}
|
|
15617
16385
|
async yieldPullGate(key, nextKey, background) {
|
|
15618
16386
|
if (!key) return;
|
|
15619
16387
|
const state = this.pullGateState(key);
|
|
15620
16388
|
const namespace = this.pullScopeKey(key);
|
|
15621
|
-
const job = state.active?.
|
|
16389
|
+
const job = state.active?.key === key ? state.active : null;
|
|
15622
16390
|
if (!job || state.active !== job) return;
|
|
15623
16391
|
const replacementKey = String(nextKey || key);
|
|
15624
16392
|
if (state.byKey.get(job.key) === job) state.byKey.delete(job.key);
|
|
@@ -15634,7 +16402,10 @@ var RpcPipeline = class {
|
|
|
15634
16402
|
state.active = null;
|
|
15635
16403
|
(job.background ? state.background : state.foreground).push(job);
|
|
15636
16404
|
this.drainPullGate(state);
|
|
15637
|
-
if (job.resume)
|
|
16405
|
+
if (job.resume) {
|
|
16406
|
+
await job.resume;
|
|
16407
|
+
if (job.invalidated) throw new Error("pull invalidated");
|
|
16408
|
+
}
|
|
15638
16409
|
}
|
|
15639
16410
|
getPullLifecycle(namespace) {
|
|
15640
16411
|
const ns = this.pullScopeKey(namespace);
|
|
@@ -15670,13 +16441,23 @@ var RpcPipeline = class {
|
|
|
15670
16441
|
return;
|
|
15671
16442
|
}
|
|
15672
16443
|
job.running = true;
|
|
15673
|
-
void this.executePullOperation(job.operation, job.background).then(
|
|
15674
|
-
|
|
16444
|
+
void this.executePullOperation(job.operation, job.background).then(
|
|
16445
|
+
(value) => {
|
|
16446
|
+
if (!job.timedOut && !job.invalidated) job.resolve(value);
|
|
16447
|
+
},
|
|
16448
|
+
(error) => {
|
|
16449
|
+
if (!job.timedOut && !job.invalidated) job.reject(error);
|
|
16450
|
+
}
|
|
16451
|
+
).finally(() => {
|
|
16452
|
+
this.pullWorkTokens.delete(job.workToken);
|
|
15675
16453
|
state.lifecycle.set(job.namespace, "idle");
|
|
15676
16454
|
if (state.active === job) state.active = null;
|
|
15677
16455
|
if (state.byKey.get(job.key) === job) state.byKey.delete(job.key);
|
|
15678
16456
|
this.drainPullGate(state);
|
|
15679
|
-
|
|
16457
|
+
job.settledResolve?.();
|
|
16458
|
+
job.settledResolve = null;
|
|
16459
|
+
if (!job.invalidated) this.runtime.client._delivery?.onPullGateIdle?.(job.namespace);
|
|
16460
|
+
this.runtime.client._delivery?.onPullWorkSettled?.();
|
|
15680
16461
|
});
|
|
15681
16462
|
}
|
|
15682
16463
|
armQueuedPullTimers(_state) {
|
|
@@ -15699,7 +16480,9 @@ var RpcPipeline = class {
|
|
|
15699
16480
|
else request = client._transport.call(method, payload);
|
|
15700
16481
|
return this.bindActivePullCancellation(method, payload, request);
|
|
15701
16482
|
};
|
|
15702
|
-
|
|
16483
|
+
const result = await this.transportCallWithIdentityRecovery(invokeTransport, method, payload);
|
|
16484
|
+
this.throwIfPullInvalidated();
|
|
16485
|
+
return result;
|
|
15703
16486
|
}
|
|
15704
16487
|
async transportCallWithIdentityRecovery(operation, method, params2) {
|
|
15705
16488
|
try {
|
|
@@ -15707,11 +16490,22 @@ var RpcPipeline = class {
|
|
|
15707
16490
|
} catch (err) {
|
|
15708
16491
|
const recover = this.runtime.client._recoverIdentityAdmission;
|
|
15709
16492
|
if (typeof recover !== "function" || !await recover.call(this.runtime.client, err, method, params2)) throw err;
|
|
15710
|
-
|
|
16493
|
+
let lastError = err;
|
|
16494
|
+
for (const delay of IDENTITY_ADMISSION_RETRY_DELAYS_MS) {
|
|
16495
|
+
if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay));
|
|
16496
|
+
try {
|
|
16497
|
+
return await operation();
|
|
16498
|
+
} catch (retryError) {
|
|
16499
|
+
if (!sameIdentityAdmissionRejection(retryError, err)) throw retryError;
|
|
16500
|
+
lastError = retryError;
|
|
16501
|
+
}
|
|
16502
|
+
}
|
|
16503
|
+
throw lastError;
|
|
15711
16504
|
}
|
|
15712
16505
|
}
|
|
15713
16506
|
async postprocessResult(method, params2, result, options = {}) {
|
|
15714
16507
|
const client = this.runtime.client;
|
|
16508
|
+
this.throwIfPullInvalidated();
|
|
15715
16509
|
let next = result;
|
|
15716
16510
|
if ((method === "group.send" || method === "group.v2.send") && isJsonObject(next)) {
|
|
15717
16511
|
next = normalizeGroupMentionMode(next);
|
|
@@ -17412,17 +18206,21 @@ var GroupFacade = class extends RpcFacade {
|
|
|
17412
18206
|
}
|
|
17413
18207
|
create(params2) {
|
|
17414
18208
|
const split = splitAidStore(params2);
|
|
17415
|
-
if (
|
|
18209
|
+
if (split.params.group_name === void 0 && split.params.groupName !== void 0) {
|
|
18210
|
+
split.params.group_name = split.params.groupName;
|
|
18211
|
+
delete split.params.groupName;
|
|
18212
|
+
}
|
|
18213
|
+
if (split.aidStore && typeof this.client.createGroup === "function") {
|
|
17416
18214
|
return this.client.createGroup(split.params, { aidStore: split.aidStore });
|
|
17417
18215
|
}
|
|
17418
18216
|
return this.call("group.create", split.params);
|
|
17419
18217
|
}
|
|
17420
18218
|
bindAid(params2) {
|
|
17421
|
-
return this.
|
|
18219
|
+
return this.call("group.bind_aid", splitAidStore(params2).params);
|
|
17422
18220
|
}
|
|
17423
18221
|
bindGroupAid(params2) {
|
|
17424
18222
|
const split = splitAidStore(params2);
|
|
17425
|
-
if (typeof this.client.bindGroupAid === "function") {
|
|
18223
|
+
if (split.aidStore && typeof this.client.bindGroupAid === "function") {
|
|
17426
18224
|
return this.client.bindGroupAid(split.params, { aidStore: split.aidStore });
|
|
17427
18225
|
}
|
|
17428
18226
|
return this.call("group.bind_group_aid", split.params);
|
|
@@ -22795,6 +23593,10 @@ var V2Session = class {
|
|
|
22795
23593
|
};
|
|
22796
23594
|
|
|
22797
23595
|
// src/client/v2-e2ee.ts
|
|
23596
|
+
function pullGateKeyForClient(client, method, params2, fallback) {
|
|
23597
|
+
const key = client._rpcPipeline?.pullGateKeyForCall?.(method, params2);
|
|
23598
|
+
return typeof key === "string" && key ? key : fallback;
|
|
23599
|
+
}
|
|
22798
23600
|
var V2_BOOTSTRAP_TTL_MS = 60 * 60 * 1e3;
|
|
22799
23601
|
var V2_RETRYABLE_CODES = /* @__PURE__ */ new Set([-33011, -33012, -33050, -33052, -33054]);
|
|
22800
23602
|
var V2_GROUP_STALE_BOOTSTRAP_CODE = -33054;
|
|
@@ -23552,11 +24354,22 @@ var V2E2EECoordinator = class {
|
|
|
23552
24354
|
const fetchKey = this.pendingSenderIKFetchKey(fromAid, senderDeviceId, groupId);
|
|
23553
24355
|
if (!fromAid || client._v2SenderIKFetching.has(fetchKey)) return;
|
|
23554
24356
|
client._v2SenderIKFetching.add(fetchKey);
|
|
23555
|
-
client.
|
|
24357
|
+
const releasePullWork = client._rpcPipeline?.reservePullWork?.() ?? (() => {
|
|
24358
|
+
});
|
|
24359
|
+
const generation = client._delivery.captureInlineGeneration?.();
|
|
24360
|
+
client._safeAsync(this.resolveSenderIKPending(
|
|
24361
|
+
fromAid,
|
|
24362
|
+
senderDeviceId,
|
|
24363
|
+
groupId,
|
|
24364
|
+
fetchKey,
|
|
24365
|
+
generation
|
|
24366
|
+
).finally(releasePullWork));
|
|
23556
24367
|
}
|
|
23557
|
-
async resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey) {
|
|
24368
|
+
async resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey, generation) {
|
|
23558
24369
|
const client = this.client;
|
|
24370
|
+
const generationCurrent = () => generation === void 0 || client._delivery.isInlineGenerationCurrent?.(generation) !== false;
|
|
23559
24371
|
try {
|
|
24372
|
+
if (!generationCurrent()) return;
|
|
23560
24373
|
const session = client._v2Session;
|
|
23561
24374
|
if (session && fromAid) {
|
|
23562
24375
|
try {
|
|
@@ -23587,17 +24400,21 @@ var V2E2EECoordinator = class {
|
|
|
23587
24400
|
await this.getV2SenderPubDer(fromAid, senderDeviceId);
|
|
23588
24401
|
}
|
|
23589
24402
|
}
|
|
24403
|
+
if (!generationCurrent()) return;
|
|
23590
24404
|
const pendingItems = [...client._v2SenderIKPending.entries()].filter(([, entry]) => entry.fromAid === fromAid && entry.senderDeviceId === senderDeviceId && entry.groupId === groupId);
|
|
23591
24405
|
for (const [key, entry] of pendingItems) {
|
|
24406
|
+
if (!generationCurrent()) return;
|
|
23592
24407
|
let plaintext = null;
|
|
23593
24408
|
const retryStatus = {};
|
|
23594
24409
|
try {
|
|
23595
24410
|
plaintext = await client._decryptV2Message(entry.msg, false, true, true, true, retryStatus);
|
|
23596
24411
|
} catch (exc) {
|
|
24412
|
+
if (!generationCurrent()) return;
|
|
23597
24413
|
client._clientLog.warn(`V2 sender IK pending retry raised: key=${key} err=${String(formatE2EEError(exc))}`);
|
|
23598
24414
|
client._v2SenderIKPending.delete(key);
|
|
23599
24415
|
continue;
|
|
23600
24416
|
}
|
|
24417
|
+
if (!generationCurrent()) return;
|
|
23601
24418
|
if (plaintext === null) {
|
|
23602
24419
|
if (retryStatus.deferred) {
|
|
23603
24420
|
client._clientLog.warn(`V2 pending retry still missing key material: key=${key}`);
|
|
@@ -23649,6 +24466,7 @@ var V2E2EECoordinator = class {
|
|
|
23649
24466
|
return client.call("message.send", {
|
|
23650
24467
|
to: toAid,
|
|
23651
24468
|
payload: envelope,
|
|
24469
|
+
message_id: opts?.messageId,
|
|
23652
24470
|
encrypt: false,
|
|
23653
24471
|
_skip_send_result_envelope: true
|
|
23654
24472
|
});
|
|
@@ -23725,7 +24543,8 @@ var V2E2EECoordinator = class {
|
|
|
23725
24543
|
event.event,
|
|
23726
24544
|
client._aid ? `p2p:${client._aid}` : "",
|
|
23727
24545
|
seq2,
|
|
23728
|
-
event.payload
|
|
24546
|
+
event.payload,
|
|
24547
|
+
source
|
|
23729
24548
|
);
|
|
23730
24549
|
} else {
|
|
23731
24550
|
await client._publishAppEvent(event.event, event.payload, source);
|
|
@@ -23784,7 +24603,8 @@ var V2E2EECoordinator = class {
|
|
|
23784
24603
|
"group.message_created",
|
|
23785
24604
|
`group:${groupId}`,
|
|
23786
24605
|
seq2,
|
|
23787
|
-
plaintext
|
|
24606
|
+
plaintext,
|
|
24607
|
+
source
|
|
23788
24608
|
);
|
|
23789
24609
|
} else {
|
|
23790
24610
|
await client._publishAppEvent("group.message_created", plaintext, source);
|
|
@@ -23854,7 +24674,11 @@ var V2E2EECoordinator = class {
|
|
|
23854
24674
|
const ns = client._aid ? `p2p:${client._aid}` : "";
|
|
23855
24675
|
if (opts?.windowMode === "tail") {
|
|
23856
24676
|
if (!opts.gateLocked) {
|
|
23857
|
-
const key =
|
|
24677
|
+
const key = pullGateKeyForClient(client, "message.v2.pull", {
|
|
24678
|
+
window_mode: "tail",
|
|
24679
|
+
after_seq: afterSeq,
|
|
24680
|
+
limit
|
|
24681
|
+
}, `${ns}|tail|after=${afterSeq}|limit=${limit}`);
|
|
23858
24682
|
return await client._runPullSerialized(key, () => this.pullV2(afterSeq, limit, {
|
|
23859
24683
|
...opts ?? {},
|
|
23860
24684
|
gateLocked: true
|
|
@@ -23864,7 +24688,11 @@ var V2E2EECoordinator = class {
|
|
|
23864
24688
|
return Array.isArray(result.messages) ? result.messages : [];
|
|
23865
24689
|
}
|
|
23866
24690
|
if (ns && !opts?.gateLocked) {
|
|
23867
|
-
const key =
|
|
24691
|
+
const key = pullGateKeyForClient(client, "message.v2.pull", {
|
|
24692
|
+
after_seq: afterSeq,
|
|
24693
|
+
force: opts?.force === true,
|
|
24694
|
+
limit
|
|
24695
|
+
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
23868
24696
|
return await client._runPullSerialized(key, () => this.pullV2(afterSeq, limit, {
|
|
23869
24697
|
...opts ?? {},
|
|
23870
24698
|
gateLocked: true
|
|
@@ -23872,7 +24700,11 @@ var V2E2EECoordinator = class {
|
|
|
23872
24700
|
}
|
|
23873
24701
|
const decrypted = [];
|
|
23874
24702
|
let nextAfterSeq = opts?.force ? afterSeq : afterSeq || (ns ? client._seqTracker.getContiguousSeq(ns) : 0);
|
|
23875
|
-
let pullGateKey = ns ?
|
|
24703
|
+
let pullGateKey = ns ? pullGateKeyForClient(client, "message.v2.pull", {
|
|
24704
|
+
after_seq: afterSeq,
|
|
24705
|
+
force: opts?.force === true,
|
|
24706
|
+
limit
|
|
24707
|
+
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`) : "";
|
|
23876
24708
|
const deferredServerCursor = ns ? this.pendingForwardCursor(ns, client._seqTracker.getContiguousSeq(ns)) : 0;
|
|
23877
24709
|
const deferredForwardAck = ns ? this.pendingForwardAck(ns, client._seqTracker.getContiguousSeq(ns)) : 0;
|
|
23878
24710
|
let pageCount = 0;
|
|
@@ -23917,6 +24749,7 @@ var V2E2EECoordinator = class {
|
|
|
23917
24749
|
const pageTrackerSnapshot = ns && typeof client._seqTracker.snapshotNamespace === "function" ? client._seqTracker.snapshotNamespace(ns) : null;
|
|
23918
24750
|
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
23919
24751
|
const deferredKeyFetches = /* @__PURE__ */ new Map();
|
|
24752
|
+
let blockedSeq = 0;
|
|
23920
24753
|
for (const msg of messages) {
|
|
23921
24754
|
const seq2 = Number(msg.seq ?? 0);
|
|
23922
24755
|
if (!Number.isFinite(seq2) || seq2 <= 0) continue;
|
|
@@ -23939,7 +24772,7 @@ var V2E2EECoordinator = class {
|
|
|
23939
24772
|
attachGatewayProximity(v1Msg, msg);
|
|
23940
24773
|
const appEvent = client._delivery.p2pAppEventForMessage(v1Msg);
|
|
23941
24774
|
if (ns) await client._publishPulledMessage(appEvent.event, ns, seq2, appEvent.payload, false);
|
|
23942
|
-
else await client._publishAppEvent(appEvent.event, appEvent.payload);
|
|
24775
|
+
else await client._publishAppEvent(appEvent.event, appEvent.payload, "pull");
|
|
23943
24776
|
decrypted.push(v1Msg);
|
|
23944
24777
|
} else {
|
|
23945
24778
|
client._clientLog.debug(`message.v2.pull skipping V1 envelope seq=${seq2} payload_type=${payloadType || "<none>"} (V1 E2EE removed)`);
|
|
@@ -23956,6 +24789,9 @@ var V2E2EECoordinator = class {
|
|
|
23956
24789
|
}
|
|
23957
24790
|
const deferStatus = {};
|
|
23958
24791
|
const plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true);
|
|
24792
|
+
if (deferStatus.deferred) {
|
|
24793
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
24794
|
+
}
|
|
23959
24795
|
if (deferStatus.deferred && deferStatus.fromAid) {
|
|
23960
24796
|
const key = `${deferStatus.fromAid}\0${deferStatus.senderDeviceId ?? ""}\0${deferStatus.groupId ?? ""}`;
|
|
23961
24797
|
deferredKeyFetches.set(key, {
|
|
@@ -23969,7 +24805,7 @@ var V2E2EECoordinator = class {
|
|
|
23969
24805
|
await client._publishPulledMessage("message.received", ns, seq2, plaintext, false);
|
|
23970
24806
|
decrypted.push(plaintext);
|
|
23971
24807
|
} else {
|
|
23972
|
-
await client._publishAppEvent("message.received", plaintext);
|
|
24808
|
+
await client._publishAppEvent("message.received", plaintext, "pull");
|
|
23973
24809
|
decrypted.push(plaintext);
|
|
23974
24810
|
}
|
|
23975
24811
|
}
|
|
@@ -23978,7 +24814,7 @@ var V2E2EECoordinator = class {
|
|
|
23978
24814
|
const serverAckSeq = parsedServerAckSeq ?? 0;
|
|
23979
24815
|
const retentionFloor = Math.max(0, Number(result.retention_floor_seq ?? 0));
|
|
23980
24816
|
if (ns) {
|
|
23981
|
-
const commitTarget = Math.max(
|
|
24817
|
+
const commitTarget = blockedSeq > 0 ? pageContigBefore : Math.max(
|
|
23982
24818
|
pageContigBefore,
|
|
23983
24819
|
Number.isFinite(retentionFloor) ? retentionFloor : 0,
|
|
23984
24820
|
pageCount === 1 ? deferredServerCursor : 0,
|
|
@@ -24020,7 +24856,7 @@ var V2E2EECoordinator = class {
|
|
|
24020
24856
|
this.recordForwardCursor(ns, serverAckSeq, ackSeq);
|
|
24021
24857
|
}
|
|
24022
24858
|
const knownServerAckSeq = hasServerAckSeq ? serverAckSeq : pageCount === 1 ? deferredServerCursor : 0;
|
|
24023
|
-
const ackNeeded = ackSeq > 0 && ackSeq > lastAutoAckSeq && (hasServerAckSeq && ackSeq > serverAckSeq || contigAdvanced && ackSeq > knownServerAckSeq);
|
|
24859
|
+
const ackNeeded = blockedSeq <= 0 && ackSeq > 0 && ackSeq > lastAutoAckSeq && (hasServerAckSeq && ackSeq > serverAckSeq || contigAdvanced && ackSeq > knownServerAckSeq);
|
|
24024
24860
|
if (ackNeeded) {
|
|
24025
24861
|
this.recordForwardAck(ns, ackSeq);
|
|
24026
24862
|
const nextAfter2 = Math.max(pageMaxSeq, nextAfterSeq);
|
|
@@ -24036,6 +24872,7 @@ var V2E2EECoordinator = class {
|
|
|
24036
24872
|
}
|
|
24037
24873
|
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
24038
24874
|
const rawCount = messages.length;
|
|
24875
|
+
if (blockedSeq > 0) break;
|
|
24039
24876
|
const shouldContinue = shouldContinueForwardPage({
|
|
24040
24877
|
rawCount,
|
|
24041
24878
|
nextAfterSeq,
|
|
@@ -24048,7 +24885,11 @@ var V2E2EECoordinator = class {
|
|
|
24048
24885
|
});
|
|
24049
24886
|
if (!shouldContinue) break;
|
|
24050
24887
|
if (pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
24051
|
-
const nextKey =
|
|
24888
|
+
const nextKey = pullGateKeyForClient(client, "message.v2.pull", {
|
|
24889
|
+
after_seq: nextAfter,
|
|
24890
|
+
force: opts?.force === true,
|
|
24891
|
+
limit
|
|
24892
|
+
}, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
24052
24893
|
await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
|
|
24053
24894
|
pullGateKey = nextKey;
|
|
24054
24895
|
}
|
|
@@ -24140,7 +24981,8 @@ var V2E2EECoordinator = class {
|
|
|
24140
24981
|
});
|
|
24141
24982
|
return client.call("group.v2.send", withExplicitGroupAid({
|
|
24142
24983
|
group_id: gid,
|
|
24143
|
-
envelope
|
|
24984
|
+
envelope,
|
|
24985
|
+
message_id: opts?.messageId
|
|
24144
24986
|
}, groupAid));
|
|
24145
24987
|
};
|
|
24146
24988
|
try {
|
|
@@ -24268,7 +25110,13 @@ var V2E2EECoordinator = class {
|
|
|
24268
25110
|
const ns = `group:${gid}`;
|
|
24269
25111
|
if (opts?.windowMode === "tail") {
|
|
24270
25112
|
if (!opts.gateLocked) {
|
|
24271
|
-
const key =
|
|
25113
|
+
const key = pullGateKeyForClient(client, "group.v2.pull", {
|
|
25114
|
+
group_id: gid,
|
|
25115
|
+
window_mode: "tail",
|
|
25116
|
+
after_seq: afterSeq,
|
|
25117
|
+
limit,
|
|
25118
|
+
_group_cursor_params: opts?.cursorParams
|
|
25119
|
+
}, `${ns}|tail|after=${afterSeq}|limit=${limit}`);
|
|
24272
25120
|
return await client._runPullSerialized(key, () => this.pullGroupV2(gid, afterSeq, limit, {
|
|
24273
25121
|
...opts ?? {},
|
|
24274
25122
|
gateLocked: true
|
|
@@ -24284,7 +25132,13 @@ var V2E2EECoordinator = class {
|
|
|
24284
25132
|
return Array.isArray(result.messages) ? result.messages : [];
|
|
24285
25133
|
}
|
|
24286
25134
|
if (!opts?.gateLocked) {
|
|
24287
|
-
const key =
|
|
25135
|
+
const key = pullGateKeyForClient(client, "group.v2.pull", {
|
|
25136
|
+
group_id: gid,
|
|
25137
|
+
after_seq: afterSeq,
|
|
25138
|
+
force: opts?.force === true,
|
|
25139
|
+
limit,
|
|
25140
|
+
_group_cursor_params: opts?.cursorParams
|
|
25141
|
+
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
24288
25142
|
return await client._runPullSerialized(key, () => this.pullGroupV2(gid, afterSeq, limit, {
|
|
24289
25143
|
...opts ?? {},
|
|
24290
25144
|
gateLocked: true
|
|
@@ -24294,7 +25148,13 @@ var V2E2EECoordinator = class {
|
|
|
24294
25148
|
const wireGroupId = String(opts?.wireGroupId ?? groupId ?? "").trim() || gid;
|
|
24295
25149
|
const cursorParams = opts?.cursorParams ?? {};
|
|
24296
25150
|
const ownsCursor = opts?.ownsCursor !== false;
|
|
24297
|
-
let pullGateKey = ownsCursor ?
|
|
25151
|
+
let pullGateKey = ownsCursor ? pullGateKeyForClient(client, "group.v2.pull", {
|
|
25152
|
+
group_id: gid,
|
|
25153
|
+
after_seq: afterSeq,
|
|
25154
|
+
force: opts?.force === true,
|
|
25155
|
+
limit,
|
|
25156
|
+
_group_cursor_params: cursorParams
|
|
25157
|
+
}, `${ns}|forward|after=${afterSeq}|force=${String(Boolean(opts?.force))}|limit=${limit}`) : "";
|
|
24298
25158
|
let nextAfterSeq = opts?.explicitAfterSeq || opts?.force ? afterSeq : afterSeq || client._seqTracker.getContiguousSeq(ns);
|
|
24299
25159
|
const deferredServerCursor = ownsCursor ? this.pendingForwardCursor(ns, client._seqTracker.getContiguousSeq(ns)) : 0;
|
|
24300
25160
|
const deferredForwardAck = ownsCursor ? this.pendingForwardAck(ns, client._seqTracker.getContiguousSeq(ns)) : 0;
|
|
@@ -24347,6 +25207,7 @@ var V2E2EECoordinator = class {
|
|
|
24347
25207
|
const pageTrackerSnapshot = typeof client._seqTracker.snapshotNamespace === "function" ? client._seqTracker.snapshotNamespace(ns) : null;
|
|
24348
25208
|
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
24349
25209
|
const deferredKeyFetches = /* @__PURE__ */ new Map();
|
|
25210
|
+
let blockedSeq = 0;
|
|
24350
25211
|
for (const msg of messages) {
|
|
24351
25212
|
const seq2 = Number(msg.seq ?? 0);
|
|
24352
25213
|
if (!Number.isFinite(seq2) || seq2 <= 0) continue;
|
|
@@ -24411,6 +25272,9 @@ var V2E2EECoordinator = class {
|
|
|
24411
25272
|
}
|
|
24412
25273
|
const deferStatus = {};
|
|
24413
25274
|
let plaintext = await client._decryptV2Message(msg, true, true, true, true, deferStatus, true);
|
|
25275
|
+
if (deferStatus.deferred) {
|
|
25276
|
+
blockedSeq = blockedSeq > 0 ? Math.min(blockedSeq, seq2) : seq2;
|
|
25277
|
+
}
|
|
24414
25278
|
if (deferStatus.deferred && deferStatus.fromAid) {
|
|
24415
25279
|
const key = `${deferStatus.fromAid}\0${deferStatus.senderDeviceId ?? ""}\0${deferStatus.groupId ?? ""}`;
|
|
24416
25280
|
deferredKeyFetches.set(key, {
|
|
@@ -24439,7 +25303,7 @@ var V2E2EECoordinator = class {
|
|
|
24439
25303
|
Number(cursor?.join_seq ?? 0)
|
|
24440
25304
|
);
|
|
24441
25305
|
const effectiveFloor = Math.max(retentionFloor, visibilityFloor);
|
|
24442
|
-
const commitTarget = Math.max(
|
|
25306
|
+
const commitTarget = blockedSeq > 0 ? pageContigBefore : Math.max(
|
|
24443
25307
|
pageContigBefore,
|
|
24444
25308
|
Number.isFinite(effectiveFloor) ? effectiveFloor : 0,
|
|
24445
25309
|
ownsCursor && pageCount === 1 ? deferredServerCursor : 0,
|
|
@@ -24483,7 +25347,7 @@ var V2E2EECoordinator = class {
|
|
|
24483
25347
|
this.recordForwardCursor(ns, cursorCurrentSeq, ackSeq);
|
|
24484
25348
|
}
|
|
24485
25349
|
const knownServerCursorSeq = hasServerCursor ? cursorCurrentSeq : ownsCursor && pageCount === 1 ? deferredServerCursor : 0;
|
|
24486
|
-
const ackNeeded = ackSeq > 0 && ackSeq > lastAutoAckSeq && ownsCursor && (hasServerCursor && ackSeq > cursorCurrentSeq || contigAdvanced && ackSeq > knownServerCursorSeq);
|
|
25350
|
+
const ackNeeded = blockedSeq <= 0 && ackSeq > 0 && ackSeq > lastAutoAckSeq && ownsCursor && (hasServerCursor && ackSeq > cursorCurrentSeq || contigAdvanced && ackSeq > knownServerCursorSeq);
|
|
24487
25351
|
if (ackNeeded) {
|
|
24488
25352
|
this.recordForwardAck(ns, ackSeq);
|
|
24489
25353
|
const nextAfter2 = Math.max(pageMaxSeq, nextAfterSeq);
|
|
@@ -24499,6 +25363,7 @@ var V2E2EECoordinator = class {
|
|
|
24499
25363
|
const nextAfter = Math.max(pageMaxSeq, nextAfterSeq);
|
|
24500
25364
|
if (!ownsCursor) break;
|
|
24501
25365
|
const rawCount = messages.length;
|
|
25366
|
+
if (blockedSeq > 0) break;
|
|
24502
25367
|
const shouldContinue = shouldContinueForwardPage({
|
|
24503
25368
|
rawCount,
|
|
24504
25369
|
nextAfterSeq,
|
|
@@ -24511,7 +25376,13 @@ var V2E2EECoordinator = class {
|
|
|
24511
25376
|
});
|
|
24512
25377
|
if (!shouldContinue) break;
|
|
24513
25378
|
if (pullGateKey && opts?.gateLocked && client._rpcPipeline?.yieldPullGate) {
|
|
24514
|
-
const nextKey =
|
|
25379
|
+
const nextKey = pullGateKeyForClient(client, "group.v2.pull", {
|
|
25380
|
+
group_id: gid,
|
|
25381
|
+
after_seq: nextAfter,
|
|
25382
|
+
force: opts?.force === true,
|
|
25383
|
+
limit,
|
|
25384
|
+
_group_cursor_params: cursorParams
|
|
25385
|
+
}, `${ns}|forward|after=${nextAfter}|force=${String(Boolean(opts?.force))}|limit=${limit}`);
|
|
24515
25386
|
await client._rpcPipeline.yieldPullGate(pullGateKey, nextKey, true);
|
|
24516
25387
|
pullGateKey = nextKey;
|
|
24517
25388
|
}
|
|
@@ -25547,7 +26418,7 @@ var GroupStateCoordinator = class {
|
|
|
25547
26418
|
if (!isJsonObject(data) || !client._v2Session) return;
|
|
25548
26419
|
const groupId = groupIdFromRecord(data);
|
|
25549
26420
|
if (!groupId) return;
|
|
25550
|
-
|
|
26421
|
+
client._dispatcher.enqueue("group.v2.state_proposed", data);
|
|
25551
26422
|
try {
|
|
25552
26423
|
await client._v2ConfirmPendingProposal(groupId);
|
|
25553
26424
|
} catch (exc) {
|
|
@@ -25559,7 +26430,7 @@ var GroupStateCoordinator = class {
|
|
|
25559
26430
|
if (!isJsonObject(data) || !client._v2Session) return;
|
|
25560
26431
|
const groupId = groupIdFromRecord(data);
|
|
25561
26432
|
if (!groupId) return;
|
|
25562
|
-
|
|
26433
|
+
client._dispatcher.enqueue("group.v2.state_retry_needed", data);
|
|
25563
26434
|
try {
|
|
25564
26435
|
await client._v2AutoProposeState(groupId, { leaderDelay: true });
|
|
25565
26436
|
} catch (exc) {
|
|
@@ -25577,7 +26448,7 @@ var GroupStateCoordinator = class {
|
|
|
25577
26448
|
}
|
|
25578
26449
|
client._v2AutoProposeLastSnapshot?.delete?.(groupId);
|
|
25579
26450
|
}
|
|
25580
|
-
|
|
26451
|
+
client._dispatcher.enqueue("group.v2.state_confirmed", data);
|
|
25581
26452
|
}
|
|
25582
26453
|
async publishV2GroupSecurityLevel(groupId, bootstrap) {
|
|
25583
26454
|
const client = this.client;
|
|
@@ -25588,7 +26459,7 @@ var GroupStateCoordinator = class {
|
|
|
25588
26459
|
const previous = securityLevels.get(gid);
|
|
25589
26460
|
if (previous === level) return;
|
|
25590
26461
|
securityLevels.set(gid, level);
|
|
25591
|
-
|
|
26462
|
+
client._dispatcher.enqueue("group.v2.security_level", {
|
|
25592
26463
|
group_id: gid,
|
|
25593
26464
|
level,
|
|
25594
26465
|
warning: String(bootstrap.e2ee_security_warning ?? ""),
|
|
@@ -25683,7 +26554,7 @@ var GroupStateCoordinator = class {
|
|
|
25683
26554
|
} catch {
|
|
25684
26555
|
}
|
|
25685
26556
|
client._clientLog.warn(`V2 state chain fork detected: group=${gid} local_chain=${localChain.slice(0, 16)}... server_chain=${serverChain.slice(0, 16)}...`);
|
|
25686
|
-
|
|
26557
|
+
client._dispatcher.enqueue("group.v2.fork_detected", {
|
|
25687
26558
|
group_id: gid,
|
|
25688
26559
|
local_chain: localChain,
|
|
25689
26560
|
server_chain: serverChain
|
|
@@ -26084,7 +26955,7 @@ var GroupStateCoordinator = class {
|
|
|
26084
26955
|
const proposalId = isJsonObject(proposeResult) ? String(proposeResult.proposal_id ?? "").trim() : "";
|
|
26085
26956
|
if (proposalId) {
|
|
26086
26957
|
try {
|
|
26087
|
-
await client.call("group.v2.confirm_state", { proposal_id: proposalId });
|
|
26958
|
+
await client.call("group.v2.confirm_state", { proposal_id: proposalId, group_id: groupId });
|
|
26088
26959
|
client._v2AutoProposeLastSnapshot.set(groupId, membershipSnapshot);
|
|
26089
26960
|
client._clientLog.debug(`V2 auto confirm_state: group=${groupId} proposal=${proposalId}`);
|
|
26090
26961
|
} catch (confirmExc) {
|
|
@@ -26137,7 +27008,7 @@ var GroupStateCoordinator = class {
|
|
|
26137
27008
|
return false;
|
|
26138
27009
|
}
|
|
26139
27010
|
if (!await this.verifyPendingProposalAgainstBase(groupId, proposal, stateResp)) return false;
|
|
26140
|
-
await client.call("group.v2.confirm_state", { proposal_id: proposalId });
|
|
27011
|
+
await client.call("group.v2.confirm_state", { proposal_id: proposalId, group_id: groupId });
|
|
26141
27012
|
client._clientLog.info(`V2 confirmed pending proposal: group=${groupId} proposal=${proposalId}`);
|
|
26142
27013
|
return true;
|
|
26143
27014
|
}
|
|
@@ -26217,7 +27088,7 @@ var GroupStateCoordinator = class {
|
|
|
26217
27088
|
}
|
|
26218
27089
|
if (mode !== "open" && mode !== "invite_code" && mode !== "invite_only") {
|
|
26219
27090
|
client._clientLog.warn(`V2 state tamper detected: group=${groupId} pending_extra=${extra.sort().join(",")} mode=${mode}`);
|
|
26220
|
-
|
|
27091
|
+
client._dispatcher.enqueue("group.v2.state_tampered", {
|
|
26221
27092
|
group_id: groupId,
|
|
26222
27093
|
pending_extra: extra.sort(),
|
|
26223
27094
|
mode
|
|
@@ -27167,6 +28038,7 @@ function buildDefaultAgentMd(aid, options = {}) {
|
|
|
27167
28038
|
].join("\n");
|
|
27168
28039
|
}
|
|
27169
28040
|
var HEAD_HTTP_TIMEOUT_MS = 15e3;
|
|
28041
|
+
var AGENT_MD_NEGATIVE_CACHE_TTL_MS = 6e4;
|
|
27170
28042
|
var noopLogger = {
|
|
27171
28043
|
error: () => {
|
|
27172
28044
|
},
|
|
@@ -27400,6 +28272,7 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
27400
28272
|
if (content !== void 0 && content !== null) {
|
|
27401
28273
|
const text3 = String(content ?? "");
|
|
27402
28274
|
if (text3.length === 0) throw new ValidationError("uploadAgentMd requires non-empty content");
|
|
28275
|
+
validateAgentMdDocument(text3, { expectedAid: target });
|
|
27403
28276
|
await this.saveRecord(target, {
|
|
27404
28277
|
content: text3,
|
|
27405
28278
|
local_etag: await _AgentMdManager.contentEtag(text3),
|
|
@@ -27506,6 +28379,24 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
27506
28379
|
ttl_days: Number(ttlDays) || 0
|
|
27507
28380
|
};
|
|
27508
28381
|
}
|
|
28382
|
+
const remoteMissingCached = String(before.remote_status ?? "").trim().toLowerCase() === "missing";
|
|
28383
|
+
if (!localFound && !remoteEtagCached && remoteMissingCached && _AgentMdManager.checkedAtFresh(checkedAtCached, ttlDays)) {
|
|
28384
|
+
return {
|
|
28385
|
+
aid: target,
|
|
28386
|
+
local_found: false,
|
|
28387
|
+
remote_found: false,
|
|
28388
|
+
local_etag: "",
|
|
28389
|
+
remote_etag: "",
|
|
28390
|
+
in_sync: false,
|
|
28391
|
+
needs_update: false,
|
|
28392
|
+
last_modified: "",
|
|
28393
|
+
status: 404,
|
|
28394
|
+
cached: true,
|
|
28395
|
+
verify_status: "",
|
|
28396
|
+
verify_error: "",
|
|
28397
|
+
ttl_days: Number(ttlDays) || 0
|
|
28398
|
+
};
|
|
28399
|
+
}
|
|
27509
28400
|
const now = Date.now();
|
|
27510
28401
|
let remote;
|
|
27511
28402
|
try {
|
|
@@ -27995,14 +28886,16 @@ var AgentMdManager = class _AgentMdManager {
|
|
|
27995
28886
|
async _scheduleFetchIfMissing(aid, record, source = "") {
|
|
27996
28887
|
const target = String(aid ?? "").trim();
|
|
27997
28888
|
if (!target || await this._hasLocalContent(target, record)) return;
|
|
28889
|
+
const cached = record ?? await this.loadRecord(target) ?? {};
|
|
28890
|
+
const checkedAt = Number(cached.checked_at ?? 0) || 0;
|
|
28891
|
+
if (String(cached.remote_status ?? "").trim().toLowerCase() === "missing" && checkedAt > 0 && Date.now() - checkedAt <= AGENT_MD_NEGATIVE_CACHE_TTL_MS) return;
|
|
27998
28892
|
if (this._fetchInflight.has(target)) return;
|
|
27999
28893
|
this._fetchInflight.add(target);
|
|
28000
28894
|
try {
|
|
28001
28895
|
await this.download(target);
|
|
28002
28896
|
} catch (err) {
|
|
28003
28897
|
await this.saveRecord(target, {
|
|
28004
|
-
last_error: err instanceof Error ? err.message : String(err)
|
|
28005
|
-
remote_status: "found"
|
|
28898
|
+
last_error: err instanceof Error ? err.message : String(err)
|
|
28006
28899
|
});
|
|
28007
28900
|
this._log.debug(`agent.md auto fetch failed: aid=${target} source=${source || "-"} err=${err instanceof Error ? err.message : String(err)}`);
|
|
28008
28901
|
} finally {
|
|
@@ -28426,7 +29319,6 @@ var _AUNClient = class _AUNClient {
|
|
|
28426
29319
|
__publicField(this, "_reconnectActive", false);
|
|
28427
29320
|
__publicField(this, "_reconnectAbort", null);
|
|
28428
29321
|
__publicField(this, "_reconnectTask", null);
|
|
28429
|
-
__publicField(this, "_reconnectEventDispatchDepth", 0);
|
|
28430
29322
|
__publicField(this, "_serverKicked", false);
|
|
28431
29323
|
// 重连状态追踪(对齐 Python client.py)
|
|
28432
29324
|
__publicField(this, "_nextRetryAt", null);
|
|
@@ -28606,7 +29498,7 @@ var _AUNClient = class _AUNClient {
|
|
|
28606
29498
|
});
|
|
28607
29499
|
for (const evt of ["message.ack", "storage.object_changed", "stream/created", "stream/closed"]) {
|
|
28608
29500
|
this._dispatcher.subscribe(`_raw.${evt}`, (data) => {
|
|
28609
|
-
this._dispatcher.
|
|
29501
|
+
this._dispatcher.enqueue(evt, data);
|
|
28610
29502
|
});
|
|
28611
29503
|
}
|
|
28612
29504
|
this._dispatcher.subscribe("_raw.gateway.disconnect", async (data) => {
|
|
@@ -28877,6 +29769,7 @@ var _AUNClient = class _AUNClient {
|
|
|
28877
29769
|
this._peerCache.clear();
|
|
28878
29770
|
this._certCache.clear();
|
|
28879
29771
|
this._gatewayUrl = null;
|
|
29772
|
+
this._gatewayCandidates = [];
|
|
28880
29773
|
this._deviceId = aid.deviceId || getDeviceId();
|
|
28881
29774
|
this._slotId = aid.slotId || "default";
|
|
28882
29775
|
this._logger = new AUNLogger({ debug: aid.debug, aunPath: nextConfig.aunPath });
|
|
@@ -29056,7 +29949,7 @@ var _AUNClient = class _AUNClient {
|
|
|
29056
29949
|
if (!message.toLowerCase().startsWith(agentPrefix)) return false;
|
|
29057
29950
|
const target = message.slice(agentPrefix.length).trim();
|
|
29058
29951
|
if (!target || target.toLowerCase() !== String(this._aid ?? "").trim().toLowerCase()) return false;
|
|
29059
|
-
await this._agentMdManager.upload(buildDefaultAgentMd(target));
|
|
29952
|
+
await this._agentMdManager.upload(await this._agentMdManager.readContent(target) ?? buildDefaultAgentMd(target));
|
|
29060
29953
|
return true;
|
|
29061
29954
|
}
|
|
29062
29955
|
async _runGroupIdentityOperation(groupId, operation) {
|
|
@@ -29085,6 +29978,10 @@ var _AUNClient = class _AUNClient {
|
|
|
29085
29978
|
}
|
|
29086
29979
|
this._aidStore = store;
|
|
29087
29980
|
const payload = { ...params2 };
|
|
29981
|
+
if (payload.group_name === void 0 && payload.groupName !== void 0) {
|
|
29982
|
+
payload.group_name = payload.groupName;
|
|
29983
|
+
delete payload.groupName;
|
|
29984
|
+
}
|
|
29088
29985
|
const isNamed = Boolean(String(payload.group_name ?? "").trim());
|
|
29089
29986
|
payload._defer_group_ready_postprocess = true;
|
|
29090
29987
|
if (!isNamed) {
|
|
@@ -29112,7 +30009,24 @@ var _AUNClient = class _AUNClient {
|
|
|
29112
30009
|
delete postprocessParams2._defer_group_ready_postprocess;
|
|
29113
30010
|
return await this._groupState.postprocessResult("group.create", postprocessParams2, result2);
|
|
29114
30011
|
}
|
|
29115
|
-
const
|
|
30012
|
+
const pendingKey = `create:${String(payload.group_name).trim().toLowerCase()}`;
|
|
30013
|
+
const keystore = store._keystore;
|
|
30014
|
+
let keyPair = null;
|
|
30015
|
+
if (keystore && typeof keystore.loadPendingGroupBind === "function") {
|
|
30016
|
+
keyPair = await keystore.loadPendingGroupBind(pendingKey);
|
|
30017
|
+
}
|
|
30018
|
+
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
30019
|
+
const generated = await new CryptoProvider().generateIdentity();
|
|
30020
|
+
keyPair = {
|
|
30021
|
+
private_key_pem: generated.private_key_pem,
|
|
30022
|
+
public_key_der_b64: generated.public_key_der_b64,
|
|
30023
|
+
curve: generated.curve
|
|
30024
|
+
};
|
|
30025
|
+
if (keystore && typeof keystore.savePendingGroupBind === "function") {
|
|
30026
|
+
await keystore.savePendingGroupBind(pendingKey, keyPair);
|
|
30027
|
+
}
|
|
30028
|
+
}
|
|
30029
|
+
if (!keyPair) throw new ValidationError("createGroup: failed to generate or load key pair");
|
|
29116
30030
|
payload.public_key = keyPair.public_key_der_b64;
|
|
29117
30031
|
payload.curve = keyPair.curve;
|
|
29118
30032
|
const result = await this.call("group.create", payload);
|
|
@@ -29138,6 +30052,9 @@ var _AUNClient = class _AUNClient {
|
|
|
29138
30052
|
throw new ValidationError("createGroup requires current owner AID for group agent.md upload");
|
|
29139
30053
|
}
|
|
29140
30054
|
await this._uploadGroupAgentMd(store, groupAid, payload, group, uploaderAid);
|
|
30055
|
+
if (keystore && typeof keystore.clearPendingGroupBind === "function") {
|
|
30056
|
+
await keystore.clearPendingGroupBind(pendingKey);
|
|
30057
|
+
}
|
|
29141
30058
|
const postprocessParams = { ...payload };
|
|
29142
30059
|
delete postprocessParams._defer_group_ready_postprocess;
|
|
29143
30060
|
return await this._groupState.postprocessResult("group.create", postprocessParams, result);
|
|
@@ -29195,10 +30112,14 @@ var _AUNClient = class _AUNClient {
|
|
|
29195
30112
|
}
|
|
29196
30113
|
this._aidStore = store;
|
|
29197
30114
|
const groupId = String(params2.group_id ?? params2.group_aid ?? "").trim();
|
|
30115
|
+
const pendingKey = `bind:${groupId}`;
|
|
29198
30116
|
const keystore = store._keystore;
|
|
29199
30117
|
let keyPair = null;
|
|
29200
30118
|
if (groupId && keystore && typeof keystore.loadPendingGroupBind === "function") {
|
|
29201
|
-
keyPair = await keystore.loadPendingGroupBind(
|
|
30119
|
+
keyPair = await keystore.loadPendingGroupBind(pendingKey);
|
|
30120
|
+
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
30121
|
+
keyPair = await keystore.loadPendingGroupBind(groupId);
|
|
30122
|
+
}
|
|
29202
30123
|
}
|
|
29203
30124
|
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
29204
30125
|
const generated = await new CryptoProvider().generateIdentity();
|
|
@@ -29208,7 +30129,7 @@ var _AUNClient = class _AUNClient {
|
|
|
29208
30129
|
curve: generated.curve
|
|
29209
30130
|
};
|
|
29210
30131
|
if (groupId && keystore && typeof keystore.savePendingGroupBind === "function") {
|
|
29211
|
-
await keystore.savePendingGroupBind(
|
|
30132
|
+
await keystore.savePendingGroupBind(pendingKey, keyPair);
|
|
29212
30133
|
}
|
|
29213
30134
|
}
|
|
29214
30135
|
if (!keyPair) {
|
|
@@ -29244,6 +30165,7 @@ var _AUNClient = class _AUNClient {
|
|
|
29244
30165
|
}
|
|
29245
30166
|
await this._uploadGroupAgentMd(store, groupAid, payload, group, uploaderAid);
|
|
29246
30167
|
if (groupId && keystore && typeof keystore.clearPendingGroupBind === "function") {
|
|
30168
|
+
await keystore.clearPendingGroupBind(pendingKey);
|
|
29247
30169
|
await keystore.clearPendingGroupBind(groupId);
|
|
29248
30170
|
}
|
|
29249
30171
|
return result;
|
|
@@ -29280,7 +30202,24 @@ var _AUNClient = class _AUNClient {
|
|
|
29280
30202
|
if (!oldPublicKey) {
|
|
29281
30203
|
throw new ValidationError(`renewGroupAid: cannot determine old public key for ${groupAid}`);
|
|
29282
30204
|
}
|
|
29283
|
-
const
|
|
30205
|
+
const keystore = store._keystore;
|
|
30206
|
+
const pendingKey = `renew:${groupId}`;
|
|
30207
|
+
let newKeyPair = null;
|
|
30208
|
+
if (keystore && typeof keystore.loadPendingGroupBind === "function") {
|
|
30209
|
+
newKeyPair = await keystore.loadPendingGroupBind(pendingKey);
|
|
30210
|
+
}
|
|
30211
|
+
if (!newKeyPair || !newKeyPair.public_key_der_b64 || !newKeyPair.private_key_pem) {
|
|
30212
|
+
const generated = await new CryptoProvider().generateIdentity();
|
|
30213
|
+
newKeyPair = {
|
|
30214
|
+
private_key_pem: generated.private_key_pem,
|
|
30215
|
+
public_key_der_b64: generated.public_key_der_b64,
|
|
30216
|
+
curve: generated.curve
|
|
30217
|
+
};
|
|
30218
|
+
if (keystore && typeof keystore.savePendingGroupBind === "function") {
|
|
30219
|
+
await keystore.savePendingGroupBind(pendingKey, newKeyPair);
|
|
30220
|
+
}
|
|
30221
|
+
}
|
|
30222
|
+
if (!newKeyPair) throw new ValidationError("renewGroupAid: failed to generate or load key pair");
|
|
29284
30223
|
const newPublicKey = newKeyPair.public_key_der_b64;
|
|
29285
30224
|
const newPrivateKey = newKeyPair.private_key_pem;
|
|
29286
30225
|
const curve = newKeyPair.curve || "P-256";
|
|
@@ -29336,6 +30275,9 @@ var _AUNClient = class _AUNClient {
|
|
|
29336
30275
|
throw new ValidationError("renewGroupAid requires current owner AID for group agent.md upload");
|
|
29337
30276
|
}
|
|
29338
30277
|
await this._uploadGroupAgentMd(store, returnedGroupAid, payload, group, uploaderAid);
|
|
30278
|
+
if (keystore && typeof keystore.clearPendingGroupBind === "function") {
|
|
30279
|
+
await keystore.clearPendingGroupBind(pendingKey);
|
|
30280
|
+
}
|
|
29339
30281
|
return result;
|
|
29340
30282
|
}
|
|
29341
30283
|
async startGroupTransfer(params2 = {}, options = {}) {
|
|
@@ -29393,12 +30335,12 @@ var _AUNClient = class _AUNClient {
|
|
|
29393
30335
|
if (!store) {
|
|
29394
30336
|
throw new ValidationError("completeGroupTransfer requires aidStore");
|
|
29395
30337
|
}
|
|
29396
|
-
const keyPair = await new CryptoProvider().generateIdentity();
|
|
29397
30338
|
const payload = { ...params2 };
|
|
29398
30339
|
const groupId = String(params2.group_id ?? "").trim();
|
|
29399
30340
|
if (!groupId) {
|
|
29400
30341
|
throw new ValidationError("completeGroupTransfer requires group_id");
|
|
29401
30342
|
}
|
|
30343
|
+
const pendingKey = `complete:${groupId}`;
|
|
29402
30344
|
let groupAid = String(params2.group_aid ?? "").trim();
|
|
29403
30345
|
if (!groupAid) {
|
|
29404
30346
|
const info = await this.call("group.get_info", { group_id: groupId, required: ["member"] });
|
|
@@ -29407,6 +30349,23 @@ var _AUNClient = class _AUNClient {
|
|
|
29407
30349
|
if (!groupAid) {
|
|
29408
30350
|
throw new ValidationError("completeGroupTransfer: unable to determine group_aid");
|
|
29409
30351
|
}
|
|
30352
|
+
const keystore = store._keystore;
|
|
30353
|
+
let keyPair = null;
|
|
30354
|
+
if (keystore && typeof keystore.loadPendingGroupBind === "function") {
|
|
30355
|
+
keyPair = await keystore.loadPendingGroupBind(pendingKey);
|
|
30356
|
+
}
|
|
30357
|
+
if (!keyPair || !keyPair.public_key_der_b64 || !keyPair.private_key_pem) {
|
|
30358
|
+
const generated = await new CryptoProvider().generateIdentity();
|
|
30359
|
+
keyPair = {
|
|
30360
|
+
private_key_pem: generated.private_key_pem,
|
|
30361
|
+
public_key_der_b64: generated.public_key_der_b64,
|
|
30362
|
+
curve: generated.curve
|
|
30363
|
+
};
|
|
30364
|
+
if (keystore && typeof keystore.savePendingGroupBind === "function") {
|
|
30365
|
+
await keystore.savePendingGroupBind(pendingKey, keyPair);
|
|
30366
|
+
}
|
|
30367
|
+
}
|
|
30368
|
+
if (!keyPair) throw new ValidationError("completeGroupTransfer: failed to generate or load key pair");
|
|
29410
30369
|
const current = this.currentAid;
|
|
29411
30370
|
const newOwner = String(current?.aid ?? "").trim();
|
|
29412
30371
|
if (!current || !newOwner || !current.isPrivateKeyValid()) {
|
|
@@ -29460,6 +30419,9 @@ var _AUNClient = class _AUNClient {
|
|
|
29460
30419
|
throw new ValidationError("completeGroupTransfer requires current owner AID for group agent.md upload");
|
|
29461
30420
|
}
|
|
29462
30421
|
await this._uploadGroupAgentMd(store, returnedGroupAid, payload, group, uploaderAid);
|
|
30422
|
+
if (keystore && typeof keystore.clearPendingGroupBind === "function") {
|
|
30423
|
+
await keystore.clearPendingGroupBind(pendingKey);
|
|
30424
|
+
}
|
|
29463
30425
|
return result;
|
|
29464
30426
|
}
|
|
29465
30427
|
static _notifyParamsSizeOk(params2) {
|
|
@@ -29623,8 +30585,8 @@ var _AUNClient = class _AUNClient {
|
|
|
29623
30585
|
_markPublishedSeq(ns, seq2) {
|
|
29624
30586
|
this._delivery.markPublishedSeq(ns, seq2);
|
|
29625
30587
|
}
|
|
29626
|
-
async _publishAppEvent(event, payload) {
|
|
29627
|
-
await this._delivery.publishAppEvent(event, payload);
|
|
30588
|
+
async _publishAppEvent(event, payload, source = "", ns = "", batch) {
|
|
30589
|
+
await this._delivery.publishAppEvent(event, payload, source, ns, batch);
|
|
29628
30590
|
}
|
|
29629
30591
|
_echoTimestamp() {
|
|
29630
30592
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -29662,11 +30624,11 @@ var _AUNClient = class _AUNClient {
|
|
|
29662
30624
|
async _drainOrderedMessages(ns, beforeSeq, pullResponse = false, persist = true) {
|
|
29663
30625
|
await this._delivery.drainOrderedMessages(ns, beforeSeq, pullResponse, persist);
|
|
29664
30626
|
}
|
|
29665
|
-
async _publishOrderedMessage(event, ns, seq2, payload) {
|
|
29666
|
-
return this._delivery.publishOrderedMessage(event, ns, seq2, payload);
|
|
30627
|
+
async _publishOrderedMessage(event, ns, seq2, payload, source = "push", batch) {
|
|
30628
|
+
return this._delivery.publishOrderedMessage(event, ns, seq2, payload, source, batch);
|
|
29667
30629
|
}
|
|
29668
|
-
async _publishPulledMessage(event, ns, seq2, payload, persist = true) {
|
|
29669
|
-
return this._delivery.publishPulledMessage(event, ns, seq2, payload, persist);
|
|
30630
|
+
async _publishPulledMessage(event, ns, seq2, payload, persist = true, source = "pull", batch) {
|
|
30631
|
+
return this._delivery.publishPulledMessage(event, ns, seq2, payload, persist, source, batch);
|
|
29670
30632
|
}
|
|
29671
30633
|
_extractGroupIdFromResult(result) {
|
|
29672
30634
|
const group = isJsonObject(result.group) ? result.group : null;
|
|
@@ -29697,7 +30659,7 @@ var _AUNClient = class _AUNClient {
|
|
|
29697
30659
|
const groupId = d.group_id ?? d.group_aid ?? "";
|
|
29698
30660
|
await this._delivery.handleGroupChangedEventSeq(d, groupId);
|
|
29699
30661
|
} else {
|
|
29700
|
-
|
|
30662
|
+
this._dispatcher.enqueue("group.changed", data);
|
|
29701
30663
|
}
|
|
29702
30664
|
this._clientLog.debug(`_onRawGroupChanged exit: elapsed=${Date.now() - tStart}ms group_id=${groupIdInit}`);
|
|
29703
30665
|
} catch (err) {
|
|
@@ -29770,7 +30732,7 @@ var _AUNClient = class _AUNClient {
|
|
|
29770
30732
|
const ok = await ecdsaVerifyDer(pubKey, sigBytes, signData);
|
|
29771
30733
|
if (!ok) {
|
|
29772
30734
|
this._clientLog.warn(`group event sig verify failed aid=%s method=%s${sigAid} ${method}`);
|
|
29773
|
-
this._dispatcher.
|
|
30735
|
+
this._dispatcher.enqueue("signature.verification_failed", {
|
|
29774
30736
|
aid: sigAid,
|
|
29775
30737
|
method,
|
|
29776
30738
|
error: "ECDSA verification failed"
|
|
@@ -29779,7 +30741,7 @@ var _AUNClient = class _AUNClient {
|
|
|
29779
30741
|
return ok;
|
|
29780
30742
|
} catch (exc) {
|
|
29781
30743
|
this._clientLog.warn(`group event sig verify exception:${String(exc)}`);
|
|
29782
|
-
this._dispatcher.
|
|
30744
|
+
this._dispatcher.enqueue("signature.verification_failed", {
|
|
29783
30745
|
aid: sigAid,
|
|
29784
30746
|
method,
|
|
29785
30747
|
error: String(exc)
|
|
@@ -29997,7 +30959,7 @@ var _AUNClient = class _AUNClient {
|
|
|
29997
30959
|
throw new StateError("connection attempt superseded");
|
|
29998
30960
|
}
|
|
29999
30961
|
} else {
|
|
30000
|
-
|
|
30962
|
+
this._dispatcher.enqueue("state_change", statePayload);
|
|
30001
30963
|
}
|
|
30002
30964
|
this._assertReconnectOwner(reconnectOwner);
|
|
30003
30965
|
this._lifecycle.assertConnectionAttemptOwner(connectionOwner);
|
|
@@ -30040,16 +31002,21 @@ var _AUNClient = class _AUNClient {
|
|
|
30040
31002
|
async _resolveGatewayCandidatesForAid(aid) {
|
|
30041
31003
|
const target = String(aid ?? this._aid ?? "").trim();
|
|
30042
31004
|
if (!target) throw new StateError("gateway discovery requires a loaded AID");
|
|
31005
|
+
const discovery = this._discovery;
|
|
31006
|
+
const tokenStore = this._tokenStore;
|
|
31007
|
+
const discoveryIsCurrent = () => this._discovery === discovery && this._tokenStore === tokenStore && !this._closing;
|
|
30043
31008
|
if (this._gatewayCandidates.length > 0) return [...this._gatewayCandidates];
|
|
30044
31009
|
if (this._gatewayUrl) {
|
|
30045
31010
|
this._gatewayCandidates = [this._gatewayUrl];
|
|
30046
31011
|
return [...this._gatewayCandidates];
|
|
30047
31012
|
}
|
|
30048
31013
|
try {
|
|
30049
|
-
const getMetadata =
|
|
30050
|
-
const rawList = typeof getMetadata === "function" ? String(await getMetadata.call(
|
|
31014
|
+
const getMetadata = tokenStore.getMetadata;
|
|
31015
|
+
const rawList = typeof getMetadata === "function" ? String(await getMetadata.call(tokenStore, target, "gateway_urls") ?? "").trim() : "";
|
|
31016
|
+
if (!discoveryIsCurrent()) throw new StateError("gateway discovery superseded by identity change");
|
|
30051
31017
|
const cachedList = rawList ? JSON.parse(rawList) : [];
|
|
30052
|
-
const raw = typeof getMetadata === "function" ? String(await getMetadata.call(
|
|
31018
|
+
const raw = typeof getMetadata === "function" ? String(await getMetadata.call(tokenStore, target, "gateway_url") ?? "").trim() : "";
|
|
31019
|
+
if (!discoveryIsCurrent()) throw new StateError("gateway discovery superseded by identity change");
|
|
30053
31020
|
const candidates2 = Array.isArray(cachedList) ? cachedList.map((item) => String(item ?? "").trim()).filter(Boolean) : [];
|
|
30054
31021
|
const gateway = candidates2[0] ?? (raw.startsWith('"') && raw.endsWith('"') ? String(JSON.parse(raw)).trim() : raw);
|
|
30055
31022
|
if (gateway) {
|
|
@@ -30058,6 +31025,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30058
31025
|
return [...this._gatewayCandidates];
|
|
30059
31026
|
}
|
|
30060
31027
|
} catch {
|
|
31028
|
+
if (!discoveryIsCurrent()) throw new StateError("gateway discovery superseded by identity change");
|
|
30061
31029
|
}
|
|
30062
31030
|
const dotIdx = target.indexOf(".");
|
|
30063
31031
|
const issuerDomain = dotIdx >= 0 ? target.slice(dotIdx + 1) : target;
|
|
@@ -30069,22 +31037,26 @@ var _AUNClient = class _AUNClient {
|
|
|
30069
31037
|
let lastError = null;
|
|
30070
31038
|
for (const url of candidates) {
|
|
30071
31039
|
try {
|
|
30072
|
-
const discovered = await
|
|
31040
|
+
const discovered = await discovery.discoverAll(url);
|
|
31041
|
+
if (!discoveryIsCurrent()) throw new StateError("gateway discovery superseded by identity change");
|
|
30073
31042
|
const gatewayUrls = [...new Set(discovered.map((item) => String(item ?? "").trim()).filter(Boolean))];
|
|
30074
31043
|
if (gatewayUrls.length === 0) throw new ValidationError("gateway discovery returned no candidates");
|
|
30075
31044
|
const gateway = gatewayUrls[0];
|
|
30076
31045
|
this._gatewayCandidates = gatewayUrls;
|
|
30077
31046
|
this._gatewayUrl = gateway;
|
|
30078
31047
|
try {
|
|
30079
|
-
const setMetadata =
|
|
31048
|
+
const setMetadata = tokenStore.setMetadata;
|
|
30080
31049
|
if (typeof setMetadata === "function") {
|
|
30081
|
-
await setMetadata.call(
|
|
30082
|
-
|
|
31050
|
+
await setMetadata.call(tokenStore, target, "gateway_url", gateway);
|
|
31051
|
+
if (!discoveryIsCurrent()) throw new StateError("gateway discovery superseded by identity change");
|
|
31052
|
+
await setMetadata.call(tokenStore, target, "gateway_urls", JSON.stringify(gatewayUrls));
|
|
31053
|
+
if (!discoveryIsCurrent()) throw new StateError("gateway discovery superseded by identity change");
|
|
30083
31054
|
}
|
|
30084
31055
|
} catch {
|
|
30085
31056
|
}
|
|
30086
31057
|
return [...gatewayUrls];
|
|
30087
31058
|
} catch (err) {
|
|
31059
|
+
if (!discoveryIsCurrent()) throw new StateError("gateway discovery superseded by identity change");
|
|
30088
31060
|
lastError = err;
|
|
30089
31061
|
}
|
|
30090
31062
|
}
|
|
@@ -30304,7 +31276,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30304
31276
|
if (this._sessionParams && identity.access_token) {
|
|
30305
31277
|
this._sessionParams.access_token = identity.access_token;
|
|
30306
31278
|
}
|
|
30307
|
-
|
|
31279
|
+
this._dispatcher.enqueue("token.refreshed", {
|
|
30308
31280
|
aid: identity.aid,
|
|
30309
31281
|
expires_at: identity.access_token_expires_at
|
|
30310
31282
|
});
|
|
@@ -30313,7 +31285,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30313
31285
|
if (exc instanceof AuthError) {
|
|
30314
31286
|
if (authErrorRequiresRelogin(exc)) {
|
|
30315
31287
|
this._clientLog.warn(`token refresh requires relogin, stopping refresh loop and triggering reconnect: ${exc.message}`);
|
|
30316
|
-
|
|
31288
|
+
this._dispatcher.enqueue("token.refresh_exhausted", {
|
|
30317
31289
|
aid: this._identity?.aid ?? null,
|
|
30318
31290
|
consecutive_failures: 1,
|
|
30319
31291
|
last_error: String(exc),
|
|
@@ -30326,7 +31298,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30326
31298
|
this._tokenRefreshFailures++;
|
|
30327
31299
|
if (this._tokenRefreshFailures >= 3) {
|
|
30328
31300
|
this._clientLog.warn(`token refresh failed ${this._tokenRefreshFailures} consecutive times, stopping refresh loop and triggering reconnect`);
|
|
30329
|
-
|
|
31301
|
+
this._dispatcher.enqueue("token.refresh_exhausted", {
|
|
30330
31302
|
aid: this._identity?.aid ?? null,
|
|
30331
31303
|
consecutive_failures: this._tokenRefreshFailures,
|
|
30332
31304
|
last_error: String(exc)
|
|
@@ -30337,7 +31309,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30337
31309
|
}
|
|
30338
31310
|
this._clientLog.warn(`token refresh failed (${this._tokenRefreshFailures}/3), next retry: ${String(exc)}`);
|
|
30339
31311
|
} else {
|
|
30340
|
-
this._dispatcher.
|
|
31312
|
+
this._dispatcher.enqueue("connection.error", { error: formatCaughtError2(exc) });
|
|
30341
31313
|
}
|
|
30342
31314
|
}
|
|
30343
31315
|
scheduleRefresh();
|
|
@@ -30366,7 +31338,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30366
31338
|
this._serverKicked = !retryable;
|
|
30367
31339
|
this._lastDisconnectInfo = { code, reason, detail };
|
|
30368
31340
|
try {
|
|
30369
|
-
|
|
31341
|
+
this._dispatcher.enqueue("gateway.disconnect", { code, reason, detail });
|
|
30370
31342
|
} catch (exc) {
|
|
30371
31343
|
this._clientLog.debug(`publish gateway.disconnect failed: ${exc?.message ?? exc}`);
|
|
30372
31344
|
}
|
|
@@ -30385,7 +31357,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30385
31357
|
} catch (exc) {
|
|
30386
31358
|
this._clientLog.debug(`transport cleanup skipped: ${formatCaughtError2(exc)}`);
|
|
30387
31359
|
}
|
|
30388
|
-
|
|
31360
|
+
this._dispatcher.enqueue("state_change", {
|
|
30389
31361
|
state: this._publicState(this._state),
|
|
30390
31362
|
error
|
|
30391
31363
|
});
|
|
@@ -30421,7 +31393,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30421
31393
|
if (disconnectInfo.code !== void 0 && disconnectInfo.code !== null) {
|
|
30422
31394
|
eventPayload.code = disconnectInfo.code;
|
|
30423
31395
|
}
|
|
30424
|
-
|
|
31396
|
+
this._dispatcher.enqueue("state_change", eventPayload);
|
|
30425
31397
|
if (this._reconnectAbort === reconnectAbort) {
|
|
30426
31398
|
this._reconnectAbort = null;
|
|
30427
31399
|
this._reconnectActive = false;
|
|
@@ -30452,12 +31424,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30452
31424
|
}
|
|
30453
31425
|
async _publishReconnectEvent(owner, event, payload) {
|
|
30454
31426
|
if (!this._ownsReconnect(owner)) return false;
|
|
30455
|
-
this.
|
|
30456
|
-
try {
|
|
30457
|
-
await this._dispatcher.publish(event, payload);
|
|
30458
|
-
} finally {
|
|
30459
|
-
this._reconnectEventDispatchDepth -= 1;
|
|
30460
|
-
}
|
|
31427
|
+
this._dispatcher.enqueue(event, payload);
|
|
30461
31428
|
return this._ownsReconnect(owner);
|
|
30462
31429
|
}
|
|
30463
31430
|
async _cancelReconnectAndWait() {
|
|
@@ -30470,7 +31437,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30470
31437
|
} catch (exc) {
|
|
30471
31438
|
this._clientLog.debug(`reconnect cancellation transport cleanup skipped: ${formatCaughtError2(exc)}`);
|
|
30472
31439
|
}
|
|
30473
|
-
if (task
|
|
31440
|
+
if (task) {
|
|
30474
31441
|
try {
|
|
30475
31442
|
await task;
|
|
30476
31443
|
} catch (exc) {
|
|
@@ -30993,7 +31960,7 @@ var _AUNClient = class _AUNClient {
|
|
|
30993
31960
|
_spk_id: spkId
|
|
30994
31961
|
};
|
|
30995
31962
|
attachV2EnvelopeMetadata2(event, e2eeMeta);
|
|
30996
|
-
|
|
31963
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
30997
31964
|
} catch {
|
|
30998
31965
|
}
|
|
30999
31966
|
}
|
|
@@ -31034,7 +32001,7 @@ var _AUNClient = class _AUNClient {
|
|
|
31034
32001
|
_suite: String(envelope.suite ?? "")
|
|
31035
32002
|
};
|
|
31036
32003
|
attachV2EnvelopeMetadata2(event, e2eeMeta);
|
|
31037
|
-
|
|
32004
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
31038
32005
|
} catch {
|
|
31039
32006
|
}
|
|
31040
32007
|
}
|
|
@@ -31070,7 +32037,7 @@ var _AUNClient = class _AUNClient {
|
|
|
31070
32037
|
_suite: String(envelope.suite ?? "")
|
|
31071
32038
|
};
|
|
31072
32039
|
attachV2EnvelopeMetadata2(event, e2eeMeta);
|
|
31073
|
-
|
|
32040
|
+
this._dispatcher.enqueue(undecryptableEvent, event);
|
|
31074
32041
|
} catch {
|
|
31075
32042
|
}
|
|
31076
32043
|
}
|
|
@@ -31645,7 +32612,7 @@ var RegisterFlow = class _RegisterFlow {
|
|
|
31645
32612
|
return new Promise((resolve, reject) => {
|
|
31646
32613
|
let ws;
|
|
31647
32614
|
try {
|
|
31648
|
-
ws =
|
|
32615
|
+
ws = createTransportWebSocket(gatewayUrl);
|
|
31649
32616
|
} catch {
|
|
31650
32617
|
reject(new AuthError(`WebSocket \u8FDE\u63A5\u5931\u8D25: ${gatewayUrl}`));
|
|
31651
32618
|
return;
|
|
@@ -31679,7 +32646,19 @@ var RegisterFlow = class _RegisterFlow {
|
|
|
31679
32646
|
if (!receivedChallenge) {
|
|
31680
32647
|
if (!isJsonObject(msg) || msg.method !== "challenge") return;
|
|
31681
32648
|
receivedChallenge = true;
|
|
31682
|
-
ws.send(JSON.stringify({ jsonrpc: "2.0", id: requestId, method, params: params2 }));
|
|
32649
|
+
const sendResult = ws.send(JSON.stringify({ jsonrpc: "2.0", id: requestId, method, params: params2 }));
|
|
32650
|
+
if (sendResult && typeof sendResult.then === "function") {
|
|
32651
|
+
void Promise.resolve(sendResult).catch((error) => {
|
|
32652
|
+
if (settled) return;
|
|
32653
|
+
settled = true;
|
|
32654
|
+
globalThis.clearTimeout(timeout);
|
|
32655
|
+
try {
|
|
32656
|
+
ws.close();
|
|
32657
|
+
} catch {
|
|
32658
|
+
}
|
|
32659
|
+
reject(error instanceof Error ? error : new AuthError(String(error)));
|
|
32660
|
+
});
|
|
32661
|
+
}
|
|
31683
32662
|
return;
|
|
31684
32663
|
}
|
|
31685
32664
|
if (!isJsonObject(msg) || msg.id !== requestId) return;
|
|
@@ -32600,8 +33579,7 @@ var AIDStore = class {
|
|
|
32600
33579
|
});
|
|
32601
33580
|
}
|
|
32602
33581
|
await this._persistGatewayUrl(target, gatewayUrl);
|
|
32603
|
-
|
|
32604
|
-
if (!uploaded.ok) return uploaded;
|
|
33582
|
+
await this.uploadAgentMd(target, buildDefaultAgentMd(target));
|
|
32605
33583
|
return resultOk({ registered: true });
|
|
32606
33584
|
} catch (exc) {
|
|
32607
33585
|
if (exc instanceof IdentityConflictError) {
|