@agentunion/fastaun-browser 0.4.8 → 0.4.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 +33 -0
- package/_packed_docs/CHANGELOG.md +33 -0
- package/_packed_docs/INDEX.md +43 -20
- package/_packed_docs/KITE_DOCS_GUIDE.md +22 -15
- package/_packed_docs/protocol/06-/346/234/215/345/212/241/345/215/217/350/256/256.md +244 -16
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +113 -27
- package/_packed_docs/sdk/09-group-rpc-manual.md +97 -0
- package/_packed_docs/sdk/09-proxy-rpc-manual.md +231 -0
- package/_packed_docs/sdk/09-storage-rpc-manual.md +117 -4
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +18 -13
- package/_packed_docs/sdk/INDEX.md +26 -14
- package/_packed_docs/sdk/Notify/351/200/232/347/237/245/346/226/271/346/241/210.md +214 -0
- package/_packed_docs/sdk/README.md +9 -6
- package/dist/bundle.js +2015 -440
- package/dist/client/delivery.d.ts +4 -0
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +188 -15
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.d.ts.map +1 -1
- package/dist/client/group-state.js +13 -21
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/identity.d.ts.map +1 -1
- package/dist/client/identity.js +5 -11
- package/dist/client/identity.js.map +1 -1
- package/dist/client/lifecycle.d.ts +2 -0
- package/dist/client/lifecycle.d.ts.map +1 -1
- package/dist/client/lifecycle.js +86 -26
- package/dist/client/lifecycle.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts +4 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +131 -0
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/runtime.d.ts +84 -0
- package/dist/client/runtime.d.ts.map +1 -1
- package/dist/client/runtime.js +234 -0
- package/dist/client/runtime.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +15 -15
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +20 -31
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +90 -300
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/service-proxy.d.ts +219 -0
- package/dist/service-proxy.d.ts.map +1 -0
- package/dist/service-proxy.js +1321 -0
- package/dist/service-proxy.js.map +1 -0
- package/dist/transport.d.ts +2 -0
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +34 -0
- package/dist/transport.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -81,6 +81,7 @@ const DEFAULT_SESSION_OPTIONS = {
|
|
|
81
81
|
const RECONNECT_MIN_BASE_DELAY_SECONDS = 1.0;
|
|
82
82
|
const RECONNECT_MAX_BASE_DELAY_SECONDS = 64.0;
|
|
83
83
|
const TOKEN_REFRESH_CHECK_INTERVAL_MS = 30_000;
|
|
84
|
+
const MAX_NOTIFY_PAYLOAD_SIZE = 64 * 1024;
|
|
84
85
|
// 心跳间隔下/上限(秒)。0 = 关闭心跳;负值视为 0;其余值 clamp 到 [10, 600]。
|
|
85
86
|
// 服务端通过 hello.heartbeat_interval 与 meta.ping pong 中的同名字段下发。
|
|
86
87
|
const HEARTBEAT_MIN_INTERVAL_SECONDS = 10;
|
|
@@ -102,7 +103,7 @@ const NON_IDEMPOTENT_METHODS = new Set([
|
|
|
102
103
|
'group.kick', 'group.remove_member', 'group.leave', 'group.dissolve',
|
|
103
104
|
'group.update_name', 'group.update_avatar', 'group.update_announcement',
|
|
104
105
|
'group.update_settings',
|
|
105
|
-
'storage.
|
|
106
|
+
'storage.create_upload_session', 'storage.complete_upload', 'storage.delete_object',
|
|
106
107
|
'auth.create_aid', 'auth.renew_cert', 'auth.rekey',
|
|
107
108
|
'message.thought.put', 'group.thought.put',
|
|
108
109
|
'group.add_member',
|
|
@@ -146,7 +147,6 @@ function reconnectSleepDelaySeconds(baseDelay, maxBaseDelay) {
|
|
|
146
147
|
}
|
|
147
148
|
/** 对端证书缓存 TTL(秒) */
|
|
148
149
|
const PEER_CERT_CACHE_TTL = 3600;
|
|
149
|
-
const PEER_PREKEYS_CACHE_TTL = 3600;
|
|
150
150
|
/**
|
|
151
151
|
* 将 WebSocket URL 转为对应的 HTTP URL
|
|
152
152
|
*/
|
|
@@ -508,6 +508,8 @@ export class AUNClient {
|
|
|
508
508
|
_pendingOrderedMsgs = new Map();
|
|
509
509
|
/** Lazy group sync:首次发送群消息前自动拉取历史 */
|
|
510
510
|
_groupSynced = new Set();
|
|
511
|
+
/** 群撤回去重:group_id|sorted(message_ids)|recalled_at -> 时间戳,保证应用层只回调一次 */
|
|
512
|
+
_groupRecallSeen = new Map();
|
|
511
513
|
/** 在线未读 hint 队列:同一 group 只保留最后一条,延迟 drain 降低登录瞬时拉取压力。 */
|
|
512
514
|
_onlineUnreadHintQueue = new Map();
|
|
513
515
|
_onlineUnreadHintTimer = null;
|
|
@@ -661,6 +663,10 @@ export class AUNClient {
|
|
|
661
663
|
this._dispatcher.subscribe('_raw.group.message_created', (data) => {
|
|
662
664
|
this._onRawGroupMessageCreated(data);
|
|
663
665
|
});
|
|
666
|
+
// 群组消息撤回推送:在线 push 通道,与 pull 双 tombstone 兜底互补(SDK 去重只回调一次)
|
|
667
|
+
this._dispatcher.subscribe('_raw.group.message_recalled', (data) => {
|
|
668
|
+
this._safeAsync(this._onRawGroupMessageRecalled(data));
|
|
669
|
+
});
|
|
664
670
|
// 群组变更事件:验签 + 透传 + gap 检测
|
|
665
671
|
this._dispatcher.subscribe('_raw.group.changed', (data) => {
|
|
666
672
|
this._onRawGroupChanged(data);
|
|
@@ -928,66 +934,11 @@ export class AUNClient {
|
|
|
928
934
|
}
|
|
929
935
|
/** 断开连接但保留本地状态,可再次 connect */
|
|
930
936
|
async disconnect() {
|
|
931
|
-
|
|
932
|
-
this._clientLog.debug(`disconnect enter: state=${this._state}`);
|
|
933
|
-
if (this._closing) {
|
|
934
|
-
this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms reason=closing`);
|
|
935
|
-
return;
|
|
936
|
-
}
|
|
937
|
-
if (![
|
|
938
|
-
ConnectionState.AUTHENTICATED,
|
|
939
|
-
ConnectionState.CONNECTING,
|
|
940
|
-
ConnectionState.READY,
|
|
941
|
-
ConnectionState.RETRY_BACKOFF,
|
|
942
|
-
ConnectionState.RECONNECTING,
|
|
943
|
-
ConnectionState.CONNECTION_FAILED,
|
|
944
|
-
].includes(this.state)) {
|
|
945
|
-
this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms reason=not_connected`);
|
|
946
|
-
return;
|
|
947
|
-
}
|
|
948
|
-
this._saveSeqTrackerState();
|
|
949
|
-
this._stopBackgroundTasks();
|
|
950
|
-
if (this._reconnectAbort) {
|
|
951
|
-
this._reconnectAbort.abort();
|
|
952
|
-
this._reconnectAbort = null;
|
|
953
|
-
this._reconnectActive = false;
|
|
954
|
-
}
|
|
955
|
-
await this._transport.close();
|
|
956
|
-
this._state = 'standby';
|
|
957
|
-
await this._dispatcher.publish('state_change', { state: this._publicState(this._state) });
|
|
958
|
-
this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms`);
|
|
937
|
+
return this._lifecycle.disconnect();
|
|
959
938
|
}
|
|
960
939
|
/** 关闭连接 */
|
|
961
940
|
async close() {
|
|
962
|
-
|
|
963
|
-
this._clientLog.debug(`close enter: state=${this._state}`);
|
|
964
|
-
this._closing = true;
|
|
965
|
-
this._saveSeqTrackerState();
|
|
966
|
-
this._stopBackgroundTasks();
|
|
967
|
-
// 取消进行中的重连
|
|
968
|
-
if (this._reconnectAbort) {
|
|
969
|
-
this._reconnectAbort.abort();
|
|
970
|
-
this._reconnectAbort = null;
|
|
971
|
-
this._reconnectActive = false;
|
|
972
|
-
}
|
|
973
|
-
if (this._state === 'idle' || this._state === 'closed') {
|
|
974
|
-
this._state = 'closed';
|
|
975
|
-
this._resetSeqTrackingState();
|
|
976
|
-
this._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms reason=already_idle`);
|
|
977
|
-
return;
|
|
978
|
-
}
|
|
979
|
-
// 关闭前通知服务端主动退出(best-effort,失败不阻塞)
|
|
980
|
-
try {
|
|
981
|
-
await this._transport.call('auth.logout', {});
|
|
982
|
-
}
|
|
983
|
-
catch {
|
|
984
|
-
// auth.logout 失败不影响关闭流程
|
|
985
|
-
}
|
|
986
|
-
await this._transport.close();
|
|
987
|
-
this._state = 'closed';
|
|
988
|
-
await this._dispatcher.publish('state_change', { state: this._publicState(this._state) });
|
|
989
|
-
this._resetSeqTrackingState();
|
|
990
|
-
this._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms`);
|
|
941
|
+
return this._lifecycle.close();
|
|
991
942
|
}
|
|
992
943
|
// ── RPC ───────────────────────────────────────────
|
|
993
944
|
/**
|
|
@@ -997,136 +948,91 @@ export class AUNClient {
|
|
|
997
948
|
* 自动解密 message.pull/group.pull、Group E2EE 生命周期编排。
|
|
998
949
|
*/
|
|
999
950
|
async call(method, params) {
|
|
1000
|
-
|
|
1001
|
-
this._clientLog.debug(`call enter: method=${method}`);
|
|
1002
|
-
try {
|
|
1003
|
-
const result = await this._callImpl(method, params);
|
|
1004
|
-
this._clientLog.debug(`call exit: elapsed=${Date.now() - tStart}ms method=${method}`);
|
|
1005
|
-
return result;
|
|
1006
|
-
}
|
|
1007
|
-
catch (err) {
|
|
1008
|
-
this._clientLog.debug(`call exit (error): elapsed=${Date.now() - tStart}ms method=${method} err=${err instanceof Error ? err.message : String(err)}`);
|
|
1009
|
-
throw err;
|
|
1010
|
-
}
|
|
951
|
+
return await this._rpcPipeline.call(method, params);
|
|
1011
952
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
await this._ensureV2SessionReady('message.send', 'V2 session not initialized; encrypted message.send requires V2 (V1 E2EE removed)');
|
|
1020
|
-
this._clientLog.debug('call route: message.send → V2 encrypted send');
|
|
1021
|
-
return await this._sendV2(String(p.to ?? ''), p.payload ?? {}, {
|
|
1022
|
-
messageId: String(p.message_id ?? '') || undefined,
|
|
1023
|
-
timestamp: p.timestamp,
|
|
1024
|
-
protectedHeaders: this._protectedHeadersFromParams(p),
|
|
1025
|
-
context: isJsonObject(p.context) ? p.context : undefined,
|
|
1026
|
-
});
|
|
1027
|
-
}
|
|
1028
|
-
// encrypt=false:明文走通用 RPC 路径;protected_headers/headers 是信封元数据,加密与否都保留
|
|
1029
|
-
this._maybeAppendEchoTraceSend(p);
|
|
1030
|
-
}
|
|
1031
|
-
// 自动加密:group.send 默认加密(encrypt 默认 true)— V2-only
|
|
1032
|
-
if (method === 'group.send') {
|
|
1033
|
-
const encrypt = p.encrypt !== undefined ? p.encrypt : true;
|
|
1034
|
-
delete p.encrypt;
|
|
1035
|
-
if (encrypt) {
|
|
1036
|
-
await this._ensureV2SessionReady('group.send', 'V2 session not initialized; encrypted group.send requires V2 (V1 E2EE removed)');
|
|
1037
|
-
this._clientLog.debug('call route: group.send → V2 encrypted send');
|
|
1038
|
-
return await this._sendGroupV2(String(p.group_id ?? ''), p.payload ?? {}, {
|
|
1039
|
-
messageId: String(p.message_id ?? '') || undefined,
|
|
1040
|
-
timestamp: p.timestamp,
|
|
1041
|
-
protectedHeaders: this._protectedHeadersFromParams(p),
|
|
1042
|
-
context: isJsonObject(p.context) ? p.context : undefined,
|
|
1043
|
-
});
|
|
1044
|
-
}
|
|
1045
|
-
this._maybeAppendEchoTraceSend(p);
|
|
1046
|
-
}
|
|
1047
|
-
if (method === 'group.thought.put') {
|
|
1048
|
-
const encrypt = p.encrypt !== undefined ? p.encrypt : true;
|
|
1049
|
-
delete p.encrypt;
|
|
1050
|
-
if (encrypt) {
|
|
1051
|
-
await this._ensureV2SessionReady('group.thought.put', 'V2 session not initialized; encrypted group.thought.put requires V2 (V1 E2EE removed)');
|
|
1052
|
-
this._clientLog.debug('call route: group.thought.put → V2 encrypted put');
|
|
1053
|
-
return this._putGroupThoughtEncryptedV2(p);
|
|
1054
|
-
}
|
|
953
|
+
static _notifyParamsSizeOk(params) {
|
|
954
|
+
return new TextEncoder().encode(JSON.stringify(params)).length <= MAX_NOTIFY_PAYLOAD_SIZE;
|
|
955
|
+
}
|
|
956
|
+
static _validateNotifyEventMethod(method) {
|
|
957
|
+
const normalized = String(method ?? '').trim();
|
|
958
|
+
if (!normalized.startsWith('event/app.') || normalized.length <= 'event/app.'.length) {
|
|
959
|
+
throw new ValidationError('routed notify method must be event/app.*');
|
|
1055
960
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
961
|
+
return normalized;
|
|
962
|
+
}
|
|
963
|
+
static _normalizeNotifyTtl(value) {
|
|
964
|
+
if (value === undefined || value === null)
|
|
965
|
+
return undefined;
|
|
966
|
+
const ttl = Number(value);
|
|
967
|
+
if (!Number.isInteger(ttl)) {
|
|
968
|
+
throw new ValidationError('ttl_ms must be an integer');
|
|
1064
969
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
if (pullGateKey) {
|
|
1068
|
-
return await this._runPullSerialized(pullGateKey, async () => {
|
|
1069
|
-
return await this._callImplInner(method, p);
|
|
1070
|
-
});
|
|
970
|
+
if (ttl < 0 || ttl > 60000) {
|
|
971
|
+
throw new ValidationError('ttl_ms must be between 0 and 60000');
|
|
1071
972
|
}
|
|
1072
|
-
return
|
|
973
|
+
return ttl;
|
|
1073
974
|
}
|
|
1074
975
|
/**
|
|
1075
|
-
*
|
|
1076
|
-
* 拆分出来以便 pull gate 包裹整个操作。
|
|
976
|
+
* 发送轻量在线通知,不走离线存储、seq/pull 或 ack。
|
|
1077
977
|
*/
|
|
1078
|
-
async
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
const
|
|
1099
|
-
const
|
|
1100
|
-
if (
|
|
1101
|
-
|
|
1102
|
-
if (
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
const
|
|
1115
|
-
|
|
1116
|
-
|
|
978
|
+
async notify(method, params, options = {}) {
|
|
979
|
+
if (params !== undefined && params !== null && !isJsonObject(params)) {
|
|
980
|
+
throw new ValidationError('notify params must be an object');
|
|
981
|
+
}
|
|
982
|
+
const payload = { ...(params ?? {}) };
|
|
983
|
+
if (!AUNClient._notifyParamsSizeOk(payload)) {
|
|
984
|
+
throw new ValidationError('notify payload is too large');
|
|
985
|
+
}
|
|
986
|
+
const targetAid = String(options.to ?? '').trim();
|
|
987
|
+
const targetGroupId = String(options.group_id ?? options.groupId ?? '').trim();
|
|
988
|
+
const targetDeviceId = String(options.device_id ?? options.deviceId ?? '').trim();
|
|
989
|
+
const targetSlotId = String(options.slot_id ?? options.slotId ?? '').trim();
|
|
990
|
+
const ttl = AUNClient._normalizeNotifyTtl(options.ttl_ms ?? options.ttlMs);
|
|
991
|
+
if (targetAid && targetGroupId) {
|
|
992
|
+
throw new ValidationError('notify() cannot set both to and group_id');
|
|
993
|
+
}
|
|
994
|
+
if (targetSlotId && !targetDeviceId) {
|
|
995
|
+
throw new ValidationError('slot_id requires device_id for notify target');
|
|
996
|
+
}
|
|
997
|
+
if (targetAid) {
|
|
998
|
+
const eventMethod = AUNClient._validateNotifyEventMethod(method);
|
|
999
|
+
const target = { type: 'aid', aid: targetAid };
|
|
1000
|
+
if (targetDeviceId)
|
|
1001
|
+
target.device_id = targetDeviceId;
|
|
1002
|
+
if (targetSlotId)
|
|
1003
|
+
target.slot_id = targetSlotId;
|
|
1004
|
+
const routeParams = {
|
|
1005
|
+
target,
|
|
1006
|
+
deliver: { method: eventMethod, params: payload },
|
|
1007
|
+
};
|
|
1008
|
+
if (ttl !== undefined)
|
|
1009
|
+
routeParams.ttl_ms = ttl;
|
|
1010
|
+
await this._transport.notify('notification/route', routeParams);
|
|
1011
|
+
return;
|
|
1012
|
+
}
|
|
1013
|
+
if (targetGroupId) {
|
|
1014
|
+
const eventMethod = AUNClient._validateNotifyEventMethod(method);
|
|
1015
|
+
const normalizedGroupId = normalizeGroupId(targetGroupId);
|
|
1016
|
+
if (!normalizedGroupId) {
|
|
1017
|
+
throw new ValidationError('group_id is required for group notify');
|
|
1117
1018
|
}
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1019
|
+
const routeParams = {
|
|
1020
|
+
group_id: normalizedGroupId,
|
|
1021
|
+
deliver: { method: eventMethod, params: payload },
|
|
1022
|
+
};
|
|
1023
|
+
if (ttl !== undefined)
|
|
1024
|
+
routeParams.ttl_ms = ttl;
|
|
1025
|
+
await this._transport.notify('notification/group.route', routeParams);
|
|
1026
|
+
return;
|
|
1027
|
+
}
|
|
1028
|
+
if (targetDeviceId || targetSlotId) {
|
|
1029
|
+
throw new ValidationError('device_id and slot_id require to');
|
|
1030
|
+
}
|
|
1031
|
+
const directMethod = String(method ?? '').trim();
|
|
1032
|
+
if (!directMethod.startsWith('notification/')) {
|
|
1033
|
+
throw new ValidationError('direct notify method must start with notification/');
|
|
1034
|
+
}
|
|
1035
|
+
await this._transport.notify(directMethod, payload);
|
|
1130
1036
|
}
|
|
1131
1037
|
async _callRawV2Rpc(method, params) {
|
|
1132
1038
|
const p = { ...(params ?? {}) };
|
|
@@ -1170,55 +1076,30 @@ export class AUNClient {
|
|
|
1170
1076
|
_onRawMessageReceived(data) {
|
|
1171
1077
|
this._delivery.onRawMessageReceived(data);
|
|
1172
1078
|
}
|
|
1173
|
-
/** 实际处理推送消息的异步任务(V2-only:明文消息直接透传,V2 加密消息走 _onV2PushNotification) */
|
|
1174
|
-
async _processAndPublishMessage(data) {
|
|
1175
|
-
return this._delivery.processAndPublishMessage(data);
|
|
1176
|
-
}
|
|
1177
1079
|
/** 处理群组消息推送:re-publish(V2 加密消息走 V2 push 路径) */
|
|
1178
1080
|
_onRawGroupMessageCreated(data) {
|
|
1179
1081
|
return this._delivery.onRawGroupMessageCreated(data);
|
|
1180
1082
|
}
|
|
1083
|
+
async _onRawGroupMessageRecalled(data) {
|
|
1084
|
+
return this._delivery.onRawGroupMessageRecalled(data);
|
|
1085
|
+
}
|
|
1181
1086
|
/** 处理 V2 群消息通知:主动 pull V2 envelope,由 pullGroupV2 解密并发布。 */
|
|
1182
1087
|
async _onRawGroupV2MessageCreated(data) {
|
|
1183
1088
|
return this._delivery.onRawGroupV2MessageCreated(data);
|
|
1184
1089
|
}
|
|
1185
|
-
/**
|
|
1186
|
-
* 处理群组推送消息的异步任务(V2-only:明文消息直接透传)。
|
|
1187
|
-
*
|
|
1188
|
-
* 带 payload 的事件(消息推送):直接 re-publish。
|
|
1189
|
-
* 不带 payload 的事件(通知):自动 pull 最新消息。
|
|
1190
|
-
*/
|
|
1191
|
-
async _processAndPublishGroupMessage(data) {
|
|
1192
|
-
return this._delivery.processAndPublishGroupMessage(data);
|
|
1193
|
-
}
|
|
1194
1090
|
async _publishEncryptedPushMessage(normalEvent, undecryptableEvent, ns, seq, msg, group) {
|
|
1195
1091
|
return await this._v2E2EE.publishEncryptedPushMessage(normalEvent, undecryptableEvent, ns, seq, msg, group);
|
|
1196
1092
|
}
|
|
1197
1093
|
async _decryptV2PushMessage(data) {
|
|
1198
1094
|
return await this._v2E2EE.decryptV2PushMessage(data);
|
|
1199
1095
|
}
|
|
1200
|
-
/** 后台补齐群消息空洞 */
|
|
1201
|
-
async _fillGroupGap(groupId) {
|
|
1202
|
-
return this._delivery.fillGroupGap(groupId);
|
|
1203
|
-
}
|
|
1204
|
-
/** 后台补齐群事件空洞 */
|
|
1205
|
-
async _fillGroupEventGap(groupId) {
|
|
1206
|
-
return this._delivery.fillGroupEventGap(groupId);
|
|
1207
|
-
}
|
|
1208
1096
|
/** 后台补齐 P2P 消息空洞 */
|
|
1209
1097
|
async _fillP2pGap() {
|
|
1210
1098
|
return this._delivery.fillP2pGap();
|
|
1211
1099
|
}
|
|
1212
|
-
/** 只按硬上限裁剪 published guard,不能按 contiguousSeq 清理。 */
|
|
1213
|
-
_prunePushedSeqs(ns) {
|
|
1214
|
-
this._delivery.prunePushedSeqs(ns);
|
|
1215
|
-
}
|
|
1216
1100
|
_markPublishedSeq(ns, seq) {
|
|
1217
1101
|
this._delivery.markPublishedSeq(ns, seq);
|
|
1218
1102
|
}
|
|
1219
|
-
_attachCurrentInstanceContext(payload) {
|
|
1220
|
-
return this._delivery.attachCurrentInstanceContext(payload);
|
|
1221
|
-
}
|
|
1222
1103
|
async _publishAppEvent(event, payload) {
|
|
1223
1104
|
await this._delivery.publishAppEvent(event, payload);
|
|
1224
1105
|
}
|
|
@@ -1261,9 +1142,6 @@ export class AUNClient {
|
|
|
1261
1142
|
const trace = `${this._echoTimestamp()} [AUN-SDK.receive] aid=${this._aid ?? '-'} conn_uptime=${uptime}s`;
|
|
1262
1143
|
msg.payload = { ...payload, text: payload.text + '\n' + trace };
|
|
1263
1144
|
}
|
|
1264
|
-
_messageTargetsCurrentInstance(message) {
|
|
1265
|
-
return this._delivery.messageTargetsCurrentInstance(message);
|
|
1266
|
-
}
|
|
1267
1145
|
async _drainOrderedMessages(ns, beforeSeq) {
|
|
1268
1146
|
await this._delivery.drainOrderedMessages(ns, beforeSeq);
|
|
1269
1147
|
}
|
|
@@ -1420,82 +1298,6 @@ export class AUNClient {
|
|
|
1420
1298
|
const digest = await crypto.subtle.digest('SHA-256', certBytes);
|
|
1421
1299
|
return 'sha256:' + Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
1422
1300
|
}
|
|
1423
|
-
/**
|
|
1424
|
-
* 从 X.509 DER 证书中提取 SubjectPublicKeyInfo 并计算其 SHA-256 指纹。
|
|
1425
|
-
* 返回 "sha256:<hex>",提取失败返回空串。
|
|
1426
|
-
* 用于 H7 指纹校验(DER 证书指纹 OR SPKI 指纹任一匹配)。
|
|
1427
|
-
*/
|
|
1428
|
-
async _spkiFingerprint(certPem) {
|
|
1429
|
-
try {
|
|
1430
|
-
const der = new Uint8Array(pemToArrayBuffer(certPem));
|
|
1431
|
-
// X.509: Certificate ::= SEQUENCE { tbsCertificate, signatureAlgorithm, signatureValue }
|
|
1432
|
-
// tbsCertificate ::= SEQUENCE { version?, serialNumber, signature, issuer, validity, subject, subjectPublicKeyInfo, ... }
|
|
1433
|
-
// 逐级解析 SEQUENCE 并定位 SPKI (第 7 个或第 6 个子元素,取决于 version 是否 [0] explicit)。
|
|
1434
|
-
const readLen = (buf, pos) => {
|
|
1435
|
-
const first = buf[pos];
|
|
1436
|
-
if (first < 0x80)
|
|
1437
|
-
return { len: first, next: pos + 1 };
|
|
1438
|
-
const n = first & 0x7f;
|
|
1439
|
-
let len = 0;
|
|
1440
|
-
for (let i = 0; i < n; i++)
|
|
1441
|
-
len = (len << 8) | buf[pos + 1 + i];
|
|
1442
|
-
return { len, next: pos + 1 + n };
|
|
1443
|
-
};
|
|
1444
|
-
// 外层 SEQUENCE
|
|
1445
|
-
if (der[0] !== 0x30)
|
|
1446
|
-
return '';
|
|
1447
|
-
const outer = readLen(der, 1);
|
|
1448
|
-
// tbsCertificate SEQUENCE 起点
|
|
1449
|
-
const tbsStart = outer.next;
|
|
1450
|
-
if (der[tbsStart] !== 0x30)
|
|
1451
|
-
return '';
|
|
1452
|
-
const tbsLen = readLen(der, tbsStart + 1);
|
|
1453
|
-
let p = tbsLen.next;
|
|
1454
|
-
const tbsEnd = tbsLen.next + tbsLen.len;
|
|
1455
|
-
// 跳过 [0] EXPLICIT Version(可选)
|
|
1456
|
-
if (der[p] === 0xa0) {
|
|
1457
|
-
const lv = readLen(der, p + 1);
|
|
1458
|
-
p = lv.next + lv.len;
|
|
1459
|
-
}
|
|
1460
|
-
// 跳过 serialNumber (INTEGER)
|
|
1461
|
-
if (der[p] !== 0x02)
|
|
1462
|
-
return '';
|
|
1463
|
-
let lv = readLen(der, p + 1);
|
|
1464
|
-
p = lv.next + lv.len;
|
|
1465
|
-
// 跳过 signature (SEQUENCE)
|
|
1466
|
-
if (der[p] !== 0x30)
|
|
1467
|
-
return '';
|
|
1468
|
-
lv = readLen(der, p + 1);
|
|
1469
|
-
p = lv.next + lv.len;
|
|
1470
|
-
// 跳过 issuer (SEQUENCE)
|
|
1471
|
-
if (der[p] !== 0x30)
|
|
1472
|
-
return '';
|
|
1473
|
-
lv = readLen(der, p + 1);
|
|
1474
|
-
p = lv.next + lv.len;
|
|
1475
|
-
// 跳过 validity (SEQUENCE)
|
|
1476
|
-
if (der[p] !== 0x30)
|
|
1477
|
-
return '';
|
|
1478
|
-
lv = readLen(der, p + 1);
|
|
1479
|
-
p = lv.next + lv.len;
|
|
1480
|
-
// 跳过 subject (SEQUENCE)
|
|
1481
|
-
if (der[p] !== 0x30)
|
|
1482
|
-
return '';
|
|
1483
|
-
lv = readLen(der, p + 1);
|
|
1484
|
-
p = lv.next + lv.len;
|
|
1485
|
-
// subjectPublicKeyInfo (SEQUENCE) — 连同 tag+length+value 全部即 SPKI DER
|
|
1486
|
-
if (der[p] !== 0x30 || p >= tbsEnd)
|
|
1487
|
-
return '';
|
|
1488
|
-
const spkiStart = p;
|
|
1489
|
-
const spkiLV = readLen(der, p + 1);
|
|
1490
|
-
const spkiEnd = spkiLV.next + spkiLV.len;
|
|
1491
|
-
const spkiDer = der.subarray(spkiStart, spkiEnd);
|
|
1492
|
-
const digest = await crypto.subtle.digest('SHA-256', spkiDer);
|
|
1493
|
-
return 'sha256:' + Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, '0')).join('');
|
|
1494
|
-
}
|
|
1495
|
-
catch {
|
|
1496
|
-
return '';
|
|
1497
|
-
}
|
|
1498
|
-
}
|
|
1499
1301
|
async _decryptGroupThoughts(result) {
|
|
1500
1302
|
return await this._v2E2EE.decryptGroupThoughts(result);
|
|
1501
1303
|
}
|
|
@@ -2046,12 +1848,6 @@ export class AUNClient {
|
|
|
2046
1848
|
_validateMessageRecipient(toAid) {
|
|
2047
1849
|
this._rpcPipeline.validateMessageRecipient(toAid);
|
|
2048
1850
|
}
|
|
2049
|
-
_validateOutboundCall(method, params) {
|
|
2050
|
-
this._rpcPipeline.validateOutboundCall(method, params);
|
|
2051
|
-
}
|
|
2052
|
-
_injectMessageCursorContext(method, params) {
|
|
2053
|
-
this._rpcPipeline.injectMessageCursorContext(method, params);
|
|
2054
|
-
}
|
|
2055
1851
|
// ── 内部:断线重连 ────────────────────────────────
|
|
2056
1852
|
/** 不重连 close code 集合:认证失败/权限错误/被踢等,重连无意义 */
|
|
2057
1853
|
static _NO_RECONNECT_CODES = new Set([4001, 4003, 4008, 4009, 4010, 4011, 4012, 4013, 4014, 4015]);
|
|
@@ -2454,9 +2250,6 @@ export class AUNClient {
|
|
|
2454
2250
|
_scheduleV2SenderIKPending(args) {
|
|
2455
2251
|
return this._v2E2EE.scheduleSenderIKPending(args);
|
|
2456
2252
|
}
|
|
2457
|
-
async _resolveV2SenderIKPending(fromAid, senderDeviceId, groupId, fetchKey) {
|
|
2458
|
-
return await this._v2E2EE.resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey);
|
|
2459
|
-
}
|
|
2460
2253
|
/**
|
|
2461
2254
|
* V2 P2P 加密发送(推测性:用缓存 bootstrap 直接发,失败刷新重试一次)。
|
|
2462
2255
|
*
|
|
@@ -2826,9 +2619,6 @@ export class AUNClient {
|
|
|
2826
2619
|
&& (!slotId || slotId === (this._slotId ?? ''));
|
|
2827
2620
|
}
|
|
2828
2621
|
// ── Pull Gate(序列化同一 key 的并发 pull)──────────────────
|
|
2829
|
-
_pullGateKeyForCall(method, params) {
|
|
2830
|
-
return this._rpcPipeline.pullGateKeyForCall(method, params);
|
|
2831
|
-
}
|
|
2832
2622
|
async _runPullSerialized(key, operation) {
|
|
2833
2623
|
return await this._rpcPipeline.runPullSerialized(key, operation);
|
|
2834
2624
|
}
|