@agentunion/fastaun-browser 0.5.2 → 0.5.4
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 +74 -0
- package/_packed_docs/CHANGELOG.md +74 -0
- package/_packed_docs/INDEX.md +16 -10
- package/_packed_docs/KITE_DOCS_GUIDE.md +4 -2
- package/_packed_docs/protocol/10-Group-/345/255/220/345/215/217/350/256/256.md +202 -75
- package/_packed_docs/protocol/README.md +3 -2
- package/_packed_docs/protocol/aun-docs-guide.md +3 -2
- package/_packed_docs/protocol/index.md +6 -5
- package/_packed_docs/sdk/03-/346/240/270/345/277/203/346/246/202/345/277/265.md +20 -1
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +303 -28
- package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +30 -6
- package/_packed_docs/sdk/08-/346/234/200/344/275/263/345/256/236/350/267/265.md +28 -12
- package/_packed_docs/sdk/09-group-rpc-manual.md +126 -42
- package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +6 -4
- package/_packed_docs/sdk/INDEX.md +17 -17
- package/dist/bundle.js +1084 -296
- package/dist/client/delivery.d.ts +4 -0
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +174 -7
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/v2-e2ee.js +2 -2
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +27 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +118 -12
- package/dist/client.js.map +1 -1
- package/dist/facades.d.ts +9 -0
- package/dist/facades.d.ts.map +1 -1
- package/dist/facades.js +314 -77
- package/dist/facades.js.map +1 -1
- package/dist/group-index.d.ts +105 -0
- package/dist/group-index.d.ts.map +1 -0
- package/dist/group-index.js +252 -0
- package/dist/group-index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/keystore/index.d.ts +15 -0
- package/dist/keystore/index.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.d.ts +7 -1
- package/dist/keystore/indexeddb-shared.d.ts.map +1 -1
- package/dist/keystore/indexeddb-shared.js +63 -2
- package/dist/keystore/indexeddb-shared.js.map +1 -1
- package/dist/keystore/indexeddb-token-store.d.ts +3 -1
- package/dist/keystore/indexeddb-token-store.d.ts.map +1 -1
- package/dist/keystore/indexeddb-token-store.js +22 -1
- package/dist/keystore/indexeddb-token-store.js.map +1 -1
- package/dist/tools/cross-sdk-agent.js +3 -1
- package/dist/tools/cross-sdk-agent.js.map +1 -1
- package/dist/transport.d.ts +2 -1
- package/dist/transport.d.ts.map +1 -1
- package/dist/transport.js +17 -31
- package/dist/transport.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/bundle.js
CHANGED
|
@@ -460,7 +460,7 @@ var init_indexeddb_store = __esm({
|
|
|
460
460
|
});
|
|
461
461
|
|
|
462
462
|
// src/version.ts
|
|
463
|
-
var VERSION = "0.5.
|
|
463
|
+
var VERSION = "0.5.4";
|
|
464
464
|
|
|
465
465
|
// src/types.ts
|
|
466
466
|
var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
|
|
@@ -1918,10 +1918,11 @@ var STORE_METADATA = "metadata";
|
|
|
1918
1918
|
var STORE_INSTANCE_STATE = "instance_state";
|
|
1919
1919
|
var STORE_GROUP_STATE = "group_state";
|
|
1920
1920
|
var STORE_AGENT_MD_CACHE = "agent_md_cache";
|
|
1921
|
+
var STORE_GROUP_INDEX_CACHE = "group_index_cache";
|
|
1921
1922
|
var STORE_PENDING_IDENTITIES = "pending_identities";
|
|
1922
1923
|
var STORE_PENDING_BINDS = "pending_binds";
|
|
1923
1924
|
var DB_NAME = "aun-keystore";
|
|
1924
|
-
var DB_VERSION =
|
|
1925
|
+
var DB_VERSION = 9;
|
|
1925
1926
|
function safeAid(aid) {
|
|
1926
1927
|
return aid.replace(/[/\\:]/g, "_");
|
|
1927
1928
|
}
|
|
@@ -1986,6 +1987,12 @@ function agentMdCachePrefix(ownerAid) {
|
|
|
1986
1987
|
function agentMdCacheStoreKey(ownerAid, targetAid) {
|
|
1987
1988
|
return `${agentMdCachePrefix(ownerAid)}${encodePart(targetAid)}`;
|
|
1988
1989
|
}
|
|
1990
|
+
function groupIndexCachePrefix(localAid) {
|
|
1991
|
+
return `${safeAid(localAid)}|`;
|
|
1992
|
+
}
|
|
1993
|
+
function groupIndexCacheStoreKey(localAid, groupAid) {
|
|
1994
|
+
return `${groupIndexCachePrefix(localAid)}${encodePart(groupAid)}`;
|
|
1995
|
+
}
|
|
1989
1996
|
function pendingIdentityPrefix(aid) {
|
|
1990
1997
|
return `${safeAid(aid)}-`;
|
|
1991
1998
|
}
|
|
@@ -2039,6 +2046,59 @@ function mergeAgentMdCacheRecord(aid, current, fields) {
|
|
|
2039
2046
|
out.updated_at = Date.now();
|
|
2040
2047
|
return out;
|
|
2041
2048
|
}
|
|
2049
|
+
function defaultGroupIndexCacheRecord(localAid, groupAid) {
|
|
2050
|
+
return {
|
|
2051
|
+
local_aid: localAid,
|
|
2052
|
+
group_aid: groupAid,
|
|
2053
|
+
index_jsonl: "",
|
|
2054
|
+
remote_meta: {},
|
|
2055
|
+
local_etag: "",
|
|
2056
|
+
settings: {},
|
|
2057
|
+
entry_etags: {},
|
|
2058
|
+
updated_at: 0
|
|
2059
|
+
};
|
|
2060
|
+
}
|
|
2061
|
+
function normalizeGroupIndexCacheRecord(localAid, groupAid, value) {
|
|
2062
|
+
if (!isRecord(value)) return null;
|
|
2063
|
+
const out = defaultGroupIndexCacheRecord(localAid, groupAid);
|
|
2064
|
+
out.local_aid = String(value.local_aid ?? localAid).trim() || localAid;
|
|
2065
|
+
out.group_aid = String(value.group_aid ?? groupAid).trim() || groupAid;
|
|
2066
|
+
out.index_jsonl = String(value.index_jsonl ?? "");
|
|
2067
|
+
out.local_etag = String(value.local_etag ?? "");
|
|
2068
|
+
out.remote_meta = isRecord(value.remote_meta) ? deepClone(value.remote_meta) : {};
|
|
2069
|
+
out.settings = isRecord(value.settings) ? deepClone(value.settings) : {};
|
|
2070
|
+
if (isRecord(value.entry_etags)) {
|
|
2071
|
+
out.entry_etags = Object.fromEntries(Object.entries(value.entry_etags).map(([key, etag]) => [key, String(etag ?? "")]));
|
|
2072
|
+
}
|
|
2073
|
+
const updatedAt = Number(value.updated_at ?? 0);
|
|
2074
|
+
out.updated_at = Number.isFinite(updatedAt) ? Math.trunc(updatedAt) : 0;
|
|
2075
|
+
return out;
|
|
2076
|
+
}
|
|
2077
|
+
function mergeGroupIndexCacheRecord(localAid, groupAid, current, fields) {
|
|
2078
|
+
const out = current ? deepClone(current) : defaultGroupIndexCacheRecord(localAid, groupAid);
|
|
2079
|
+
out.local_aid = localAid;
|
|
2080
|
+
out.group_aid = groupAid;
|
|
2081
|
+
if (Object.prototype.hasOwnProperty.call(fields, "index_jsonl") && fields.index_jsonl !== void 0 && fields.index_jsonl !== null) {
|
|
2082
|
+
out.index_jsonl = String(fields.index_jsonl ?? "");
|
|
2083
|
+
}
|
|
2084
|
+
if (Object.prototype.hasOwnProperty.call(fields, "local_etag") && fields.local_etag !== void 0 && fields.local_etag !== null) {
|
|
2085
|
+
out.local_etag = String(fields.local_etag ?? "");
|
|
2086
|
+
}
|
|
2087
|
+
if (isRecord(fields.remote_meta)) {
|
|
2088
|
+
out.remote_meta = { ...out.remote_meta, ...deepClone(fields.remote_meta) };
|
|
2089
|
+
}
|
|
2090
|
+
if (isRecord(fields.settings)) {
|
|
2091
|
+
out.settings = { ...out.settings, ...deepClone(fields.settings) };
|
|
2092
|
+
}
|
|
2093
|
+
if (isRecord(fields.entry_etags)) {
|
|
2094
|
+
out.entry_etags = {
|
|
2095
|
+
...out.entry_etags,
|
|
2096
|
+
...Object.fromEntries(Object.entries(fields.entry_etags).map(([key, etag]) => [key, String(etag ?? "")]))
|
|
2097
|
+
};
|
|
2098
|
+
}
|
|
2099
|
+
out.updated_at = Date.now();
|
|
2100
|
+
return out;
|
|
2101
|
+
}
|
|
2042
2102
|
var _ENC_ALGO = "AES-GCM";
|
|
2043
2103
|
var _PBKDF2_ITERATIONS = 1e5;
|
|
2044
2104
|
function _uint8ToBase64(bytes) {
|
|
@@ -2098,6 +2158,7 @@ function openDB() {
|
|
|
2098
2158
|
STORE_INSTANCE_STATE,
|
|
2099
2159
|
STORE_GROUP_STATE,
|
|
2100
2160
|
STORE_AGENT_MD_CACHE,
|
|
2161
|
+
STORE_GROUP_INDEX_CACHE,
|
|
2101
2162
|
STORE_PENDING_IDENTITIES,
|
|
2102
2163
|
STORE_PENDING_BINDS
|
|
2103
2164
|
]) {
|
|
@@ -2987,6 +3048,26 @@ var _IndexedDBTokenStore = class _IndexedDBTokenStore {
|
|
|
2987
3048
|
}
|
|
2988
3049
|
return [...aids].sort();
|
|
2989
3050
|
}
|
|
3051
|
+
// ── group.index Cache ───────────────────────────────────
|
|
3052
|
+
async loadGroupIndexCache(localAid, groupAid) {
|
|
3053
|
+
const local = String(localAid ?? "").trim();
|
|
3054
|
+
const group = String(groupAid ?? "").trim();
|
|
3055
|
+
if (!local || !group) return null;
|
|
3056
|
+
const data = await idbGet(STORE_GROUP_INDEX_CACHE, groupIndexCacheStoreKey(local, group));
|
|
3057
|
+
const record = normalizeGroupIndexCacheRecord(local, group, data);
|
|
3058
|
+
return record ? deepClone(record) : null;
|
|
3059
|
+
}
|
|
3060
|
+
async upsertGroupIndexCache(localAid, groupAid, fields) {
|
|
3061
|
+
const local = String(localAid ?? "").trim();
|
|
3062
|
+
const group = String(groupAid ?? "").trim();
|
|
3063
|
+
if (!local || !group) {
|
|
3064
|
+
throw new Error("upsertGroupIndexCache requires localAid and groupAid");
|
|
3065
|
+
}
|
|
3066
|
+
const current = await this.loadGroupIndexCache(local, group);
|
|
3067
|
+
const record = mergeGroupIndexCacheRecord(local, group, current, fields ?? {});
|
|
3068
|
+
await idbPut(STORE_GROUP_INDEX_CACHE, groupIndexCacheStoreKey(local, group), record);
|
|
3069
|
+
return deepClone(record);
|
|
3070
|
+
}
|
|
2990
3071
|
// ── Group State(群组状态快照) ─────────────────────────────
|
|
2991
3072
|
async saveGroupState(groupId, state) {
|
|
2992
3073
|
const key = encodePart(groupId);
|
|
@@ -6293,6 +6374,16 @@ var RPCTransport = class {
|
|
|
6293
6374
|
setMetaObserver(observer) {
|
|
6294
6375
|
this._metaObserver = observer;
|
|
6295
6376
|
}
|
|
6377
|
+
async _notifyMetaObserver(message) {
|
|
6378
|
+
if (this._metaObserver === null) return;
|
|
6379
|
+
const meta = message._meta;
|
|
6380
|
+
if (!isJsonObject(meta)) return;
|
|
6381
|
+
try {
|
|
6382
|
+
await this._metaObserver(meta);
|
|
6383
|
+
} catch (exc) {
|
|
6384
|
+
this._log.debug(`meta_observer raised: ${String(exc)}`);
|
|
6385
|
+
}
|
|
6386
|
+
}
|
|
6296
6387
|
/** 设置 trace 模式:off / log / diag */
|
|
6297
6388
|
setTraceMode(mode) {
|
|
6298
6389
|
if (mode !== "off" && mode !== "log" && mode !== "diag") {
|
|
@@ -6487,7 +6578,7 @@ var RPCTransport = class {
|
|
|
6487
6578
|
this._drainRpcQueue();
|
|
6488
6579
|
}, effectiveTimeout);
|
|
6489
6580
|
const pending = {
|
|
6490
|
-
resolve: (response) => {
|
|
6581
|
+
resolve: async (response) => {
|
|
6491
6582
|
clearTimeout(timer);
|
|
6492
6583
|
const elapsed = Date.now() - tStart;
|
|
6493
6584
|
if (response.error !== void 0) {
|
|
@@ -6505,16 +6596,7 @@ var RPCTransport = class {
|
|
|
6505
6596
|
if (traceId) {
|
|
6506
6597
|
this._log.info(`[trace=${traceId}] rpc_recv method=${method} rpc_id=${rpcId} duration_ms=${elapsed} status=ok`);
|
|
6507
6598
|
}
|
|
6508
|
-
|
|
6509
|
-
const meta = response._meta;
|
|
6510
|
-
if (isJsonObject(meta)) {
|
|
6511
|
-
try {
|
|
6512
|
-
this._metaObserver(meta);
|
|
6513
|
-
} catch (exc) {
|
|
6514
|
-
this._log.debug(`meta_observer raised: ${String(exc)}`);
|
|
6515
|
-
}
|
|
6516
|
-
}
|
|
6517
|
-
}
|
|
6599
|
+
await this._notifyMetaObserver(response);
|
|
6518
6600
|
const respTrace = response._trace;
|
|
6519
6601
|
if (respTrace && typeof respTrace === "object" && !Array.isArray(respTrace)) {
|
|
6520
6602
|
this._handleResponseTrace(method, "ok", elapsed, respTrace);
|
|
@@ -6796,14 +6878,7 @@ var RPCTransport = class {
|
|
|
6796
6878
|
const protocolEvent = method.slice(6);
|
|
6797
6879
|
const sdkEvent = EVENT_NAME_MAP[protocolEvent] ?? protocolEvent;
|
|
6798
6880
|
this._log.debug(`event recv: event=${sdkEvent} ${summarizeDict(message.params, DIAG_RESULT_FIELDS)}`);
|
|
6799
|
-
|
|
6800
|
-
if (this._metaObserver !== null && isJsonObject(meta2)) {
|
|
6801
|
-
try {
|
|
6802
|
-
this._metaObserver(meta2);
|
|
6803
|
-
} catch (exc) {
|
|
6804
|
-
this._log.debug(`event meta_observer raised: ${String(exc)}`);
|
|
6805
|
-
}
|
|
6806
|
-
}
|
|
6881
|
+
void this._notifyMetaObserver(message);
|
|
6807
6882
|
const params2 = message.params ?? {};
|
|
6808
6883
|
if ("_trace" in params2) {
|
|
6809
6884
|
const eventTrace = params2._trace;
|
|
@@ -6826,14 +6901,7 @@ var RPCTransport = class {
|
|
|
6826
6901
|
this._dispatcher.publish(`_raw.${sdkEvent}`, params2);
|
|
6827
6902
|
return;
|
|
6828
6903
|
}
|
|
6829
|
-
|
|
6830
|
-
if (this._metaObserver !== null && isJsonObject(meta)) {
|
|
6831
|
-
try {
|
|
6832
|
-
this._metaObserver(meta);
|
|
6833
|
-
} catch (exc) {
|
|
6834
|
-
this._log.debug(`notification meta_observer raised: ${String(exc)}`);
|
|
6835
|
-
}
|
|
6836
|
-
}
|
|
6904
|
+
void this._notifyMetaObserver(message);
|
|
6837
6905
|
this._log.debug(`notification recv: method=${method || "<no-method>"}`);
|
|
6838
6906
|
this._dispatcher.publish("notification", message);
|
|
6839
6907
|
}
|
|
@@ -7377,6 +7445,7 @@ var ClientRuntime = class {
|
|
|
7377
7445
|
// src/client/delivery.ts
|
|
7378
7446
|
var PUSHED_SEQS_LIMIT = 5e4;
|
|
7379
7447
|
var PENDING_ORDERED_LIMIT = 5e4;
|
|
7448
|
+
var MESSAGE_RECALL_SEEN_LIMIT = 1e4;
|
|
7380
7449
|
var GROUP_RECALL_SEEN_LIMIT = 1e4;
|
|
7381
7450
|
var SEQ_TRACKER_PERSIST_FLUSH_DELAY_MS = 200;
|
|
7382
7451
|
var P2P_GAP_FILL_RETRY_LIMIT = 12;
|
|
@@ -7402,6 +7471,22 @@ var APP_MESSAGE_ENVELOPE_KEYS = [
|
|
|
7402
7471
|
"headers",
|
|
7403
7472
|
"payload_type"
|
|
7404
7473
|
];
|
|
7474
|
+
var RECALL_PAYLOAD_KEYS = [
|
|
7475
|
+
"message_ids",
|
|
7476
|
+
"target_message_seqs",
|
|
7477
|
+
"recalled_message_id",
|
|
7478
|
+
"target_message_id",
|
|
7479
|
+
"original_message_id",
|
|
7480
|
+
"target_seq",
|
|
7481
|
+
"original_seq",
|
|
7482
|
+
"notice_message_id",
|
|
7483
|
+
"notice_seq",
|
|
7484
|
+
"event_seq",
|
|
7485
|
+
"sender_aid",
|
|
7486
|
+
"recalled_by",
|
|
7487
|
+
"recalled_at",
|
|
7488
|
+
"reason"
|
|
7489
|
+
];
|
|
7405
7490
|
var APP_SEND_ENVELOPE_METHODS = /* @__PURE__ */ new Set([
|
|
7406
7491
|
"message.send",
|
|
7407
7492
|
"group.send",
|
|
@@ -7524,6 +7609,10 @@ var MessageDeliveryEngine = class {
|
|
|
7524
7609
|
await this.publishOrderedGroupChanged(payload);
|
|
7525
7610
|
return;
|
|
7526
7611
|
}
|
|
7612
|
+
if (event === "message.recalled") {
|
|
7613
|
+
await this.publishMessageRecallTombstone(seq, payload);
|
|
7614
|
+
return;
|
|
7615
|
+
}
|
|
7527
7616
|
await client._publishAppEvent(event, payload);
|
|
7528
7617
|
}
|
|
7529
7618
|
async publishOrderedGroupChanged(payload) {
|
|
@@ -7704,6 +7793,11 @@ var MessageDeliveryEngine = class {
|
|
|
7704
7793
|
event[key] = msg[key];
|
|
7705
7794
|
}
|
|
7706
7795
|
}
|
|
7796
|
+
for (const key of RECALL_PAYLOAD_KEYS) {
|
|
7797
|
+
if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
|
|
7798
|
+
event[key] = msg[key];
|
|
7799
|
+
}
|
|
7800
|
+
}
|
|
7707
7801
|
const rawIds = event.message_ids;
|
|
7708
7802
|
let messageIds = Array.isArray(rawIds) ? rawIds.map((item) => String(item ?? "").trim()).filter(Boolean) : [];
|
|
7709
7803
|
if (messageIds.length === 0) {
|
|
@@ -7735,6 +7829,48 @@ var MessageDeliveryEngine = class {
|
|
|
7735
7829
|
if (recall) return { event: "message.recalled", payload: recall };
|
|
7736
7830
|
return { event: "message.received", payload: message };
|
|
7737
7831
|
}
|
|
7832
|
+
messageRecallDedupKey(payload) {
|
|
7833
|
+
const ids = payload.message_ids;
|
|
7834
|
+
const idPart = Array.isArray(ids) ? ids.map((i) => String(i ?? "").trim()).filter(Boolean).sort().join(",") : String(ids ?? "").trim();
|
|
7835
|
+
if (idPart) return `p2p|id:${idPart}`;
|
|
7836
|
+
for (const key of ["recalled_message_id", "target_message_id", "original_message_id"]) {
|
|
7837
|
+
const value = String(payload[key] ?? "").trim();
|
|
7838
|
+
if (value) return `p2p|id:${value}`;
|
|
7839
|
+
}
|
|
7840
|
+
const rawSeqs = payload.target_message_seqs;
|
|
7841
|
+
const seqPart = Array.isArray(rawSeqs) ? rawSeqs.map((seq) => String(seq ?? "").trim()).filter(Boolean).sort().join(",") : String(rawSeqs ?? payload.target_seq ?? payload.original_seq ?? "").trim();
|
|
7842
|
+
if (seqPart) {
|
|
7843
|
+
const fromAid = String(payload.from ?? payload.from_aid ?? payload.sender_aid ?? "").trim();
|
|
7844
|
+
const toAid = String(payload.to ?? payload.to_aid ?? "").trim();
|
|
7845
|
+
return `p2p|from:${fromAid}|to:${toAid}|seq:${seqPart}`;
|
|
7846
|
+
}
|
|
7847
|
+
const tombstoneId = String(payload.tombstone_message_id ?? payload.message_id ?? "").trim();
|
|
7848
|
+
if (tombstoneId) return `p2p|tombstone:${tombstoneId}`;
|
|
7849
|
+
return `p2p|unknown:${Date.now()}:${Math.random()}`;
|
|
7850
|
+
}
|
|
7851
|
+
async publishMessageRecallTombstone(seq, message) {
|
|
7852
|
+
const client = this.runtime.client;
|
|
7853
|
+
const eventPayload = this.recallEventFromMessage(message);
|
|
7854
|
+
if (!eventPayload) return false;
|
|
7855
|
+
const dedupKey = this.messageRecallDedupKey(eventPayload);
|
|
7856
|
+
let seen = client._messageRecallSeen;
|
|
7857
|
+
if (!seen) {
|
|
7858
|
+
seen = /* @__PURE__ */ new Map();
|
|
7859
|
+
client._messageRecallSeen = seen;
|
|
7860
|
+
}
|
|
7861
|
+
if (seen.has(dedupKey)) {
|
|
7862
|
+
client._clientLog.debug(`message.recalled dedup suppressed: seq=${String(seq)} key=${dedupKey}`);
|
|
7863
|
+
return false;
|
|
7864
|
+
}
|
|
7865
|
+
seen.set(dedupKey, Date.now());
|
|
7866
|
+
if (seen.size > MESSAGE_RECALL_SEEN_LIMIT) {
|
|
7867
|
+
const drop = [...seen.entries()].sort((a, b) => a[1] - b[1]).slice(0, seen.size - MESSAGE_RECALL_SEEN_LIMIT);
|
|
7868
|
+
for (const [oldKey] of drop) seen.delete(oldKey);
|
|
7869
|
+
}
|
|
7870
|
+
await client._publishAppEvent("message.recalled", eventPayload);
|
|
7871
|
+
client._clientLog.debug(`message.recalled published: seq=${String(seq)} ids=${JSON.stringify(eventPayload.message_ids)}`);
|
|
7872
|
+
return true;
|
|
7873
|
+
}
|
|
7738
7874
|
recallEventFromGroupMessage(message) {
|
|
7739
7875
|
if (!isJsonObject(message)) return null;
|
|
7740
7876
|
const msg = message;
|
|
@@ -7749,6 +7885,11 @@ var MessageDeliveryEngine = class {
|
|
|
7749
7885
|
event[key] = msg[key];
|
|
7750
7886
|
}
|
|
7751
7887
|
}
|
|
7888
|
+
for (const key of RECALL_PAYLOAD_KEYS) {
|
|
7889
|
+
if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
|
|
7890
|
+
event[key] = msg[key];
|
|
7891
|
+
}
|
|
7892
|
+
}
|
|
7752
7893
|
const rawIds = event.message_ids;
|
|
7753
7894
|
let messageIds = Array.isArray(rawIds) ? rawIds.map((item) => String(item ?? "").trim()).filter(Boolean) : [];
|
|
7754
7895
|
if (messageIds.length === 0) {
|
|
@@ -7773,15 +7914,29 @@ var MessageDeliveryEngine = class {
|
|
|
7773
7914
|
return event;
|
|
7774
7915
|
}
|
|
7775
7916
|
groupRecallDedupKey(groupId, payload) {
|
|
7917
|
+
const normalizedGroupId2 = normalizeGroupId(groupId) || String(groupId ?? "").trim();
|
|
7776
7918
|
const ids = payload.message_ids;
|
|
7777
|
-
const idPart = Array.isArray(ids) ? ids.map((i) => String(i ?? "").trim()).filter(Boolean).sort().join(",") : String(ids ?? "");
|
|
7778
|
-
return `${
|
|
7919
|
+
const idPart = Array.isArray(ids) ? ids.map((i) => String(i ?? "").trim()).filter(Boolean).sort().join(",") : String(ids ?? "").trim();
|
|
7920
|
+
if (idPart) return `${normalizedGroupId2}|id:${idPart}`;
|
|
7921
|
+
for (const key of ["recalled_message_id", "target_message_id", "original_message_id"]) {
|
|
7922
|
+
const value = String(payload[key] ?? "").trim();
|
|
7923
|
+
if (value) return `${normalizedGroupId2}|id:${value}`;
|
|
7924
|
+
}
|
|
7925
|
+
const rawSeqs = payload.target_message_seqs;
|
|
7926
|
+
const seqPart = Array.isArray(rawSeqs) ? rawSeqs.map((seq) => String(seq ?? "").trim()).filter(Boolean).sort().join(",") : String(rawSeqs ?? payload.original_seq ?? "").trim();
|
|
7927
|
+
if (seqPart) return `${normalizedGroupId2}|seq:${seqPart}`;
|
|
7928
|
+
const tombstoneId = String(payload.tombstone_message_id ?? payload.message_id ?? "").trim();
|
|
7929
|
+
if (tombstoneId) return `${normalizedGroupId2}|tombstone:${tombstoneId}`;
|
|
7930
|
+
return `${normalizedGroupId2}|unknown:${Date.now()}:${Math.random()}`;
|
|
7779
7931
|
}
|
|
7780
7932
|
async publishGroupRecallTombstone(groupId, seq, message) {
|
|
7781
7933
|
const client = this.runtime.client;
|
|
7782
7934
|
const eventPayload = this.recallEventFromGroupMessage(message);
|
|
7783
7935
|
if (!eventPayload) return false;
|
|
7784
|
-
const
|
|
7936
|
+
const rawGroupId = String(eventPayload.group_id ?? groupId ?? "").trim();
|
|
7937
|
+
const dedupGroupId = normalizeGroupId(rawGroupId) || rawGroupId;
|
|
7938
|
+
if (dedupGroupId) eventPayload.group_id = dedupGroupId;
|
|
7939
|
+
const dedupKey = this.groupRecallDedupKey(dedupGroupId, eventPayload);
|
|
7785
7940
|
let seen = client._groupRecallSeen;
|
|
7786
7941
|
if (!seen) {
|
|
7787
7942
|
seen = /* @__PURE__ */ new Map();
|
|
@@ -7898,12 +8053,68 @@ var MessageDeliveryEngine = class {
|
|
|
7898
8053
|
}
|
|
7899
8054
|
return true;
|
|
7900
8055
|
}
|
|
8056
|
+
async runPushProcessSerialized(ns, operation) {
|
|
8057
|
+
const client = this.runtime.client;
|
|
8058
|
+
if (!ns) {
|
|
8059
|
+
await operation();
|
|
8060
|
+
return;
|
|
8061
|
+
}
|
|
8062
|
+
let queues = client._pushProcessQueues;
|
|
8063
|
+
if (!queues) {
|
|
8064
|
+
queues = /* @__PURE__ */ new Map();
|
|
8065
|
+
client._pushProcessQueues = queues;
|
|
8066
|
+
}
|
|
8067
|
+
const previous = queues.get(ns) ?? Promise.resolve();
|
|
8068
|
+
const current = previous.catch(() => void 0).then(operation);
|
|
8069
|
+
const stored = current.then(() => void 0, () => void 0);
|
|
8070
|
+
queues.set(ns, stored);
|
|
8071
|
+
try {
|
|
8072
|
+
await current;
|
|
8073
|
+
} finally {
|
|
8074
|
+
if (queues.get(ns) === stored) queues.delete(ns);
|
|
8075
|
+
}
|
|
8076
|
+
}
|
|
7901
8077
|
onRawMessageReceived(data) {
|
|
7902
8078
|
const client = this.runtime.client;
|
|
7903
8079
|
client._clientLog.debug(`_onRawMessageReceived enter: from=${data?.from ?? "-"} mid=${data?.message_id ?? "-"} seq=${data?.seq ?? "-"}`);
|
|
7904
|
-
client.
|
|
8080
|
+
const ns = isJsonObject(data) && client._aid && data.seq !== void 0 ? `p2p:${client._aid}` : "";
|
|
8081
|
+
client._safeAsync(this.runPushProcessSerialized(ns, () => this.processAndPublishMessage(data)));
|
|
7905
8082
|
client._clientLog.debug("_onRawMessageReceived exit: elapsed=0ms (dispatched async)");
|
|
7906
8083
|
}
|
|
8084
|
+
async onRawMessageRecalled(data) {
|
|
8085
|
+
const client = this.runtime.client;
|
|
8086
|
+
if (!isJsonObject(data)) return;
|
|
8087
|
+
const msg = { ...data };
|
|
8088
|
+
if (!("type" in msg)) msg.type = "message.recalled";
|
|
8089
|
+
if (!this.messageTargetsCurrentInstance(msg)) return;
|
|
8090
|
+
const seq = msg.seq;
|
|
8091
|
+
if (seq !== void 0 && seq !== null && client._aid) {
|
|
8092
|
+
const ns = `p2p:${client._aid}`;
|
|
8093
|
+
if (seq > 0) client._seqTracker.updateMaxSeen(ns, seq);
|
|
8094
|
+
const contigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
8095
|
+
const seqNeedsPull = client._seqTracker.onMessageSeq(ns, seq);
|
|
8096
|
+
const published = await this.publishOrderedMessage("message.recalled", ns, seq, msg);
|
|
8097
|
+
const contigAfter = client._seqTracker.getContiguousSeq(ns);
|
|
8098
|
+
if (seqNeedsPull && !published) {
|
|
8099
|
+
client._safeAsync(this.fillP2pGap());
|
|
8100
|
+
}
|
|
8101
|
+
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
8102
|
+
if (contig > 0) {
|
|
8103
|
+
const ackSeq = this.clampAckSeq("message.ack", "seq", ns, contig);
|
|
8104
|
+
client._transport.call("message.ack", {
|
|
8105
|
+
seq: ackSeq,
|
|
8106
|
+
device_id: client._deviceId,
|
|
8107
|
+
slot_id: client._slotId,
|
|
8108
|
+
_rpc_background: true
|
|
8109
|
+
}).catch((e) => {
|
|
8110
|
+
client._clientLog.warn(`P2P recall auto-ack failed:${String(e)}`);
|
|
8111
|
+
});
|
|
8112
|
+
}
|
|
8113
|
+
if (contigAfter !== contigBefore) this.persistSeq(ns);
|
|
8114
|
+
return;
|
|
8115
|
+
}
|
|
8116
|
+
await this.publishMessageRecallTombstone(seq, msg);
|
|
8117
|
+
}
|
|
7907
8118
|
async processAndPublishMessage(data) {
|
|
7908
8119
|
const client = this.runtime.client;
|
|
7909
8120
|
try {
|
|
@@ -7969,7 +8180,8 @@ var MessageDeliveryEngine = class {
|
|
|
7969
8180
|
onRawGroupMessageCreated(data) {
|
|
7970
8181
|
const client = this.runtime.client;
|
|
7971
8182
|
client._clientLog.debug(`_onRawGroupMessageCreated enter: group_id=${data?.group_id ?? "-"} from=${data?.from ?? "-"} seq=${data?.seq ?? "-"}`);
|
|
7972
|
-
|
|
8183
|
+
const groupId = isJsonObject(data) ? String(data.group_id ?? "").trim() : "";
|
|
8184
|
+
client._safeAsync(this.runPushProcessSerialized(groupId ? `group:${groupId}` : "", () => this.processAndPublishGroupMessage(data)));
|
|
7973
8185
|
client._clientLog.debug("_onRawGroupMessageCreated exit: elapsed=0ms (dispatched async)");
|
|
7974
8186
|
}
|
|
7975
8187
|
async processAndPublishGroupMessage(data) {
|
|
@@ -8926,6 +9138,9 @@ var MessageDeliveryEngine = class {
|
|
|
8926
9138
|
const client = this.runtime.client;
|
|
8927
9139
|
const seqNum = Number(seq);
|
|
8928
9140
|
if (!Number.isFinite(seqNum) || !Number.isInteger(seqNum) || seqNum <= 0 || !ns) {
|
|
9141
|
+
if (event === "message.recalled") {
|
|
9142
|
+
return this.publishMessageRecallTombstone(seq, payload);
|
|
9143
|
+
}
|
|
8929
9144
|
await client._publishAppEvent(event, payload);
|
|
8930
9145
|
return true;
|
|
8931
9146
|
}
|
|
@@ -8935,8 +9150,14 @@ var MessageDeliveryEngine = class {
|
|
|
8935
9150
|
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
8936
9151
|
return false;
|
|
8937
9152
|
}
|
|
9153
|
+
await this.drainOrderedMessages(ns, seqNum);
|
|
8938
9154
|
queue?.delete(seqNum);
|
|
8939
9155
|
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
9156
|
+
if (event === "message.recalled") {
|
|
9157
|
+
const published = await this.publishMessageRecallTombstone(seqNum, payload);
|
|
9158
|
+
this.markPublishedSeq(ns, seqNum);
|
|
9159
|
+
return published;
|
|
9160
|
+
}
|
|
8940
9161
|
await client._publishAppEvent(event, payload);
|
|
8941
9162
|
this.markPublishedSeq(ns, seqNum);
|
|
8942
9163
|
return true;
|
|
@@ -11098,7 +11319,360 @@ var GroupFSVFS = class {
|
|
|
11098
11319
|
}
|
|
11099
11320
|
};
|
|
11100
11321
|
|
|
11322
|
+
// src/v2/crypto/canonical.ts
|
|
11323
|
+
var encoder = new TextEncoder();
|
|
11324
|
+
var MAX_SAFE_JSON_INTEGER = 9007199254740991;
|
|
11325
|
+
function canonicalJson(obj) {
|
|
11326
|
+
return encoder.encode(serialize(obj));
|
|
11327
|
+
}
|
|
11328
|
+
function serialize(obj) {
|
|
11329
|
+
if (obj === null) return "null";
|
|
11330
|
+
if (obj === true) return "true";
|
|
11331
|
+
if (obj === false) return "false";
|
|
11332
|
+
if (typeof obj === "number") {
|
|
11333
|
+
return serializeNumber(obj);
|
|
11334
|
+
}
|
|
11335
|
+
if (typeof obj === "string") {
|
|
11336
|
+
return serializeString(obj);
|
|
11337
|
+
}
|
|
11338
|
+
if (Array.isArray(obj)) {
|
|
11339
|
+
return serializeArray(obj);
|
|
11340
|
+
}
|
|
11341
|
+
if (typeof obj === "object") {
|
|
11342
|
+
return serializeObject(obj);
|
|
11343
|
+
}
|
|
11344
|
+
throw new TypeError(`canonicalJson: unsupported type ${typeof obj}`);
|
|
11345
|
+
}
|
|
11346
|
+
function serializeNumber(n) {
|
|
11347
|
+
if (!isFinite(n)) {
|
|
11348
|
+
throw new RangeError("canonicalJson: Infinity and NaN not allowed");
|
|
11349
|
+
}
|
|
11350
|
+
if (Object.is(n, -0)) return "0";
|
|
11351
|
+
if (Number.isInteger(n)) {
|
|
11352
|
+
if (Math.abs(n) > MAX_SAFE_JSON_INTEGER) {
|
|
11353
|
+
throw new RangeError(`canonicalJson: integer outside safe range ${n}`);
|
|
11354
|
+
}
|
|
11355
|
+
return String(n);
|
|
11356
|
+
}
|
|
11357
|
+
return expandExponent(String(n));
|
|
11358
|
+
}
|
|
11359
|
+
function expandExponent(s) {
|
|
11360
|
+
if (!/[eE]/.test(s)) return s;
|
|
11361
|
+
const match = /^(-?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
|
|
11362
|
+
if (!match) {
|
|
11363
|
+
throw new TypeError(`canonicalJson: invalid number ${s}`);
|
|
11364
|
+
}
|
|
11365
|
+
const sign = match[1] ?? "";
|
|
11366
|
+
const intPart = match[2] ?? "";
|
|
11367
|
+
const fracPart = match[3] ?? "";
|
|
11368
|
+
const exp = Number(match[4]);
|
|
11369
|
+
const digits = intPart + fracPart;
|
|
11370
|
+
const point = intPart.length + exp;
|
|
11371
|
+
if (point <= 0) {
|
|
11372
|
+
return `${sign}0.${"0".repeat(-point)}${digits}`;
|
|
11373
|
+
}
|
|
11374
|
+
if (point >= digits.length) {
|
|
11375
|
+
return `${sign}${digits}${"0".repeat(point - digits.length)}`;
|
|
11376
|
+
}
|
|
11377
|
+
return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
11378
|
+
}
|
|
11379
|
+
function serializeString(s) {
|
|
11380
|
+
let result = '"';
|
|
11381
|
+
for (let i = 0; i < s.length; i++) {
|
|
11382
|
+
const ch = s[i];
|
|
11383
|
+
const code = s.charCodeAt(i);
|
|
11384
|
+
if (ch === '"') {
|
|
11385
|
+
result += '\\"';
|
|
11386
|
+
} else if (ch === "\\") {
|
|
11387
|
+
result += "\\\\";
|
|
11388
|
+
} else if (ch === "\b") {
|
|
11389
|
+
result += "\\b";
|
|
11390
|
+
} else if (ch === "\f") {
|
|
11391
|
+
result += "\\f";
|
|
11392
|
+
} else if (ch === "\n") {
|
|
11393
|
+
result += "\\n";
|
|
11394
|
+
} else if (ch === "\r") {
|
|
11395
|
+
result += "\\r";
|
|
11396
|
+
} else if (ch === " ") {
|
|
11397
|
+
result += "\\t";
|
|
11398
|
+
} else if (code < 32) {
|
|
11399
|
+
result += "\\u" + code.toString(16).padStart(4, "0");
|
|
11400
|
+
} else {
|
|
11401
|
+
result += ch;
|
|
11402
|
+
}
|
|
11403
|
+
}
|
|
11404
|
+
result += '"';
|
|
11405
|
+
return result;
|
|
11406
|
+
}
|
|
11407
|
+
function serializeArray(arr) {
|
|
11408
|
+
const items = arr.map((item) => serialize(item));
|
|
11409
|
+
return "[" + items.join(",") + "]";
|
|
11410
|
+
}
|
|
11411
|
+
function serializeObject(obj) {
|
|
11412
|
+
const sortedKeys = Object.keys(obj).sort(compareCodePoints);
|
|
11413
|
+
const pairs = sortedKeys.map(
|
|
11414
|
+
(key) => serializeString(key) + ":" + serialize(obj[key])
|
|
11415
|
+
);
|
|
11416
|
+
return "{" + pairs.join(",") + "}";
|
|
11417
|
+
}
|
|
11418
|
+
function compareCodePoints(a, b) {
|
|
11419
|
+
const ac = Array.from(a);
|
|
11420
|
+
const bc = Array.from(b);
|
|
11421
|
+
const n = Math.min(ac.length, bc.length);
|
|
11422
|
+
for (let i = 0; i < n; i++) {
|
|
11423
|
+
const av = ac[i].codePointAt(0) ?? 0;
|
|
11424
|
+
const bv = bc[i].codePointAt(0) ?? 0;
|
|
11425
|
+
if (av !== bv) return av - bv;
|
|
11426
|
+
}
|
|
11427
|
+
return ac.length - bc.length;
|
|
11428
|
+
}
|
|
11429
|
+
|
|
11430
|
+
// src/group-index.ts
|
|
11431
|
+
var GROUP_INDEX_SCHEMA = "aun.group.index.v1";
|
|
11432
|
+
var GROUP_INDEX_KEY = "group.index";
|
|
11433
|
+
var GROUP_INDEX_SIG_ALG = "ECDSA-P256-SHA256";
|
|
11434
|
+
var GroupIndexMetaCache = class {
|
|
11435
|
+
constructor() {
|
|
11436
|
+
__publicField(this, "remote", /* @__PURE__ */ new Map());
|
|
11437
|
+
__publicField(this, "localEtags", /* @__PURE__ */ new Map());
|
|
11438
|
+
__publicField(this, "stale", /* @__PURE__ */ new Set());
|
|
11439
|
+
__publicField(this, "settings", /* @__PURE__ */ new Map());
|
|
11440
|
+
__publicField(this, "entryEtags", /* @__PURE__ */ new Map());
|
|
11441
|
+
}
|
|
11442
|
+
observeRpcMeta(meta, options) {
|
|
11443
|
+
const groupIndexes = isRecord4(meta?.group_indexes) ? meta.group_indexes : null;
|
|
11444
|
+
if (!groupIndexes) return;
|
|
11445
|
+
const local = String(options.localAid ?? "");
|
|
11446
|
+
for (const [groupAid, value] of Object.entries(groupIndexes)) {
|
|
11447
|
+
if (!isRecord4(value)) continue;
|
|
11448
|
+
const key = this.key(local, groupAid);
|
|
11449
|
+
const remoteMeta = {};
|
|
11450
|
+
for (const name of ["etag", "last_modified", "schema"]) {
|
|
11451
|
+
if (value[name] !== void 0 && value[name] !== null) remoteMeta[name] = value[name];
|
|
11452
|
+
}
|
|
11453
|
+
this.remote.set(key, remoteMeta);
|
|
11454
|
+
const remoteEtag = String(remoteMeta.etag ?? "");
|
|
11455
|
+
if (remoteEtag && this.localEtags.get(key) !== remoteEtag) this.stale.add(key);
|
|
11456
|
+
}
|
|
11457
|
+
}
|
|
11458
|
+
markFresh(localAid, groupAid, options) {
|
|
11459
|
+
const key = this.key(localAid, groupAid);
|
|
11460
|
+
this.localEtags.set(key, String(options.etag ?? ""));
|
|
11461
|
+
this.stale.delete(key);
|
|
11462
|
+
}
|
|
11463
|
+
isStale(localAid, groupAid) {
|
|
11464
|
+
return this.stale.has(this.key(localAid, groupAid));
|
|
11465
|
+
}
|
|
11466
|
+
remoteMeta(localAid, groupAid) {
|
|
11467
|
+
const value = this.remote.get(this.key(localAid, groupAid));
|
|
11468
|
+
return value ? { ...value } : null;
|
|
11469
|
+
}
|
|
11470
|
+
localEtag(localAid, groupAid) {
|
|
11471
|
+
return this.localEtags.get(this.key(localAid, groupAid)) ?? "";
|
|
11472
|
+
}
|
|
11473
|
+
cachedSettings(localAid, groupAid, keys) {
|
|
11474
|
+
const value = this.settings.get(this.key(localAid, groupAid)) ?? {};
|
|
11475
|
+
if (!keys.every((item) => item in value)) return null;
|
|
11476
|
+
return Object.fromEntries(keys.map((item) => [item, value[item]]));
|
|
11477
|
+
}
|
|
11478
|
+
cachedSettingsByEntries(localAid, groupAid, keys, entries) {
|
|
11479
|
+
const key = this.key(localAid, groupAid);
|
|
11480
|
+
const value = this.settings.get(key) ?? {};
|
|
11481
|
+
const localEntryEtags = this.entryEtags.get(key) ?? {};
|
|
11482
|
+
const remoteEntryEtags = Object.fromEntries(entries.map((item) => [String(item.key ?? ""), String(item.etag ?? "")]));
|
|
11483
|
+
const cached = {};
|
|
11484
|
+
const missing = [];
|
|
11485
|
+
for (const item of keys) {
|
|
11486
|
+
if (item in value && localEntryEtags[item] === remoteEntryEtags[item]) {
|
|
11487
|
+
cached[item] = value[item];
|
|
11488
|
+
} else {
|
|
11489
|
+
missing.push(item);
|
|
11490
|
+
}
|
|
11491
|
+
}
|
|
11492
|
+
return { cached, missing };
|
|
11493
|
+
}
|
|
11494
|
+
cacheSettings(localAid, groupAid, settings, options) {
|
|
11495
|
+
const key = this.key(localAid, groupAid);
|
|
11496
|
+
this.settings.set(key, { ...this.settings.get(key) ?? {}, ...settings });
|
|
11497
|
+
if (options?.entries) {
|
|
11498
|
+
const nextEntryEtags = { ...this.entryEtags.get(key) ?? {} };
|
|
11499
|
+
for (const item of options.entries) {
|
|
11500
|
+
const entryKey = String(item.key ?? "");
|
|
11501
|
+
if (entryKey) nextEntryEtags[entryKey] = String(item.etag ?? "");
|
|
11502
|
+
}
|
|
11503
|
+
this.entryEtags.set(key, nextEntryEtags);
|
|
11504
|
+
}
|
|
11505
|
+
if (options?.etag) this.markFresh(localAid, groupAid, { etag: options.etag });
|
|
11506
|
+
}
|
|
11507
|
+
restore(localAid, groupAid, record) {
|
|
11508
|
+
const key = this.key(localAid, groupAid);
|
|
11509
|
+
if (isRecord4(record.remote_meta)) this.remote.set(key, { ...record.remote_meta });
|
|
11510
|
+
if (record.local_etag !== void 0 && record.local_etag !== null) {
|
|
11511
|
+
this.localEtags.set(key, String(record.local_etag ?? ""));
|
|
11512
|
+
}
|
|
11513
|
+
if (isRecord4(record.settings)) this.settings.set(key, { ...this.settings.get(key) ?? {}, ...record.settings });
|
|
11514
|
+
if (isRecord4(record.entry_etags)) {
|
|
11515
|
+
this.entryEtags.set(key, {
|
|
11516
|
+
...this.entryEtags.get(key) ?? {},
|
|
11517
|
+
...Object.fromEntries(Object.entries(record.entry_etags).map(([entryKey, entryEtag]) => [entryKey, String(entryEtag ?? "")]))
|
|
11518
|
+
});
|
|
11519
|
+
}
|
|
11520
|
+
const remoteEtag = String(this.remote.get(key)?.etag ?? "");
|
|
11521
|
+
if (remoteEtag && this.localEtags.get(key) !== remoteEtag) this.stale.add(key);
|
|
11522
|
+
if (remoteEtag && this.localEtags.get(key) === remoteEtag) this.stale.delete(key);
|
|
11523
|
+
}
|
|
11524
|
+
key(localAid, groupAid) {
|
|
11525
|
+
return `${String(localAid ?? "")}\0${String(groupAid ?? "")}`;
|
|
11526
|
+
}
|
|
11527
|
+
};
|
|
11528
|
+
async function computeGroupIndexBodyHash(entries) {
|
|
11529
|
+
return `sha256:${await sha256Hex2(entriesBytes(entries))}`;
|
|
11530
|
+
}
|
|
11531
|
+
async function groupIndexEtag(entries) {
|
|
11532
|
+
return `"sha256:${await sha256Hex2(entriesBytes(entries))}"`;
|
|
11533
|
+
}
|
|
11534
|
+
function groupIndexSigningPayload(meta, entries) {
|
|
11535
|
+
const metaWithoutSignature = { ...meta };
|
|
11536
|
+
delete metaWithoutSignature.signature;
|
|
11537
|
+
const lines = [canonicalStringify(metaWithoutSignature)];
|
|
11538
|
+
lines.push(...canonicalEntries(entries).map((item) => canonicalStringify(item)));
|
|
11539
|
+
return encode(`${lines.join("\n")}
|
|
11540
|
+
`);
|
|
11541
|
+
}
|
|
11542
|
+
async function buildSignedGroupIndex(options) {
|
|
11543
|
+
const entries = canonicalEntries(options.entries);
|
|
11544
|
+
const meta = {
|
|
11545
|
+
type: "index_meta",
|
|
11546
|
+
group_aid: String(options.groupAid),
|
|
11547
|
+
etag: await groupIndexEtag(entries),
|
|
11548
|
+
last_modified: Math.trunc(Number(options.lastModified)),
|
|
11549
|
+
schema: String(options.schema ?? GROUP_INDEX_SCHEMA),
|
|
11550
|
+
body_hash: await computeGroupIndexBodyHash(entries),
|
|
11551
|
+
signed_by: options.signer.aid,
|
|
11552
|
+
sig_alg: GROUP_INDEX_SIG_ALG
|
|
11553
|
+
};
|
|
11554
|
+
const signed = await options.signer.sign(groupIndexSigningPayload(meta, entries));
|
|
11555
|
+
if (!signed.ok) throw new Error(signed.error.message || "group index signing failed");
|
|
11556
|
+
meta.signature = signed.data.signature;
|
|
11557
|
+
const body = [canonicalStringify(meta), ...entries.map((item) => canonicalStringify(item))].join("\n") + "\n";
|
|
11558
|
+
return { body, meta, entries };
|
|
11559
|
+
}
|
|
11560
|
+
function parseGroupIndex(body) {
|
|
11561
|
+
const text3 = typeof body === "string" ? body : String(body?.body ?? "");
|
|
11562
|
+
const lines = text3.split(/\r?\n/).filter((line) => line.trim());
|
|
11563
|
+
if (lines.length === 0) throw new Error("group index body is empty");
|
|
11564
|
+
const meta = JSON.parse(lines[0]);
|
|
11565
|
+
const entries = lines.slice(1).map((line) => JSON.parse(line));
|
|
11566
|
+
if (meta.type !== "index_meta") throw new Error("first group index line must be index_meta");
|
|
11567
|
+
return { meta, entries };
|
|
11568
|
+
}
|
|
11569
|
+
async function verifyGroupIndex(body, signer) {
|
|
11570
|
+
try {
|
|
11571
|
+
const parsed = parseGroupIndex(body);
|
|
11572
|
+
const signature = String(parsed.meta.signature ?? "");
|
|
11573
|
+
if (!signature) return resultOk({ valid: false, reason: "signature missing" });
|
|
11574
|
+
if (String(parsed.meta.signed_by ?? "") !== signer.aid) return resultOk({ valid: false, reason: "signed_by mismatch" });
|
|
11575
|
+
if (String(parsed.meta.sig_alg ?? "") !== GROUP_INDEX_SIG_ALG) return resultOk({ valid: false, reason: "unsupported sig_alg" });
|
|
11576
|
+
if (String(parsed.meta.body_hash ?? "") !== await computeGroupIndexBodyHash(parsed.entries)) {
|
|
11577
|
+
return resultOk({ valid: false, reason: "body_hash mismatch" });
|
|
11578
|
+
}
|
|
11579
|
+
if (String(parsed.meta.etag ?? "") !== await groupIndexEtag(parsed.entries)) {
|
|
11580
|
+
return resultOk({ valid: false, reason: "etag mismatch" });
|
|
11581
|
+
}
|
|
11582
|
+
const verified = await signer.verify(groupIndexSigningPayload(parsed.meta, parsed.entries), signature);
|
|
11583
|
+
if (!verified.ok) return resultErr(verified.error.code, verified.error.message || "group index verify failed", verified.error.cause);
|
|
11584
|
+
if (!verified.data.valid) return resultOk({ valid: false, reason: "signature verification failed" });
|
|
11585
|
+
return resultOk({ valid: true, meta: parsed.meta, entries: canonicalEntries(parsed.entries) });
|
|
11586
|
+
} catch (exc) {
|
|
11587
|
+
return resultErr("GROUP_INDEX_VERIFY_ERROR", String(exc), exc);
|
|
11588
|
+
}
|
|
11589
|
+
}
|
|
11590
|
+
async function prepareGroupSettingsWithIndex(options) {
|
|
11591
|
+
const result = { ...options.settings };
|
|
11592
|
+
const updatedEntries = [];
|
|
11593
|
+
for (const [key, value] of Object.entries(options.settings)) {
|
|
11594
|
+
if (key !== GROUP_INDEX_KEY) updatedEntries.push(await settingEntry(key, value, options.lastModified));
|
|
11595
|
+
}
|
|
11596
|
+
const updatedKeys = new Set(updatedEntries.map((item) => item.key));
|
|
11597
|
+
const entries = [];
|
|
11598
|
+
if (options.baseIndex) {
|
|
11599
|
+
const parsed = parseGroupIndex(options.baseIndex);
|
|
11600
|
+
entries.push(...parsed.entries.filter((item) => !updatedKeys.has(String(item.key))).map((item) => ({ ...item })));
|
|
11601
|
+
}
|
|
11602
|
+
entries.push(...updatedEntries);
|
|
11603
|
+
result[GROUP_INDEX_KEY] = await buildSignedGroupIndex({
|
|
11604
|
+
groupAid: options.groupAid,
|
|
11605
|
+
entries,
|
|
11606
|
+
signer: options.signer,
|
|
11607
|
+
lastModified: options.lastModified
|
|
11608
|
+
});
|
|
11609
|
+
return result;
|
|
11610
|
+
}
|
|
11611
|
+
async function settingEntry(key, value, lastModified) {
|
|
11612
|
+
const digest = await sha256Hex2(encode(canonicalStringify(value)));
|
|
11613
|
+
return {
|
|
11614
|
+
key,
|
|
11615
|
+
source: "db",
|
|
11616
|
+
etag: `"sha256:${digest}"`,
|
|
11617
|
+
last_modified: Math.trunc(Number(lastModified))
|
|
11618
|
+
};
|
|
11619
|
+
}
|
|
11620
|
+
function canonicalEntries(entries) {
|
|
11621
|
+
return entries.map((item) => ({ ...item })).sort((a, b) => compareCodePoints2(String(a.key ?? ""), String(b.key ?? "")));
|
|
11622
|
+
}
|
|
11623
|
+
function entriesBytes(entries) {
|
|
11624
|
+
const lines = canonicalEntries(entries).map((item) => canonicalStringify(item));
|
|
11625
|
+
return encode(lines.length ? `${lines.join("\n")}
|
|
11626
|
+
` : "");
|
|
11627
|
+
}
|
|
11628
|
+
function canonicalStringify(value) {
|
|
11629
|
+
return new TextDecoder().decode(canonicalJson(value));
|
|
11630
|
+
}
|
|
11631
|
+
function encode(text3) {
|
|
11632
|
+
return new TextEncoder().encode(text3);
|
|
11633
|
+
}
|
|
11634
|
+
async function sha256Hex2(data) {
|
|
11635
|
+
const digest = await globalThis.crypto.subtle.digest("SHA-256", exactArrayBuffer(data));
|
|
11636
|
+
return bytesToHex(new Uint8Array(digest));
|
|
11637
|
+
}
|
|
11638
|
+
function exactArrayBuffer(data) {
|
|
11639
|
+
if (data.byteOffset === 0 && data.byteLength === data.buffer.byteLength && data.buffer instanceof ArrayBuffer) {
|
|
11640
|
+
return data.buffer;
|
|
11641
|
+
}
|
|
11642
|
+
return data.slice().buffer;
|
|
11643
|
+
}
|
|
11644
|
+
function bytesToHex(bytes) {
|
|
11645
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
11646
|
+
}
|
|
11647
|
+
function compareCodePoints2(a, b) {
|
|
11648
|
+
const ac = Array.from(a);
|
|
11649
|
+
const bc = Array.from(b);
|
|
11650
|
+
const n = Math.min(ac.length, bc.length);
|
|
11651
|
+
for (let i = 0; i < n; i++) {
|
|
11652
|
+
const av = ac[i].codePointAt(0) ?? 0;
|
|
11653
|
+
const bv = bc[i].codePointAt(0) ?? 0;
|
|
11654
|
+
if (av !== bv) return av - bv;
|
|
11655
|
+
}
|
|
11656
|
+
return ac.length - bc.length;
|
|
11657
|
+
}
|
|
11658
|
+
function isRecord4(value) {
|
|
11659
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
11660
|
+
}
|
|
11661
|
+
|
|
11101
11662
|
// src/facades.ts
|
|
11663
|
+
var INDEXED_DOCUMENT_SETTING_KEY_NAME_RE = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
11664
|
+
var INDEXED_DOCUMENT_SETTING_RESERVED_BASES = /* @__PURE__ */ new Set([
|
|
11665
|
+
"join",
|
|
11666
|
+
"dispatch_mode",
|
|
11667
|
+
"duty",
|
|
11668
|
+
"e2ee",
|
|
11669
|
+
"group",
|
|
11670
|
+
"group_index",
|
|
11671
|
+
"index",
|
|
11672
|
+
"name",
|
|
11673
|
+
"description",
|
|
11674
|
+
"visibility"
|
|
11675
|
+
]);
|
|
11102
11676
|
function stripNil2(params2) {
|
|
11103
11677
|
const out = {};
|
|
11104
11678
|
for (const [key, value] of Object.entries(params2 ?? {})) {
|
|
@@ -11120,6 +11694,21 @@ function settingsToMap(result) {
|
|
|
11120
11694
|
}
|
|
11121
11695
|
return settings;
|
|
11122
11696
|
}
|
|
11697
|
+
function indexUpdateParams(groupId, settings, merged) {
|
|
11698
|
+
const out = { group_id: groupId, settings };
|
|
11699
|
+
for (const key of ["signer", "last_modified", "max_attempts"]) {
|
|
11700
|
+
if (key in merged) out[key] = merged[key];
|
|
11701
|
+
}
|
|
11702
|
+
return out;
|
|
11703
|
+
}
|
|
11704
|
+
function indexedDocumentKeyName(params2) {
|
|
11705
|
+
const raw = "keyName" in params2 ? params2.keyName : params2.key_name;
|
|
11706
|
+
const keyName = String(raw ?? "").trim();
|
|
11707
|
+
if (!keyName || INDEXED_DOCUMENT_SETTING_RESERVED_BASES.has(keyName.toLowerCase()) || !INDEXED_DOCUMENT_SETTING_KEY_NAME_RE.test(keyName)) {
|
|
11708
|
+
throw new Error("keyName must match ^[A-Za-z][A-Za-z0-9_-]{0,63}$");
|
|
11709
|
+
}
|
|
11710
|
+
return keyName;
|
|
11711
|
+
}
|
|
11123
11712
|
var RpcFacade = class {
|
|
11124
11713
|
constructor(client) {
|
|
11125
11714
|
__publicField(this, "client");
|
|
@@ -11280,17 +11869,185 @@ var GroupFacade = class extends RpcFacade {
|
|
|
11280
11869
|
listInviteCodes(params2) {
|
|
11281
11870
|
return this.call("group.list_invite_codes", params2);
|
|
11282
11871
|
}
|
|
11283
|
-
useInviteCode(params2) {
|
|
11284
|
-
return this.call("group.use_invite_code", params2);
|
|
11872
|
+
useInviteCode(params2) {
|
|
11873
|
+
return this.call("group.use_invite_code", params2);
|
|
11874
|
+
}
|
|
11875
|
+
revokeInviteCode(params2) {
|
|
11876
|
+
return this.call("group.revoke_invite_code", params2);
|
|
11877
|
+
}
|
|
11878
|
+
setSettings(params2) {
|
|
11879
|
+
return this.call("group.set_settings", params2);
|
|
11880
|
+
}
|
|
11881
|
+
getSettings(params2) {
|
|
11882
|
+
return this.call("group.get_settings", params2);
|
|
11883
|
+
}
|
|
11884
|
+
async checkGroupIndex(params2) {
|
|
11885
|
+
const merged = stripNil2(params2);
|
|
11886
|
+
const groupAid = String(merged.group_aid ?? merged.group_id ?? "").trim();
|
|
11887
|
+
if (!groupAid) throw new Error("group_aid is required");
|
|
11888
|
+
const clientAny = this.client;
|
|
11889
|
+
const stale = typeof clientAny.isGroupIndexStale === "function" ? Boolean(clientAny.isGroupIndexStale(groupAid)) : false;
|
|
11890
|
+
const remoteMeta = typeof clientAny.getGroupIndexRemoteMeta === "function" ? clientAny.getGroupIndexRemoteMeta(groupAid) ?? {} : {};
|
|
11891
|
+
const localEtag = typeof clientAny.getGroupIndexLocalEtag === "function" ? String(clientAny.getGroupIndexLocalEtag(groupAid) ?? "") : "";
|
|
11892
|
+
const remoteEtag = String(remoteMeta?.etag ?? "");
|
|
11893
|
+
const localFound = Boolean(localEtag);
|
|
11894
|
+
const remoteFound = Boolean(remoteEtag);
|
|
11895
|
+
const inSync = localFound && remoteFound && localEtag === remoteEtag;
|
|
11896
|
+
return {
|
|
11897
|
+
group_aid: groupAid,
|
|
11898
|
+
local_found: localFound,
|
|
11899
|
+
remote_found: remoteFound,
|
|
11900
|
+
local_etag: localEtag,
|
|
11901
|
+
remote_etag: remoteEtag,
|
|
11902
|
+
in_sync: inSync,
|
|
11903
|
+
needs_update: Boolean(stale || remoteFound && !inSync),
|
|
11904
|
+
last_modified: remoteMeta?.last_modified,
|
|
11905
|
+
status: remoteFound ? 200 : 404,
|
|
11906
|
+
cached: true
|
|
11907
|
+
};
|
|
11908
|
+
}
|
|
11909
|
+
async getGroupIndex(params2) {
|
|
11910
|
+
const merged = stripNil2(params2);
|
|
11911
|
+
const groupId = String(merged.group_id ?? "").trim();
|
|
11912
|
+
if (!groupId) throw new Error("group_id is required");
|
|
11913
|
+
const result = await this.getSettings({ group_id: groupId, keys: [GROUP_INDEX_KEY] });
|
|
11914
|
+
const groupAid = String(result.group_aid ?? groupId);
|
|
11915
|
+
let groupIndex = null;
|
|
11916
|
+
for (const item of result.settings ?? []) {
|
|
11917
|
+
if (item?.key !== GROUP_INDEX_KEY) continue;
|
|
11918
|
+
groupIndex = item.value;
|
|
11919
|
+
break;
|
|
11920
|
+
}
|
|
11921
|
+
if (!groupIndex) {
|
|
11922
|
+
return { group_id: result.group_id, group_aid: groupAid, group_index: null, meta: {}, entries: [] };
|
|
11923
|
+
}
|
|
11924
|
+
const parsed = parseGroupIndex(groupIndex);
|
|
11925
|
+
await this.verifyPulledGroupIndex(groupIndex, parsed);
|
|
11926
|
+
const etag = String(parsed.meta.etag ?? "");
|
|
11927
|
+
const settings = await this.hydrateGroupIndexSettings(groupId, groupAid, parsed.entries, etag, groupIndex);
|
|
11928
|
+
const clientAny = this.client;
|
|
11929
|
+
if (etag && typeof clientAny.markGroupIndexFresh === "function") {
|
|
11930
|
+
clientAny.markGroupIndexFresh(groupAid, { etag });
|
|
11931
|
+
}
|
|
11932
|
+
return {
|
|
11933
|
+
group_id: result.group_id,
|
|
11934
|
+
group_aid: groupAid,
|
|
11935
|
+
group_index: groupIndex,
|
|
11936
|
+
meta: parsed.meta,
|
|
11937
|
+
entries: parsed.entries,
|
|
11938
|
+
settings
|
|
11939
|
+
};
|
|
11285
11940
|
}
|
|
11286
|
-
|
|
11287
|
-
|
|
11941
|
+
async verifyPulledGroupIndex(groupIndex, parsed) {
|
|
11942
|
+
const signedBy = String(parsed.meta?.signed_by ?? "").trim();
|
|
11943
|
+
if (!signedBy) throw new Error("group.index signed_by is required");
|
|
11944
|
+
const clientAny = this.client;
|
|
11945
|
+
let signer = clientAny.currentAid?.aid === signedBy ? clientAny.currentAid : null;
|
|
11946
|
+
if (!signer && typeof clientAny.lookupPeer === "function") {
|
|
11947
|
+
signer = await clientAny.lookupPeer(signedBy);
|
|
11948
|
+
}
|
|
11949
|
+
if (!signer) throw new Error(`group.index signer is unavailable: ${signedBy}`);
|
|
11950
|
+
const verified = await verifyGroupIndex(groupIndex, signer);
|
|
11951
|
+
if (!verified.ok) throw new Error(verified.error.message || "group.index verification failed");
|
|
11952
|
+
if (!verified.data.valid) throw new Error(`group.index verification failed: ${verified.data.reason || "invalid signature"}`);
|
|
11953
|
+
}
|
|
11954
|
+
async updateGroupIndex(params2) {
|
|
11955
|
+
const merged = stripNil2(params2);
|
|
11956
|
+
const groupId = String(merged.group_id ?? "").trim();
|
|
11957
|
+
const settings = merged.settings;
|
|
11958
|
+
if (!groupId) throw new Error("group_id is required");
|
|
11959
|
+
if (!settings || typeof settings !== "object" || Array.isArray(settings) || Object.keys(settings).length === 0) {
|
|
11960
|
+
throw new Error("settings must be a non-empty object");
|
|
11961
|
+
}
|
|
11962
|
+
const signer = merged.signer ?? this.client.currentAid;
|
|
11963
|
+
if (!signer) throw new Error("signer is required");
|
|
11964
|
+
const lastModified = Math.trunc(Number(merged.last_modified ?? Date.now()));
|
|
11965
|
+
const maxAttempts = Math.max(1, Math.trunc(Number(merged.max_attempts ?? 2)));
|
|
11966
|
+
let lastError = null;
|
|
11967
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
11968
|
+
const current = await this.getSettings({ group_id: groupId, keys: [GROUP_INDEX_KEY] });
|
|
11969
|
+
const groupAid = String(current.group_aid ?? groupId);
|
|
11970
|
+
let currentIndex = null;
|
|
11971
|
+
let expectedEtag = "";
|
|
11972
|
+
for (const item of current.settings ?? []) {
|
|
11973
|
+
if (item?.key !== GROUP_INDEX_KEY) continue;
|
|
11974
|
+
currentIndex = item.value;
|
|
11975
|
+
if (currentIndex) expectedEtag = String(parseGroupIndex(currentIndex).meta.etag ?? "");
|
|
11976
|
+
break;
|
|
11977
|
+
}
|
|
11978
|
+
const signedSettings = await prepareGroupSettingsWithIndex({
|
|
11979
|
+
groupAid,
|
|
11980
|
+
settings,
|
|
11981
|
+
signer,
|
|
11982
|
+
lastModified,
|
|
11983
|
+
baseIndex: currentIndex
|
|
11984
|
+
});
|
|
11985
|
+
try {
|
|
11986
|
+
const result = await this.setSettings({
|
|
11987
|
+
group_id: groupId,
|
|
11988
|
+
settings: signedSettings,
|
|
11989
|
+
expected_index_etag: expectedEtag
|
|
11990
|
+
});
|
|
11991
|
+
const pushedEtag = String(parseGroupIndex(signedSettings[GROUP_INDEX_KEY]).meta.etag ?? "");
|
|
11992
|
+
const clientAny = this.client;
|
|
11993
|
+
if (pushedEtag && typeof clientAny.markGroupIndexFresh === "function") {
|
|
11994
|
+
clientAny.markGroupIndexFresh(groupAid, { etag: pushedEtag });
|
|
11995
|
+
}
|
|
11996
|
+
if (typeof clientAny.cacheGroupIndexSettings === "function") {
|
|
11997
|
+
const parsed = parseGroupIndex(signedSettings[GROUP_INDEX_KEY]);
|
|
11998
|
+
await clientAny.cacheGroupIndexSettings(groupAid, settings, {
|
|
11999
|
+
entries: parsed.entries,
|
|
12000
|
+
etag: pushedEtag,
|
|
12001
|
+
groupIndex: signedSettings[GROUP_INDEX_KEY]
|
|
12002
|
+
});
|
|
12003
|
+
}
|
|
12004
|
+
return result;
|
|
12005
|
+
} catch (exc) {
|
|
12006
|
+
if (!String(exc?.message ?? exc).includes("etag conflict")) throw exc;
|
|
12007
|
+
lastError = exc;
|
|
12008
|
+
}
|
|
12009
|
+
}
|
|
12010
|
+
if (lastError) throw lastError;
|
|
12011
|
+
throw new Error("updateGroupIndex failed");
|
|
11288
12012
|
}
|
|
11289
|
-
|
|
11290
|
-
|
|
12013
|
+
async getIndexedSettings(groupId, keys) {
|
|
12014
|
+
const clientAny = this.client;
|
|
12015
|
+
if (typeof clientAny.getGroupIndexCachedSettings === "function") {
|
|
12016
|
+
const cached = await clientAny.getGroupIndexCachedSettings(groupId, keys);
|
|
12017
|
+
if (cached && typeof cached === "object") return { groupId, settings: cached };
|
|
12018
|
+
}
|
|
12019
|
+
const result = await this.getSettings({ group_id: groupId, keys });
|
|
12020
|
+
const resultMap = result;
|
|
12021
|
+
const settings = settingsToMap(result);
|
|
12022
|
+
if (typeof clientAny.cacheGroupIndexSettings === "function") {
|
|
12023
|
+
const groupAid = String(resultMap.group_aid ?? groupId);
|
|
12024
|
+
await clientAny.cacheGroupIndexSettings(groupAid, settings);
|
|
12025
|
+
if (groupAid !== groupId) {
|
|
12026
|
+
await clientAny.cacheGroupIndexSettings(groupId, settings);
|
|
12027
|
+
}
|
|
12028
|
+
}
|
|
12029
|
+
return { groupId: String(resultMap.group_id ?? groupId), settings };
|
|
11291
12030
|
}
|
|
11292
|
-
|
|
11293
|
-
|
|
12031
|
+
async hydrateGroupIndexSettings(groupId, groupAid, entries, etag, groupIndex) {
|
|
12032
|
+
const keys = entries.filter((item) => String(item.source ?? "db") === "db" && String(item.key ?? "")).map((item) => String(item.key));
|
|
12033
|
+
if (keys.length === 0) return {};
|
|
12034
|
+
const clientAny = this.client;
|
|
12035
|
+
let cached = {};
|
|
12036
|
+
let missing = [...keys];
|
|
12037
|
+
if (typeof clientAny.getGroupIndexCachedSettingsByEntries === "function") {
|
|
12038
|
+
const value = await clientAny.getGroupIndexCachedSettingsByEntries(groupAid, keys, entries);
|
|
12039
|
+
cached = { ...value?.cached ?? {} };
|
|
12040
|
+
missing = [...value?.missing ?? []].map((item) => String(item));
|
|
12041
|
+
}
|
|
12042
|
+
let fetched = {};
|
|
12043
|
+
if (missing.length > 0) {
|
|
12044
|
+
fetched = settingsToMap(await this.getSettings({ group_id: groupId, keys: missing }));
|
|
12045
|
+
}
|
|
12046
|
+
const settings = { ...cached, ...fetched };
|
|
12047
|
+
if (typeof clientAny.cacheGroupIndexSettings === "function") {
|
|
12048
|
+
await clientAny.cacheGroupIndexSettings(groupAid, settings, { entries, etag, groupIndex });
|
|
12049
|
+
}
|
|
12050
|
+
return settings;
|
|
11294
12051
|
}
|
|
11295
12052
|
send(params2) {
|
|
11296
12053
|
validateGroupIDFormat(params2?.group_id, "group_id");
|
|
@@ -11312,91 +12069,115 @@ var GroupFacade = class extends RpcFacade {
|
|
|
11312
12069
|
ackEvents(params2) {
|
|
11313
12070
|
return this.call("group.ack_events", params2);
|
|
11314
12071
|
}
|
|
11315
|
-
|
|
12072
|
+
documentSettingResult(groupId, keyName, settings) {
|
|
12073
|
+
const contentKey = `${keyName}.content`;
|
|
12074
|
+
const attachmentsKey = `${keyName}.attachments`;
|
|
12075
|
+
return {
|
|
12076
|
+
group_id: groupId,
|
|
12077
|
+
setting: {
|
|
12078
|
+
group_id: groupId,
|
|
12079
|
+
key_name: keyName,
|
|
12080
|
+
content: settings[contentKey] ?? "",
|
|
12081
|
+
attachments: settings[attachmentsKey] ?? [],
|
|
12082
|
+
updated_by: settings[`${contentKey}.updated_by`] ?? "",
|
|
12083
|
+
updated_at: settings[`${contentKey}.updated_at`] ?? 0
|
|
12084
|
+
}
|
|
12085
|
+
};
|
|
12086
|
+
}
|
|
12087
|
+
async getSettingWithIndex(params2) {
|
|
11316
12088
|
const merged = params2 || {};
|
|
11317
12089
|
const groupId = merged.group_id;
|
|
11318
12090
|
if (!groupId) throw new Error("group_id is required");
|
|
11319
|
-
const
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
12091
|
+
const keyName = indexedDocumentKeyName(merged);
|
|
12092
|
+
const { groupId: resultGroupId, settings } = await this.getIndexedSettings(
|
|
12093
|
+
String(groupId),
|
|
12094
|
+
[`${keyName}.content`, `${keyName}.attachments`]
|
|
12095
|
+
);
|
|
12096
|
+
return this.documentSettingResult(resultGroupId, keyName, settings);
|
|
12097
|
+
}
|
|
12098
|
+
async updateSettingWithIndex(params2) {
|
|
12099
|
+
const merged = params2 || {};
|
|
12100
|
+
const groupId = merged.group_id;
|
|
12101
|
+
if (!groupId) throw new Error("group_id is required");
|
|
12102
|
+
const keyName = indexedDocumentKeyName(merged);
|
|
12103
|
+
if (!("content" in merged)) throw new Error("content is required");
|
|
12104
|
+
const content = merged.content;
|
|
12105
|
+
const attachments = merged.attachments ?? [];
|
|
12106
|
+
const settingsUpdate = { [`${keyName}.content`]: content };
|
|
12107
|
+
if ("attachments" in merged) {
|
|
12108
|
+
settingsUpdate[`${keyName}.attachments`] = attachments;
|
|
12109
|
+
}
|
|
12110
|
+
const result = await this.updateGroupIndex(indexUpdateParams(groupId, settingsUpdate, merged));
|
|
12111
|
+
const resultGroupId = String(result.group_id ?? groupId);
|
|
11324
12112
|
return {
|
|
11325
|
-
group_id:
|
|
12113
|
+
group_id: resultGroupId,
|
|
12114
|
+
setting: {
|
|
12115
|
+
group_id: resultGroupId,
|
|
12116
|
+
key_name: keyName,
|
|
12117
|
+
content,
|
|
12118
|
+
attachments,
|
|
12119
|
+
updated_by: "",
|
|
12120
|
+
updated_at: 0
|
|
12121
|
+
}
|
|
12122
|
+
};
|
|
12123
|
+
}
|
|
12124
|
+
async getAnnouncement(params2) {
|
|
12125
|
+
const merged = params2 || {};
|
|
12126
|
+
const result = await this.getSettingWithIndex({ ...merged, keyName: "announcement" });
|
|
12127
|
+
const resultMap = result;
|
|
12128
|
+
const setting = resultMap.setting;
|
|
12129
|
+
return {
|
|
12130
|
+
group_id: resultMap.group_id,
|
|
11326
12131
|
announcement: {
|
|
11327
|
-
group_id:
|
|
11328
|
-
content:
|
|
11329
|
-
attachments:
|
|
11330
|
-
updated_by:
|
|
11331
|
-
updated_at:
|
|
12132
|
+
group_id: setting.group_id,
|
|
12133
|
+
content: setting.content,
|
|
12134
|
+
attachments: setting.attachments,
|
|
12135
|
+
updated_by: setting.updated_by,
|
|
12136
|
+
updated_at: setting.updated_at
|
|
11332
12137
|
}
|
|
11333
12138
|
};
|
|
11334
12139
|
}
|
|
11335
12140
|
async updateAnnouncement(params2) {
|
|
11336
12141
|
const merged = params2 || {};
|
|
11337
|
-
const
|
|
11338
|
-
const
|
|
11339
|
-
const
|
|
11340
|
-
if (!groupId) throw new Error("group_id is required");
|
|
11341
|
-
if (content === void 0) throw new Error("content is required");
|
|
11342
|
-
const settingsUpdate = { "announcement.content": content };
|
|
11343
|
-
if (attachments !== void 0) {
|
|
11344
|
-
settingsUpdate["announcement.attachments"] = attachments;
|
|
11345
|
-
}
|
|
11346
|
-
const result = await this.setSettings({
|
|
11347
|
-
group_id: groupId,
|
|
11348
|
-
settings: settingsUpdate
|
|
11349
|
-
});
|
|
12142
|
+
const result = await this.updateSettingWithIndex({ ...merged, keyName: "announcement" });
|
|
12143
|
+
const resultMap = result;
|
|
12144
|
+
const setting = resultMap.setting;
|
|
11350
12145
|
return {
|
|
11351
|
-
group_id:
|
|
12146
|
+
group_id: resultMap.group_id,
|
|
11352
12147
|
announcement: {
|
|
11353
|
-
group_id:
|
|
11354
|
-
content,
|
|
11355
|
-
attachments: attachments
|
|
12148
|
+
group_id: resultMap.group_id,
|
|
12149
|
+
content: setting.content,
|
|
12150
|
+
attachments: setting.attachments
|
|
11356
12151
|
}
|
|
11357
12152
|
};
|
|
11358
12153
|
}
|
|
11359
12154
|
async getRules(params2) {
|
|
11360
12155
|
const merged = params2 || {};
|
|
11361
|
-
const
|
|
11362
|
-
|
|
11363
|
-
const
|
|
11364
|
-
group_id: groupId,
|
|
11365
|
-
keys: ["rules.content", "rules.attachments"]
|
|
11366
|
-
});
|
|
11367
|
-
const settings = settingsToMap(result);
|
|
12156
|
+
const result = await this.getSettingWithIndex({ ...merged, keyName: "rules" });
|
|
12157
|
+
const resultMap = result;
|
|
12158
|
+
const setting = resultMap.setting;
|
|
11368
12159
|
return {
|
|
11369
|
-
group_id:
|
|
12160
|
+
group_id: resultMap.group_id,
|
|
11370
12161
|
rules: {
|
|
11371
|
-
group_id:
|
|
11372
|
-
content:
|
|
11373
|
-
attachments:
|
|
11374
|
-
updated_by:
|
|
11375
|
-
updated_at:
|
|
12162
|
+
group_id: setting.group_id,
|
|
12163
|
+
content: setting.content,
|
|
12164
|
+
attachments: setting.attachments,
|
|
12165
|
+
updated_by: setting.updated_by,
|
|
12166
|
+
updated_at: setting.updated_at
|
|
11376
12167
|
}
|
|
11377
12168
|
};
|
|
11378
12169
|
}
|
|
11379
12170
|
async updateRules(params2) {
|
|
11380
12171
|
const merged = params2 || {};
|
|
11381
|
-
const
|
|
11382
|
-
const
|
|
11383
|
-
const
|
|
11384
|
-
if (!groupId) throw new Error("group_id is required");
|
|
11385
|
-
if (content === void 0) throw new Error("content is required");
|
|
11386
|
-
const settingsUpdate = { "rules.content": content };
|
|
11387
|
-
if (attachments !== void 0) {
|
|
11388
|
-
settingsUpdate["rules.attachments"] = attachments;
|
|
11389
|
-
}
|
|
11390
|
-
const result = await this.setSettings({
|
|
11391
|
-
group_id: groupId,
|
|
11392
|
-
settings: settingsUpdate
|
|
11393
|
-
});
|
|
12172
|
+
const result = await this.updateSettingWithIndex({ ...merged, keyName: "rules" });
|
|
12173
|
+
const resultMap = result;
|
|
12174
|
+
const setting = resultMap.setting;
|
|
11394
12175
|
return {
|
|
11395
|
-
group_id:
|
|
12176
|
+
group_id: resultMap.group_id,
|
|
11396
12177
|
rules: {
|
|
11397
|
-
group_id:
|
|
11398
|
-
content,
|
|
11399
|
-
attachments: attachments
|
|
12178
|
+
group_id: resultMap.group_id,
|
|
12179
|
+
content: setting.content,
|
|
12180
|
+
attachments: setting.attachments
|
|
11400
12181
|
}
|
|
11401
12182
|
};
|
|
11402
12183
|
}
|
|
@@ -11404,19 +12185,19 @@ var GroupFacade = class extends RpcFacade {
|
|
|
11404
12185
|
const merged = params2 || {};
|
|
11405
12186
|
const groupId = merged.group_id;
|
|
11406
12187
|
if (!groupId) throw new Error("group_id is required");
|
|
11407
|
-
const
|
|
11408
|
-
|
|
11409
|
-
|
|
11410
|
-
|
|
11411
|
-
const settings = settingsToMap(result);
|
|
12188
|
+
const { groupId: resultGroupId, settings } = await this.getIndexedSettings(
|
|
12189
|
+
String(groupId),
|
|
12190
|
+
["join.mode", "join.question", "join.auto_approve_patterns", "join.max_pending", "join.attachments"]
|
|
12191
|
+
);
|
|
11412
12192
|
return {
|
|
11413
|
-
group_id:
|
|
12193
|
+
group_id: resultGroupId,
|
|
11414
12194
|
join_requirements: {
|
|
11415
|
-
group_id:
|
|
12195
|
+
group_id: resultGroupId,
|
|
11416
12196
|
mode: settings["join.mode"] || "open",
|
|
11417
12197
|
question: settings["join.question"] || "",
|
|
11418
12198
|
auto_approve_patterns: settings["join.auto_approve_patterns"] || [],
|
|
11419
12199
|
max_pending: settings["join.max_pending"] || 100,
|
|
12200
|
+
attachments: settings["join.attachments"] || [],
|
|
11420
12201
|
updated_by: settings["join.mode.updated_by"] || "",
|
|
11421
12202
|
updated_at: settings["join.mode.updated_at"] || 0
|
|
11422
12203
|
}
|
|
@@ -11431,13 +12212,11 @@ var GroupFacade = class extends RpcFacade {
|
|
|
11431
12212
|
if ("question" in merged) settingsUpdate["join.question"] = merged.question;
|
|
11432
12213
|
if ("auto_approve_patterns" in merged) settingsUpdate["join.auto_approve_patterns"] = merged.auto_approve_patterns;
|
|
11433
12214
|
if ("max_pending" in merged) settingsUpdate["join.max_pending"] = merged.max_pending;
|
|
12215
|
+
if ("attachments" in merged) settingsUpdate["join.attachments"] = merged.attachments;
|
|
11434
12216
|
if (Object.keys(settingsUpdate).length === 0) {
|
|
11435
12217
|
throw new Error("at least one field to update is required");
|
|
11436
12218
|
}
|
|
11437
|
-
const result = await this.
|
|
11438
|
-
group_id: groupId,
|
|
11439
|
-
settings: settingsUpdate
|
|
11440
|
-
});
|
|
12219
|
+
const result = await this.updateGroupIndex(indexUpdateParams(groupId, settingsUpdate, merged));
|
|
11441
12220
|
return {
|
|
11442
12221
|
group_id: result.group_id,
|
|
11443
12222
|
join_requirements: {
|
|
@@ -11445,7 +12224,8 @@ var GroupFacade = class extends RpcFacade {
|
|
|
11445
12224
|
mode: merged.mode,
|
|
11446
12225
|
question: merged.question,
|
|
11447
12226
|
auto_approve_patterns: merged.auto_approve_patterns,
|
|
11448
|
-
max_pending: merged.max_pending
|
|
12227
|
+
max_pending: merged.max_pending,
|
|
12228
|
+
attachments: merged.attachments || []
|
|
11449
12229
|
}
|
|
11450
12230
|
};
|
|
11451
12231
|
}
|
|
@@ -11573,7 +12353,7 @@ function unmountFromAny(input, fallback) {
|
|
|
11573
12353
|
}
|
|
11574
12354
|
|
|
11575
12355
|
// src/storage/vfs.ts
|
|
11576
|
-
async function
|
|
12356
|
+
async function sha256Hex3(data) {
|
|
11577
12357
|
const copy = new Uint8Array(data.byteLength);
|
|
11578
12358
|
copy.set(data);
|
|
11579
12359
|
const subtle = globalThis.crypto?.subtle;
|
|
@@ -11625,7 +12405,7 @@ var StorageVFS = class {
|
|
|
11625
12405
|
const owner = this.owner(options.owner);
|
|
11626
12406
|
const bucket = options.bucket ?? "default";
|
|
11627
12407
|
const objectKey = pathToKey(path);
|
|
11628
|
-
const sha2566 = await
|
|
12408
|
+
const sha2566 = await sha256Hex3(data);
|
|
11629
12409
|
const overwrite = options.overwrite ?? false;
|
|
11630
12410
|
try {
|
|
11631
12411
|
const check = await this.lowlevel.checkUpload({ owner, bucket, objectKey, size: data.length, sha256: sha2566 });
|
|
@@ -11721,7 +12501,7 @@ var StorageVFS = class {
|
|
|
11721
12501
|
}
|
|
11722
12502
|
async downloadFile(path, options = {}) {
|
|
11723
12503
|
const { data, sha256: expectedSha } = await this.readBytesWithMetadata(path, options);
|
|
11724
|
-
const actualSha = await
|
|
12504
|
+
const actualSha = await sha256Hex3(data);
|
|
11725
12505
|
const expected = expectedSha.toLowerCase();
|
|
11726
12506
|
const verified = Boolean(expected) && actualSha.toLowerCase() === expected;
|
|
11727
12507
|
if (!verified && expected && (options.verifyHash ?? true)) {
|
|
@@ -12207,114 +12987,6 @@ init_crypto();
|
|
|
12207
12987
|
// src/v2/e2ee/types.ts
|
|
12208
12988
|
var SUITE_NAME = "P256_HKDF_SHA256_AES_256_GCM";
|
|
12209
12989
|
|
|
12210
|
-
// src/v2/crypto/canonical.ts
|
|
12211
|
-
var encoder = new TextEncoder();
|
|
12212
|
-
var MAX_SAFE_JSON_INTEGER = 9007199254740991;
|
|
12213
|
-
function canonicalJson(obj) {
|
|
12214
|
-
return encoder.encode(serialize(obj));
|
|
12215
|
-
}
|
|
12216
|
-
function serialize(obj) {
|
|
12217
|
-
if (obj === null) return "null";
|
|
12218
|
-
if (obj === true) return "true";
|
|
12219
|
-
if (obj === false) return "false";
|
|
12220
|
-
if (typeof obj === "number") {
|
|
12221
|
-
return serializeNumber(obj);
|
|
12222
|
-
}
|
|
12223
|
-
if (typeof obj === "string") {
|
|
12224
|
-
return serializeString(obj);
|
|
12225
|
-
}
|
|
12226
|
-
if (Array.isArray(obj)) {
|
|
12227
|
-
return serializeArray(obj);
|
|
12228
|
-
}
|
|
12229
|
-
if (typeof obj === "object") {
|
|
12230
|
-
return serializeObject(obj);
|
|
12231
|
-
}
|
|
12232
|
-
throw new TypeError(`canonicalJson: unsupported type ${typeof obj}`);
|
|
12233
|
-
}
|
|
12234
|
-
function serializeNumber(n) {
|
|
12235
|
-
if (!isFinite(n)) {
|
|
12236
|
-
throw new RangeError("canonicalJson: Infinity and NaN not allowed");
|
|
12237
|
-
}
|
|
12238
|
-
if (Object.is(n, -0)) return "0";
|
|
12239
|
-
if (Number.isInteger(n)) {
|
|
12240
|
-
if (Math.abs(n) > MAX_SAFE_JSON_INTEGER) {
|
|
12241
|
-
throw new RangeError(`canonicalJson: integer outside safe range ${n}`);
|
|
12242
|
-
}
|
|
12243
|
-
return String(n);
|
|
12244
|
-
}
|
|
12245
|
-
return expandExponent(String(n));
|
|
12246
|
-
}
|
|
12247
|
-
function expandExponent(s) {
|
|
12248
|
-
if (!/[eE]/.test(s)) return s;
|
|
12249
|
-
const match = /^(-?)(\d+)(?:\.(\d+))?[eE]([+-]?\d+)$/.exec(s);
|
|
12250
|
-
if (!match) {
|
|
12251
|
-
throw new TypeError(`canonicalJson: invalid number ${s}`);
|
|
12252
|
-
}
|
|
12253
|
-
const sign = match[1] ?? "";
|
|
12254
|
-
const intPart = match[2] ?? "";
|
|
12255
|
-
const fracPart = match[3] ?? "";
|
|
12256
|
-
const exp = Number(match[4]);
|
|
12257
|
-
const digits = intPart + fracPart;
|
|
12258
|
-
const point = intPart.length + exp;
|
|
12259
|
-
if (point <= 0) {
|
|
12260
|
-
return `${sign}0.${"0".repeat(-point)}${digits}`;
|
|
12261
|
-
}
|
|
12262
|
-
if (point >= digits.length) {
|
|
12263
|
-
return `${sign}${digits}${"0".repeat(point - digits.length)}`;
|
|
12264
|
-
}
|
|
12265
|
-
return `${sign}${digits.slice(0, point)}.${digits.slice(point)}`;
|
|
12266
|
-
}
|
|
12267
|
-
function serializeString(s) {
|
|
12268
|
-
let result = '"';
|
|
12269
|
-
for (let i = 0; i < s.length; i++) {
|
|
12270
|
-
const ch = s[i];
|
|
12271
|
-
const code = s.charCodeAt(i);
|
|
12272
|
-
if (ch === '"') {
|
|
12273
|
-
result += '\\"';
|
|
12274
|
-
} else if (ch === "\\") {
|
|
12275
|
-
result += "\\\\";
|
|
12276
|
-
} else if (ch === "\b") {
|
|
12277
|
-
result += "\\b";
|
|
12278
|
-
} else if (ch === "\f") {
|
|
12279
|
-
result += "\\f";
|
|
12280
|
-
} else if (ch === "\n") {
|
|
12281
|
-
result += "\\n";
|
|
12282
|
-
} else if (ch === "\r") {
|
|
12283
|
-
result += "\\r";
|
|
12284
|
-
} else if (ch === " ") {
|
|
12285
|
-
result += "\\t";
|
|
12286
|
-
} else if (code < 32) {
|
|
12287
|
-
result += "\\u" + code.toString(16).padStart(4, "0");
|
|
12288
|
-
} else {
|
|
12289
|
-
result += ch;
|
|
12290
|
-
}
|
|
12291
|
-
}
|
|
12292
|
-
result += '"';
|
|
12293
|
-
return result;
|
|
12294
|
-
}
|
|
12295
|
-
function serializeArray(arr) {
|
|
12296
|
-
const items = arr.map((item) => serialize(item));
|
|
12297
|
-
return "[" + items.join(",") + "]";
|
|
12298
|
-
}
|
|
12299
|
-
function serializeObject(obj) {
|
|
12300
|
-
const sortedKeys = Object.keys(obj).sort(compareCodePoints);
|
|
12301
|
-
const pairs = sortedKeys.map(
|
|
12302
|
-
(key) => serializeString(key) + ":" + serialize(obj[key])
|
|
12303
|
-
);
|
|
12304
|
-
return "{" + pairs.join(",") + "}";
|
|
12305
|
-
}
|
|
12306
|
-
function compareCodePoints(a, b) {
|
|
12307
|
-
const ac = Array.from(a);
|
|
12308
|
-
const bc = Array.from(b);
|
|
12309
|
-
const n = Math.min(ac.length, bc.length);
|
|
12310
|
-
for (let i = 0; i < n; i++) {
|
|
12311
|
-
const av = ac[i].codePointAt(0) ?? 0;
|
|
12312
|
-
const bv = bc[i].codePointAt(0) ?? 0;
|
|
12313
|
-
if (av !== bv) return av - bv;
|
|
12314
|
-
}
|
|
12315
|
-
return ac.length - bc.length;
|
|
12316
|
-
}
|
|
12317
|
-
|
|
12318
12990
|
// node_modules/@noble/hashes/utils.js
|
|
12319
12991
|
function isBytes(a) {
|
|
12320
12992
|
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array" && "BYTES_PER_ELEMENT" in a && a.BYTES_PER_ELEMENT === 1;
|
|
@@ -12383,7 +13055,7 @@ var hasHexBuiltin = /* @__PURE__ */ (() => (
|
|
|
12383
13055
|
typeof Uint8Array.from([]).toHex === "function" && typeof Uint8Array.fromHex === "function"
|
|
12384
13056
|
))();
|
|
12385
13057
|
var hexes = /* @__PURE__ */ Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
12386
|
-
function
|
|
13058
|
+
function bytesToHex2(bytes) {
|
|
12387
13059
|
abytes(bytes);
|
|
12388
13060
|
if (hasHexBuiltin)
|
|
12389
13061
|
return bytes.toHex();
|
|
@@ -12739,7 +13411,7 @@ var sha256 = /* @__PURE__ */ createHasher(
|
|
|
12739
13411
|
// node_modules/@noble/curves/utils.js
|
|
12740
13412
|
var abytes2 = (value, length, title) => abytes(value, length, title);
|
|
12741
13413
|
var anumber2 = anumber;
|
|
12742
|
-
var
|
|
13414
|
+
var bytesToHex3 = bytesToHex2;
|
|
12743
13415
|
var concatBytes2 = (...arrays) => concatBytes(...arrays);
|
|
12744
13416
|
var hexToBytes2 = (hex) => hexToBytes(hex);
|
|
12745
13417
|
var isBytes2 = isBytes;
|
|
@@ -12781,10 +13453,10 @@ function hexToNumber(hex) {
|
|
|
12781
13453
|
return hex === "" ? _0n : BigInt("0x" + hex);
|
|
12782
13454
|
}
|
|
12783
13455
|
function bytesToNumberBE(bytes) {
|
|
12784
|
-
return hexToNumber(
|
|
13456
|
+
return hexToNumber(bytesToHex2(bytes));
|
|
12785
13457
|
}
|
|
12786
13458
|
function bytesToNumberLE(bytes) {
|
|
12787
|
-
return hexToNumber(
|
|
13459
|
+
return hexToNumber(bytesToHex2(copyBytes(abytes(bytes)).reverse()));
|
|
12788
13460
|
}
|
|
12789
13461
|
function numberToBytesBE(n, len) {
|
|
12790
13462
|
anumber(len);
|
|
@@ -14170,7 +14842,7 @@ function weierstrass(params2, extraOpts = {}) {
|
|
|
14170
14842
|
return encodePoint(_Point, this, isCompressed);
|
|
14171
14843
|
}
|
|
14172
14844
|
toHex(isCompressed = true) {
|
|
14173
|
-
return
|
|
14845
|
+
return bytesToHex3(this.toBytes(isCompressed));
|
|
14174
14846
|
}
|
|
14175
14847
|
toString() {
|
|
14176
14848
|
return `<Point ${this.is0() ? "ZERO" : this.toHex()}>`;
|
|
@@ -14397,7 +15069,7 @@ function ecdsa(Point, hash, ecdsaOpts = {}) {
|
|
|
14397
15069
|
return concatBytes2(rb, sb);
|
|
14398
15070
|
}
|
|
14399
15071
|
toHex(format) {
|
|
14400
|
-
return
|
|
15072
|
+
return bytesToHex3(this.toBytes(format));
|
|
14401
15073
|
}
|
|
14402
15074
|
}
|
|
14403
15075
|
Object.freeze(Signature.prototype);
|
|
@@ -14764,7 +15436,7 @@ async function sha2562(data) {
|
|
|
14764
15436
|
const buf = await crypto.subtle.digest("SHA-256", data.slice().buffer);
|
|
14765
15437
|
return new Uint8Array(buf);
|
|
14766
15438
|
}
|
|
14767
|
-
function
|
|
15439
|
+
function bytesToHex4(b) {
|
|
14768
15440
|
let s = "";
|
|
14769
15441
|
for (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, "0");
|
|
14770
15442
|
return s;
|
|
@@ -14857,7 +15529,7 @@ async function computeMerkleRoot(rows) {
|
|
|
14857
15529
|
}
|
|
14858
15530
|
layer = next;
|
|
14859
15531
|
}
|
|
14860
|
-
return
|
|
15532
|
+
return bytesToHex4(layer[0]);
|
|
14861
15533
|
}
|
|
14862
15534
|
async function verifyMerkleProof(leaf, proof, expectedRootHex) {
|
|
14863
15535
|
if (!expectedRootHex) return false;
|
|
@@ -14873,7 +15545,7 @@ async function verifyMerkleProof(leaf, proof, expectedRootHex) {
|
|
|
14873
15545
|
else if (step.position === "R") cur = await nodeHash(cur, sibling);
|
|
14874
15546
|
else return false;
|
|
14875
15547
|
}
|
|
14876
|
-
return
|
|
15548
|
+
return bytesToHex4(cur) === expectedRootHex;
|
|
14877
15549
|
}
|
|
14878
15550
|
async function computeRecipientsDigest(rows) {
|
|
14879
15551
|
return computeMerkleRoot(rows);
|
|
@@ -14983,7 +15655,7 @@ function bytesToBase643(b) {
|
|
|
14983
15655
|
for (let i = 0; i < b.length; i++) bin += String.fromCharCode(b[i]);
|
|
14984
15656
|
return btoa(bin);
|
|
14985
15657
|
}
|
|
14986
|
-
function
|
|
15658
|
+
function bytesToHex5(b) {
|
|
14987
15659
|
let s = "";
|
|
14988
15660
|
for (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, "0");
|
|
14989
15661
|
return s;
|
|
@@ -15007,7 +15679,7 @@ function uuid4Hex() {
|
|
|
15007
15679
|
crypto.getRandomValues(b);
|
|
15008
15680
|
b[6] = b[6] & 15 | 64;
|
|
15009
15681
|
b[8] = b[8] & 63 | 128;
|
|
15010
|
-
return
|
|
15682
|
+
return bytesToHex5(b);
|
|
15011
15683
|
}
|
|
15012
15684
|
function randomBytes3(n) {
|
|
15013
15685
|
const b = new Uint8Array(n);
|
|
@@ -15097,7 +15769,7 @@ async function encryptP2PMessage(sender, targetSet, payload, opts = {}) {
|
|
|
15097
15769
|
pos += aadBytes.length;
|
|
15098
15770
|
signInput.set(digestBytes, pos);
|
|
15099
15771
|
const senderSig = await ecdsaSignRaw(sender.ikPriv, signInput);
|
|
15100
|
-
const certFpHash =
|
|
15772
|
+
const certFpHash = bytesToHex5(await sha2563(sender.ikPubDer));
|
|
15101
15773
|
const certFp = `sha256:${certFpHash.substring(0, 16)}`;
|
|
15102
15774
|
const envelope = {
|
|
15103
15775
|
type: "e2ee.p2p_encrypted",
|
|
@@ -15165,7 +15837,7 @@ function normalizeProtectedHeaderValue(value) {
|
|
|
15165
15837
|
async function wrapForRecipient(target, masterKey, senderSessionPriv, senderMasterPriv, wrapSalt, defaultRole) {
|
|
15166
15838
|
const role = target.role ?? defaultRole;
|
|
15167
15839
|
const keySource = target.keySource ?? "aid_master";
|
|
15168
|
-
const fpHash =
|
|
15840
|
+
const fpHash = bytesToHex5(await sha2563(target.ikPkDer));
|
|
15169
15841
|
const fp = `sha256:${fpHash.substring(0, 16)}`;
|
|
15170
15842
|
const wrapNonce = randomBytes3(12);
|
|
15171
15843
|
const use3DH = usesSPKWrap(target);
|
|
@@ -15220,7 +15892,7 @@ function bytesToBase644(b) {
|
|
|
15220
15892
|
for (let i = 0; i < b.length; i++) bin += String.fromCharCode(b[i]);
|
|
15221
15893
|
return btoa(bin);
|
|
15222
15894
|
}
|
|
15223
|
-
function
|
|
15895
|
+
function bytesToHex6(b) {
|
|
15224
15896
|
let s = "";
|
|
15225
15897
|
for (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, "0");
|
|
15226
15898
|
return s;
|
|
@@ -15244,7 +15916,7 @@ function uuid4Hex2() {
|
|
|
15244
15916
|
crypto.getRandomValues(b);
|
|
15245
15917
|
b[6] = b[6] & 15 | 64;
|
|
15246
15918
|
b[8] = b[8] & 63 | 128;
|
|
15247
|
-
return
|
|
15919
|
+
return bytesToHex6(b);
|
|
15248
15920
|
}
|
|
15249
15921
|
function randomBytes4(n) {
|
|
15250
15922
|
const b = new Uint8Array(n);
|
|
@@ -15315,7 +15987,7 @@ async function encryptGroupMessage(sender, groupId, epoch, targets, payload, opt
|
|
|
15315
15987
|
pos += aadBytes.length;
|
|
15316
15988
|
signInput.set(digestBytes, pos);
|
|
15317
15989
|
const senderSig = await ecdsaSignRaw(sender.ikPriv, signInput);
|
|
15318
|
-
const certFpHash =
|
|
15990
|
+
const certFpHash = bytesToHex6(await sha2564(sender.ikPubDer));
|
|
15319
15991
|
const certFp = `sha256:${certFpHash.substring(0, 16)}`;
|
|
15320
15992
|
const envelope = {
|
|
15321
15993
|
type: "e2ee.group_encrypted",
|
|
@@ -15353,7 +16025,7 @@ async function encryptGroupMessage(sender, groupId, epoch, targets, payload, opt
|
|
|
15353
16025
|
async function wrapForRecipient2(target, masterKey, senderSessionPriv, senderMasterPriv, wrapSalt) {
|
|
15354
16026
|
const role = target.role ?? "member";
|
|
15355
16027
|
const keySource = target.keySource ?? "aid_master";
|
|
15356
|
-
const fpHash =
|
|
16028
|
+
const fpHash = bytesToHex6(await sha2564(target.ikPkDer));
|
|
15357
16029
|
const fp = `sha256:${fpHash.substring(0, 16)}`;
|
|
15358
16030
|
const wrapNonce = randomBytes4(12);
|
|
15359
16031
|
const use3DH = usesSPKWrap2(target);
|
|
@@ -16011,7 +16683,7 @@ var PEER_KEY_CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
|
16011
16683
|
var DESTROY_DELAY_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
16012
16684
|
var RECENT_GENERATIONS = 7;
|
|
16013
16685
|
var HARD_LIMIT_MS = 180 * 24 * 60 * 60 * 1e3;
|
|
16014
|
-
async function
|
|
16686
|
+
async function sha256Hex4(data) {
|
|
16015
16687
|
const buf = await crypto.subtle.digest("SHA-256", data.slice().buffer);
|
|
16016
16688
|
const arr = new Uint8Array(buf);
|
|
16017
16689
|
let hex = "";
|
|
@@ -16102,7 +16774,7 @@ var V2Session = class {
|
|
|
16102
16774
|
}
|
|
16103
16775
|
async _generateNewSPK() {
|
|
16104
16776
|
const [priv, pubDer] = await generateP256Keypair();
|
|
16105
|
-
const hex = await
|
|
16777
|
+
const hex = await sha256Hex4(pubDer);
|
|
16106
16778
|
const spkId = `sha256:${hex.substring(0, 16)}`;
|
|
16107
16779
|
await this._store.saveSPK(this._storeDeviceId, spkId, priv, pubDer);
|
|
16108
16780
|
this._spkId = spkId;
|
|
@@ -16110,7 +16782,7 @@ var V2Session = class {
|
|
|
16110
16782
|
this._spkPubDer = pubDer;
|
|
16111
16783
|
}
|
|
16112
16784
|
async _ikSPKId() {
|
|
16113
|
-
const hex = await
|
|
16785
|
+
const hex = await sha256Hex4(this._ikPubDer);
|
|
16114
16786
|
return `sha256:${hex.substring(0, 16)}`;
|
|
16115
16787
|
}
|
|
16116
16788
|
_groupKey(groupId) {
|
|
@@ -16340,7 +17012,7 @@ var V2Session = class {
|
|
|
16340
17012
|
}
|
|
16341
17013
|
}
|
|
16342
17014
|
const [priv, pubDer] = await generateP256Keypair();
|
|
16343
|
-
const hex = await
|
|
17015
|
+
const hex = await sha256Hex4(pubDer);
|
|
16344
17016
|
const spkId = `sha256:${hex.substring(0, 16)}`;
|
|
16345
17017
|
await this._store.saveGroupSPK(this._storeDeviceId, gk, spkId, priv, pubDer);
|
|
16346
17018
|
return { spkId, priv, pubDer };
|
|
@@ -16365,7 +17037,7 @@ var V2Session = class {
|
|
|
16365
17037
|
await this.ensureKeys();
|
|
16366
17038
|
const gk = this._groupKey(groupId);
|
|
16367
17039
|
const [priv, pubDer] = await generateP256Keypair();
|
|
16368
|
-
const hex = await
|
|
17040
|
+
const hex = await sha256Hex4(pubDer);
|
|
16369
17041
|
const spkId = `sha256:${hex.substring(0, 16)}`;
|
|
16370
17042
|
await this._store.saveGroupSPK(this._storeDeviceId, gk, spkId, priv, pubDer);
|
|
16371
17043
|
await this._publishGroupSPK(gk, spkId, pubDer, callFn);
|
|
@@ -16459,7 +17131,7 @@ function v2Sleep(ms) {
|
|
|
16459
17131
|
globalThis.setTimeout(resolve, ms);
|
|
16460
17132
|
});
|
|
16461
17133
|
}
|
|
16462
|
-
function
|
|
17134
|
+
function exactArrayBuffer2(bytes) {
|
|
16463
17135
|
return bytes.slice().buffer;
|
|
16464
17136
|
}
|
|
16465
17137
|
async function pubDerMatchesFingerprint(pubDer, certFingerprint) {
|
|
@@ -16468,7 +17140,7 @@ async function pubDerMatchesFingerprint(pubDer, certFingerprint) {
|
|
|
16468
17140
|
if (!expected.startsWith("sha256:")) return false;
|
|
16469
17141
|
const expectedHex = expected.slice("sha256:".length);
|
|
16470
17142
|
if (![16, 64].includes(expectedHex.length) || !/^[0-9a-f]+$/.test(expectedHex)) return false;
|
|
16471
|
-
const digest = await crypto.subtle.digest("SHA-256",
|
|
17143
|
+
const digest = await crypto.subtle.digest("SHA-256", exactArrayBuffer2(pubDer));
|
|
16472
17144
|
const spkiHex = Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
16473
17145
|
return expectedHex.length === 16 ? spkiHex.slice(0, 16) === expectedHex : spkiHex === expectedHex;
|
|
16474
17146
|
}
|
|
@@ -16817,7 +17489,7 @@ var V2E2EECoordinator = class {
|
|
|
16817
17489
|
const pkcs8Der = v2B64ToBytes(pemBody);
|
|
16818
17490
|
const privKey = await crypto.subtle.importKey(
|
|
16819
17491
|
"pkcs8",
|
|
16820
|
-
|
|
17492
|
+
exactArrayBuffer2(pkcs8Der),
|
|
16821
17493
|
{ name: "ECDH", namedCurve: "P-256" },
|
|
16822
17494
|
true,
|
|
16823
17495
|
["deriveBits"]
|
|
@@ -17111,7 +17783,7 @@ var V2E2EECoordinator = class {
|
|
|
17111
17783
|
const messages = pageMessages.filter((msg) => {
|
|
17112
17784
|
const seq = Number(msg.seq ?? 0);
|
|
17113
17785
|
return Number.isFinite(seq) && seq > nextAfterSeq;
|
|
17114
|
-
});
|
|
17786
|
+
}).sort((a, b) => Number(a.seq ?? 0) - Number(b.seq ?? 0));
|
|
17115
17787
|
client._clientLog.debug(`message.v2.pull page response: page=${pageCount}, raw_count=${messages.length}, stale_count=${Math.max(0, pageMessages.length - messages.length)}, has_more=${String(result.has_more ?? "")}, server_ack_seq=${String(result.server_ack_seq ?? "")}`);
|
|
17116
17788
|
const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
17117
17789
|
const pageContigBefore = ns ? client._seqTracker.getContiguousSeq(ns) : 0;
|
|
@@ -17349,7 +18021,7 @@ var V2E2EECoordinator = class {
|
|
|
17349
18021
|
const messages = pageMessages.filter((msg) => {
|
|
17350
18022
|
const seq = Number(msg.seq ?? 0);
|
|
17351
18023
|
return Number.isFinite(seq) && seq > nextAfterSeq;
|
|
17352
|
-
});
|
|
18024
|
+
}).sort((a, b) => Number(a.seq ?? 0) - Number(b.seq ?? 0));
|
|
17353
18025
|
const seqs = messages.map((msg) => Number(msg.seq ?? 0)).filter((seq) => Number.isFinite(seq) && seq > 0);
|
|
17354
18026
|
const pageContigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
17355
18027
|
const pageMaxSeq = seqs.length > 0 ? Math.max(...seqs) : nextAfterSeq;
|
|
@@ -18310,7 +18982,7 @@ function lengthPrefixedBytesKey(...parts) {
|
|
|
18310
18982
|
}
|
|
18311
18983
|
return out;
|
|
18312
18984
|
}
|
|
18313
|
-
function
|
|
18985
|
+
function bytesToHex7(bytes) {
|
|
18314
18986
|
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
18315
18987
|
}
|
|
18316
18988
|
function sortObjectKeys(obj) {
|
|
@@ -18377,7 +19049,7 @@ async function computeStateHash(params2) {
|
|
|
18377
19049
|
offset += chunk.length;
|
|
18378
19050
|
}
|
|
18379
19051
|
const digest = await crypto.subtle.digest("SHA-256", data);
|
|
18380
|
-
return
|
|
19052
|
+
return bytesToHex7(new Uint8Array(digest));
|
|
18381
19053
|
}
|
|
18382
19054
|
function normalizedGroupId(raw) {
|
|
18383
19055
|
const groupId = String(raw ?? "").trim();
|
|
@@ -18518,7 +19190,7 @@ var GroupStateCoordinator = class {
|
|
|
18518
19190
|
cacheData.byteOffset + cacheData.byteLength
|
|
18519
19191
|
);
|
|
18520
19192
|
const cacheHash = new Uint8Array(await crypto.subtle.digest("SHA-256", cacheInput));
|
|
18521
|
-
const cacheKey =
|
|
19193
|
+
const cacheKey = bytesToHex7(cacheHash);
|
|
18522
19194
|
const sigCache = this.runtime.groupState.sigCache;
|
|
18523
19195
|
const now = Date.now();
|
|
18524
19196
|
const cachedExp = sigCache.get(cacheKey);
|
|
@@ -19237,10 +19909,10 @@ function isAIDObject(value) {
|
|
|
19237
19909
|
candidate && typeof candidate === "object" && typeof candidate.aid === "string" && typeof candidate.aunPath === "string" && typeof candidate.isPrivateKeyValid === "function"
|
|
19238
19910
|
);
|
|
19239
19911
|
}
|
|
19240
|
-
function
|
|
19912
|
+
function bytesToHex8(bytes) {
|
|
19241
19913
|
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
19242
19914
|
}
|
|
19243
|
-
function
|
|
19915
|
+
function exactArrayBuffer3(bytes) {
|
|
19244
19916
|
return bytes.slice().buffer;
|
|
19245
19917
|
}
|
|
19246
19918
|
function attachGatewayProximity2(message, source) {
|
|
@@ -19253,6 +19925,13 @@ function attachGatewayProximity2(message, source) {
|
|
|
19253
19925
|
}
|
|
19254
19926
|
}
|
|
19255
19927
|
}
|
|
19928
|
+
function groupIndexBodyText(value) {
|
|
19929
|
+
if (typeof value === "string") return value;
|
|
19930
|
+
if (value && typeof value === "object" && !Array.isArray(value) && "body" in value) {
|
|
19931
|
+
return String(value.body ?? "");
|
|
19932
|
+
}
|
|
19933
|
+
return "";
|
|
19934
|
+
}
|
|
19256
19935
|
var DEFAULT_SESSION_OPTIONS = {
|
|
19257
19936
|
auto_reconnect: true,
|
|
19258
19937
|
heartbeat_interval: 30,
|
|
@@ -19605,6 +20284,8 @@ var _AUNClient = class _AUNClient {
|
|
|
19605
20284
|
__publicField(this, "_v2LazyProposeTriggered", /* @__PURE__ */ new Map());
|
|
19606
20285
|
/** agent.md 运行时管理器,负责上传、下载、缓存和 RPC 元数据观察。 */
|
|
19607
20286
|
__publicField(this, "_agentMdManager");
|
|
20287
|
+
__publicField(this, "_groupIndexMetaCache", new GroupIndexMetaCache());
|
|
20288
|
+
__publicField(this, "_groupIndexCacheLoaded", /* @__PURE__ */ new Set());
|
|
19608
20289
|
/** 消息序列号跟踪器(群消息 + P2P 空洞检测) */
|
|
19609
20290
|
__publicField(this, "_seqTracker", new SeqTracker());
|
|
19610
20291
|
__publicField(this, "_seqTrackerContext", null);
|
|
@@ -19614,9 +20295,13 @@ var _AUNClient = class _AUNClient {
|
|
|
19614
20295
|
__publicField(this, "_pushedSeqs", /* @__PURE__ */ new Map());
|
|
19615
20296
|
/** 已解密但因 seq 空洞暂缓发布的应用层消息(按 namespace -> seq) */
|
|
19616
20297
|
__publicField(this, "_pendingOrderedMsgs", /* @__PURE__ */ new Map());
|
|
20298
|
+
/** push 处理队列:按 P2P / group namespace 串行化异步解密与有序投递。 */
|
|
20299
|
+
__publicField(this, "_pushProcessQueues", /* @__PURE__ */ new Map());
|
|
19617
20300
|
/** Lazy group sync:首次发送群消息前自动拉取历史 */
|
|
19618
20301
|
__publicField(this, "_groupSynced", /* @__PURE__ */ new Set());
|
|
19619
|
-
/**
|
|
20302
|
+
/** P2P 撤回去重:原始 message_id -> 时间戳,保证应用层只回调一次 */
|
|
20303
|
+
__publicField(this, "_messageRecallSeen", /* @__PURE__ */ new Map());
|
|
20304
|
+
/** 群撤回去重:group_id|sorted(message_ids) -> 时间戳,保证应用层只回调一次 */
|
|
19620
20305
|
__publicField(this, "_groupRecallSeen", /* @__PURE__ */ new Map());
|
|
19621
20306
|
/** 在线未读 hint 队列:同一 group 只保留最后一条,延迟 drain 降低登录瞬时拉取压力。 */
|
|
19622
20307
|
__publicField(this, "_onlineUnreadHintQueue", /* @__PURE__ */ new Map());
|
|
@@ -19739,11 +20424,11 @@ var _AUNClient = class _AUNClient {
|
|
|
19739
20424
|
timeout: DEFAULT_SESSION_OPTIONS.timeouts.call,
|
|
19740
20425
|
onDisconnect: (error, closeCode) => this._handleTransportDisconnect(error, closeCode)
|
|
19741
20426
|
});
|
|
19742
|
-
this._transport.setMetaObserver(
|
|
19743
|
-
|
|
19744
|
-
this._clientLog.debug(`
|
|
19745
|
-
})
|
|
19746
|
-
|
|
20427
|
+
this._transport.setMetaObserver(
|
|
20428
|
+
(meta) => this._observeRpcMeta(meta).catch((exc) => {
|
|
20429
|
+
this._clientLog.debug(`rpc meta observer skipped: ${String(exc)}`);
|
|
20430
|
+
})
|
|
20431
|
+
);
|
|
19747
20432
|
this._runtime = new ClientRuntime(this);
|
|
19748
20433
|
this._identityRuntime = new IdentityRuntimeManager(this._runtime);
|
|
19749
20434
|
this._peerDirectory = new PeerDirectory(this._runtime);
|
|
@@ -19777,6 +20462,9 @@ var _AUNClient = class _AUNClient {
|
|
|
19777
20462
|
this._dispatcher.subscribe("_raw.message.received", (data) => {
|
|
19778
20463
|
this._onRawMessageReceived(data);
|
|
19779
20464
|
});
|
|
20465
|
+
this._dispatcher.subscribe("_raw.message.recalled", (data) => {
|
|
20466
|
+
this._safeAsync(this._onRawMessageRecalled(data));
|
|
20467
|
+
});
|
|
19780
20468
|
this._dispatcher.subscribe("_raw.group.message_created", (data) => {
|
|
19781
20469
|
this._onRawGroupMessageCreated(data);
|
|
19782
20470
|
});
|
|
@@ -19804,7 +20492,7 @@ var _AUNClient = class _AUNClient {
|
|
|
19804
20492
|
this._dispatcher.subscribe("_raw.group.state_committed", (data) => {
|
|
19805
20493
|
this._safeAsync(this._onGroupStateCommitted(data));
|
|
19806
20494
|
});
|
|
19807
|
-
for (const evt of ["message.
|
|
20495
|
+
for (const evt of ["message.ack", "storage.object_changed"]) {
|
|
19808
20496
|
this._dispatcher.subscribe(`_raw.${evt}`, (data) => {
|
|
19809
20497
|
this._dispatcher.publish(evt, data);
|
|
19810
20498
|
});
|
|
@@ -19847,7 +20535,93 @@ var _AUNClient = class _AUNClient {
|
|
|
19847
20535
|
}
|
|
19848
20536
|
/** transport 的 meta observer:吸收 gateway 注入的 _meta 字段。失败不影响业务。 */
|
|
19849
20537
|
async _observeRpcMeta(meta) {
|
|
20538
|
+
const groupIndexes = isJsonObject(meta.group_indexes) ? meta.group_indexes : {};
|
|
20539
|
+
for (const groupAid of Object.keys(groupIndexes)) {
|
|
20540
|
+
await this._loadGroupIndexCache(groupAid);
|
|
20541
|
+
}
|
|
20542
|
+
this._groupIndexMetaCache.observeRpcMeta(meta, { localAid: this._aid ?? "" });
|
|
19850
20543
|
await this._agentMdManager.observeRpcMeta(meta, this._aid);
|
|
20544
|
+
for (const groupAid of Object.keys(groupIndexes)) {
|
|
20545
|
+
await this._persistGroupIndexCache(groupAid, {
|
|
20546
|
+
remote_meta: this._groupIndexMetaCache.remoteMeta(this._aid ?? "", groupAid) ?? {},
|
|
20547
|
+
local_etag: this._groupIndexMetaCache.localEtag(this._aid ?? "", groupAid)
|
|
20548
|
+
});
|
|
20549
|
+
}
|
|
20550
|
+
}
|
|
20551
|
+
isGroupIndexStale(groupAid) {
|
|
20552
|
+
return this._groupIndexMetaCache.isStale(this._aid ?? "", groupAid);
|
|
20553
|
+
}
|
|
20554
|
+
markGroupIndexFresh(groupAid, options) {
|
|
20555
|
+
this._groupIndexMetaCache.markFresh(this._aid ?? "", groupAid, options);
|
|
20556
|
+
void this._persistGroupIndexCache(groupAid, { local_etag: String(options.etag ?? "") });
|
|
20557
|
+
}
|
|
20558
|
+
getGroupIndexRemoteMeta(groupAid) {
|
|
20559
|
+
return this._groupIndexMetaCache.remoteMeta(this._aid ?? "", groupAid);
|
|
20560
|
+
}
|
|
20561
|
+
getGroupIndexLocalEtag(groupAid) {
|
|
20562
|
+
return this._groupIndexMetaCache.localEtag(this._aid ?? "", groupAid);
|
|
20563
|
+
}
|
|
20564
|
+
async getGroupIndexCachedSettings(groupAid, keys) {
|
|
20565
|
+
await this._loadGroupIndexCache(groupAid);
|
|
20566
|
+
return this._groupIndexMetaCache.cachedSettings(this._aid ?? "", groupAid, keys.map((item) => String(item)));
|
|
20567
|
+
}
|
|
20568
|
+
async getGroupIndexCachedSettingsByEntries(groupAid, keys, entries) {
|
|
20569
|
+
await this._loadGroupIndexCache(groupAid);
|
|
20570
|
+
return this._groupIndexMetaCache.cachedSettingsByEntries(
|
|
20571
|
+
this._aid ?? "",
|
|
20572
|
+
groupAid,
|
|
20573
|
+
keys.map((item) => String(item)),
|
|
20574
|
+
entries
|
|
20575
|
+
);
|
|
20576
|
+
}
|
|
20577
|
+
cacheGroupIndexSettings(groupAid, settings, options) {
|
|
20578
|
+
this._groupIndexMetaCache.cacheSettings(this._aid ?? "", groupAid, settings, options);
|
|
20579
|
+
const entryEtags = {};
|
|
20580
|
+
for (const item of options?.entries ?? []) {
|
|
20581
|
+
const key = String(item.key ?? "");
|
|
20582
|
+
if (key) entryEtags[key] = String(item.etag ?? "");
|
|
20583
|
+
}
|
|
20584
|
+
const fields = {
|
|
20585
|
+
settings,
|
|
20586
|
+
entry_etags: entryEtags,
|
|
20587
|
+
remote_meta: this._groupIndexMetaCache.remoteMeta(this._aid ?? "", groupAid) ?? {},
|
|
20588
|
+
local_etag: String(options?.etag ?? this._groupIndexMetaCache.localEtag(this._aid ?? "", groupAid) ?? "")
|
|
20589
|
+
};
|
|
20590
|
+
const indexJsonl = groupIndexBodyText(options?.groupIndex);
|
|
20591
|
+
if (indexJsonl) fields.index_jsonl = indexJsonl;
|
|
20592
|
+
return this._persistGroupIndexCache(groupAid, fields);
|
|
20593
|
+
}
|
|
20594
|
+
_groupIndexCacheKey(groupAid) {
|
|
20595
|
+
return `${this._aid ?? ""}\0${String(groupAid ?? "")}`;
|
|
20596
|
+
}
|
|
20597
|
+
async _loadGroupIndexCache(groupAid) {
|
|
20598
|
+
const localAid = String(this._aid ?? "").trim();
|
|
20599
|
+
const group = String(groupAid ?? "").trim();
|
|
20600
|
+
if (!localAid || !group || typeof this._tokenStore.loadGroupIndexCache !== "function") return;
|
|
20601
|
+
const key = this._groupIndexCacheKey(group);
|
|
20602
|
+
if (this._groupIndexCacheLoaded.has(key)) return;
|
|
20603
|
+
this._groupIndexCacheLoaded.add(key);
|
|
20604
|
+
const record = await this._tokenStore.loadGroupIndexCache(localAid, group);
|
|
20605
|
+
if (!record) return;
|
|
20606
|
+
this._groupIndexMetaCache.restore(localAid, group, {
|
|
20607
|
+
remote_meta: record.remote_meta,
|
|
20608
|
+
local_etag: record.local_etag,
|
|
20609
|
+
settings: record.settings,
|
|
20610
|
+
entry_etags: record.entry_etags
|
|
20611
|
+
});
|
|
20612
|
+
}
|
|
20613
|
+
async _persistGroupIndexCache(groupAid, fields) {
|
|
20614
|
+
const localAid = String(this._aid ?? "").trim();
|
|
20615
|
+
const group = String(groupAid ?? "").trim();
|
|
20616
|
+
if (!localAid || !group || typeof this._tokenStore.upsertGroupIndexCache !== "function") return;
|
|
20617
|
+
const record = await this._tokenStore.upsertGroupIndexCache(localAid, group, fields);
|
|
20618
|
+
this._groupIndexCacheLoaded.add(this._groupIndexCacheKey(group));
|
|
20619
|
+
this._groupIndexMetaCache.restore(localAid, group, {
|
|
20620
|
+
remote_meta: record.remote_meta,
|
|
20621
|
+
local_etag: record.local_etag,
|
|
20622
|
+
settings: record.settings,
|
|
20623
|
+
entry_etags: record.entry_etags
|
|
20624
|
+
});
|
|
19851
20625
|
}
|
|
19852
20626
|
get state() {
|
|
19853
20627
|
return this._publicState(this._state);
|
|
@@ -20011,11 +20785,11 @@ var _AUNClient = class _AUNClient {
|
|
|
20011
20785
|
timeout: DEFAULT_SESSION_OPTIONS.timeouts.call,
|
|
20012
20786
|
onDisconnect: (error, closeCode) => this._handleTransportDisconnect(error, closeCode)
|
|
20013
20787
|
});
|
|
20014
|
-
this._transport.setMetaObserver(
|
|
20015
|
-
|
|
20016
|
-
this._clientLog.debug(`
|
|
20017
|
-
})
|
|
20018
|
-
|
|
20788
|
+
this._transport.setMetaObserver(
|
|
20789
|
+
(meta) => this._observeRpcMeta(meta).catch((exc) => {
|
|
20790
|
+
this._clientLog.debug(`rpc meta observer skipped: ${String(exc)}`);
|
|
20791
|
+
})
|
|
20792
|
+
);
|
|
20019
20793
|
this._auth.setLogger(this._logAuth);
|
|
20020
20794
|
this._transport.setLogger(this._logTransport);
|
|
20021
20795
|
this._dispatcher.setLogger(this._logEvents);
|
|
@@ -20245,8 +21019,8 @@ var _AUNClient = class _AUNClient {
|
|
|
20245
21019
|
}
|
|
20246
21020
|
const nonce = globalThis.crypto.randomUUID().replace(/-/g, "");
|
|
20247
21021
|
const issuedMs = Date.now();
|
|
20248
|
-
const oldHash =
|
|
20249
|
-
const newHash =
|
|
21022
|
+
const oldHash = bytesToHex8(new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(oldPublicKey))));
|
|
21023
|
+
const newHash = bytesToHex8(new Uint8Array(await globalThis.crypto.subtle.digest("SHA-256", new TextEncoder().encode(newPublicKey))));
|
|
20250
21024
|
const canonical = [
|
|
20251
21025
|
"aun-group-aid-renew-v1",
|
|
20252
21026
|
groupId.toLowerCase(),
|
|
@@ -20365,7 +21139,7 @@ var _AUNClient = class _AUNClient {
|
|
|
20365
21139
|
}
|
|
20366
21140
|
const nonce = globalThis.crypto.randomUUID().replace(/-/g, "");
|
|
20367
21141
|
const issuedMs = Date.now();
|
|
20368
|
-
const publicKeyHash =
|
|
21142
|
+
const publicKeyHash = bytesToHex8(new Uint8Array(
|
|
20369
21143
|
await globalThis.crypto.subtle.digest(
|
|
20370
21144
|
"SHA-256",
|
|
20371
21145
|
new TextEncoder().encode(keyPair.public_key_der_b64)
|
|
@@ -20536,6 +21310,9 @@ var _AUNClient = class _AUNClient {
|
|
|
20536
21310
|
_onRawMessageReceived(data) {
|
|
20537
21311
|
this._delivery.onRawMessageReceived(data);
|
|
20538
21312
|
}
|
|
21313
|
+
async _onRawMessageRecalled(data) {
|
|
21314
|
+
return this._delivery.onRawMessageRecalled(data);
|
|
21315
|
+
}
|
|
20539
21316
|
/** 处理群组消息推送:re-publish(V2 加密消息走 V2 push 路径) */
|
|
20540
21317
|
_onRawGroupMessageCreated(data) {
|
|
20541
21318
|
return this._delivery.onRawGroupMessageCreated(data);
|
|
@@ -21585,7 +22362,7 @@ var _AUNClient = class _AUNClient {
|
|
|
21585
22362
|
if (!args.spkPkDer || args.spkPkDer.length === 0) {
|
|
21586
22363
|
throw new E2EEError(`spk_public_key_missing: aid=${args.aid} device_id=${args.deviceId} spk_id=${spkId}`);
|
|
21587
22364
|
}
|
|
21588
|
-
const spkHash =
|
|
22365
|
+
const spkHash = bytesToHex8(new Uint8Array(await crypto.subtle.digest("SHA-256", exactArrayBuffer3(args.spkPkDer))));
|
|
21589
22366
|
const expectedSpkId = `sha256:${spkHash.substring(0, 16)}`;
|
|
21590
22367
|
if (spkId !== expectedSpkId) {
|
|
21591
22368
|
throw new E2EEError(`spk_id_mismatch: aid=${args.aid} device_id=${args.deviceId} spk_id=${spkId} expected=${expectedSpkId}`);
|
|
@@ -22310,7 +23087,7 @@ var ServiceProxyClient = class {
|
|
|
22310
23087
|
provider_aid: this.providerAid,
|
|
22311
23088
|
services: services ?? this.listServiceSummaries()
|
|
22312
23089
|
});
|
|
22313
|
-
if (!
|
|
23090
|
+
if (!isRecord5(result)) return {};
|
|
22314
23091
|
if (result.ok === false) throw new ValidationError(String(result.error ?? "Gateway service registration failed"));
|
|
22315
23092
|
return result;
|
|
22316
23093
|
}
|
|
@@ -22323,7 +23100,7 @@ var ServiceProxyClient = class {
|
|
|
22323
23100
|
if (typeof serviceNames === "string") params2.service_names = [serviceNames];
|
|
22324
23101
|
else if (Array.isArray(serviceNames)) params2.service_names = serviceNames.map(String);
|
|
22325
23102
|
const result = await call("proxy.unregister_services", params2);
|
|
22326
|
-
return
|
|
23103
|
+
return isRecord5(result) ? result : {};
|
|
22327
23104
|
}
|
|
22328
23105
|
unregister_services_from_gateway(serviceNames) {
|
|
22329
23106
|
return this.unregisterServicesFromGateway(serviceNames);
|
|
@@ -22331,7 +23108,7 @@ var ServiceProxyClient = class {
|
|
|
22331
23108
|
async listGatewayServices() {
|
|
22332
23109
|
const call = this._gatewayCallMethod(true);
|
|
22333
23110
|
const result = await call("proxy.list_services", { provider_aid: this.providerAid });
|
|
22334
|
-
return
|
|
23111
|
+
return isRecord5(result) ? result : {};
|
|
22335
23112
|
}
|
|
22336
23113
|
list_gateway_services() {
|
|
22337
23114
|
return this.listGatewayServices();
|
|
@@ -22379,7 +23156,7 @@ var ServiceProxyClient = class {
|
|
|
22379
23156
|
});
|
|
22380
23157
|
const authResponse = parseTunnelMessage(await tunnel.recv());
|
|
22381
23158
|
if (!authResponse.ok) {
|
|
22382
|
-
const err =
|
|
23159
|
+
const err = isRecord5(authResponse.error) ? authResponse.error : {};
|
|
22383
23160
|
throw new AuthError(String(err.message ?? "Service Proxy auth failed"));
|
|
22384
23161
|
}
|
|
22385
23162
|
const registered = await this.registerServicesWithProxyServer(tunnel, {
|
|
@@ -22531,7 +23308,7 @@ var ServiceProxyClient = class {
|
|
|
22531
23308
|
return;
|
|
22532
23309
|
}
|
|
22533
23310
|
}
|
|
22534
|
-
const headers = backendHeaders(
|
|
23311
|
+
const headers = backendHeaders(isRecord5(message.headers) ? message.headers : {});
|
|
22535
23312
|
let response;
|
|
22536
23313
|
try {
|
|
22537
23314
|
const init = { method, headers };
|
|
@@ -22607,7 +23384,7 @@ var ServiceProxyClient = class {
|
|
|
22607
23384
|
backend = this._createWebSocket(
|
|
22608
23385
|
buildTargetUrl(record.endpoint, normalizePath2(String(message.path ?? "/")), String(message.query_string ?? "")),
|
|
22609
23386
|
protocols,
|
|
22610
|
-
{ headers: backendHeaders(
|
|
23387
|
+
{ headers: backendHeaders(isRecord5(message.headers) ? message.headers : {}), verifySsl: this._shouldVerifySsl() },
|
|
22611
23388
|
false
|
|
22612
23389
|
);
|
|
22613
23390
|
await waitForWsOpen(backend);
|
|
@@ -22674,7 +23451,7 @@ var ServiceProxyClient = class {
|
|
|
22674
23451
|
await this._autoRegisterServicesWithGateway();
|
|
22675
23452
|
const queue = new AsyncQueue();
|
|
22676
23453
|
const subscription = client.on("app.service_proxy.wakeup", (payload) => {
|
|
22677
|
-
if (!
|
|
23454
|
+
if (!isRecord5(payload)) return;
|
|
22678
23455
|
if (String(payload.type ?? "") !== "aun.service_proxy.wakeup") return;
|
|
22679
23456
|
const providerAid = String(payload.provider_aid ?? "").trim();
|
|
22680
23457
|
if (providerAid && providerAid !== this.providerAid) return;
|
|
@@ -22730,7 +23507,7 @@ var ServiceProxyClient = class {
|
|
|
22730
23507
|
let message;
|
|
22731
23508
|
try {
|
|
22732
23509
|
const parsed = JSON.parse(raw);
|
|
22733
|
-
if (!
|
|
23510
|
+
if (!isRecord5(parsed)) continue;
|
|
22734
23511
|
message = parsed;
|
|
22735
23512
|
} catch {
|
|
22736
23513
|
continue;
|
|
@@ -22772,7 +23549,7 @@ var ServiceProxyClient = class {
|
|
|
22772
23549
|
await tunnel.send({ type: "service_proxy_auth", request_id: authRequestId, provider_aid: this.providerAid, client_version: "js" });
|
|
22773
23550
|
const authResponse = parseTunnelMessage(await tunnel.recv());
|
|
22774
23551
|
if (!authResponse.ok) {
|
|
22775
|
-
const err =
|
|
23552
|
+
const err = isRecord5(authResponse.error) ? authResponse.error : {};
|
|
22776
23553
|
throw new AuthError(String(err.message ?? "Service Proxy auth failed"));
|
|
22777
23554
|
}
|
|
22778
23555
|
return this.registerServicesWithProxyServer(tunnel, { registerRequestId });
|
|
@@ -22790,7 +23567,7 @@ var ServiceProxyClient = class {
|
|
|
22790
23567
|
}
|
|
22791
23568
|
if (msgType !== "service_proxy_request_body") throw new Error("invalid_body_stream");
|
|
22792
23569
|
if (String(message.request_id ?? "") !== requestId) throw new Error("request body stream request_id mismatch");
|
|
22793
|
-
if (
|
|
23570
|
+
if (isRecord5(message.error)) throw new Error(String(message.error.message ?? "request body stream failed"));
|
|
22794
23571
|
const dataText = String(message.data_base64 ?? "");
|
|
22795
23572
|
if (dataText) yield decodeBase64Strict(dataText);
|
|
22796
23573
|
if (message.done === true) return;
|
|
@@ -22859,7 +23636,7 @@ var ServiceProxyClient = class {
|
|
|
22859
23636
|
_selectProxyWsUrl(payload) {
|
|
22860
23637
|
const direct = this._normalizeProxyWsUrl(String(payload.ws_url ?? ""));
|
|
22861
23638
|
if (direct) return direct;
|
|
22862
|
-
const servers = Array.isArray(payload.proxy_servers) ? payload.proxy_servers.filter(
|
|
23639
|
+
const servers = Array.isArray(payload.proxy_servers) ? payload.proxy_servers.filter(isRecord5) : [];
|
|
22863
23640
|
servers.sort((a, b) => Number(a.priority ?? 999) - Number(b.priority ?? 999));
|
|
22864
23641
|
for (const item of servers) {
|
|
22865
23642
|
const url = this._normalizeProxyWsUrl(String(item.ws_url ?? ""));
|
|
@@ -22874,7 +23651,7 @@ var ServiceProxyClient = class {
|
|
|
22874
23651
|
const response = await fetch(wellKnownUrl, { signal: controller.signal });
|
|
22875
23652
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
22876
23653
|
const payload = await response.json();
|
|
22877
|
-
if (!
|
|
23654
|
+
if (!isRecord5(payload)) throw new ValidationError("Service Proxy well-known returned invalid payload");
|
|
22878
23655
|
const wsUrl = this._selectProxyWsUrl(payload);
|
|
22879
23656
|
if (!wsUrl) throw new ValidationError("Service Proxy well-known missing valid ws_url");
|
|
22880
23657
|
return { ...payload, ws_url: wsUrl, source_url: wellKnownUrl, discovered_at: Date.now() / 1e3 };
|
|
@@ -22890,7 +23667,7 @@ var ServiceProxyClient = class {
|
|
|
22890
23667
|
if (typeof tokenStore.getMetadata === "function") raw = await tokenStore.getMetadata(this.providerAid, PROXY_DISCOVERY_CACHE_KEY);
|
|
22891
23668
|
else if (typeof tokenStore.loadMetadata === "function") raw = (await tokenStore.loadMetadata(this.providerAid))?.[PROXY_DISCOVERY_CACHE_KEY];
|
|
22892
23669
|
const cached = typeof raw === "string" ? JSON.parse(raw) : raw;
|
|
22893
|
-
if (!
|
|
23670
|
+
if (!isRecord5(cached)) return null;
|
|
22894
23671
|
const wsUrl = this._normalizeProxyWsUrl(String(cached.ws_url ?? ""));
|
|
22895
23672
|
if (!wsUrl) return null;
|
|
22896
23673
|
const discoveredAt = Number(cached.discovered_at ?? 0);
|
|
@@ -22930,7 +23707,7 @@ var ServiceProxyClient = class {
|
|
|
22930
23707
|
if (!client) return "";
|
|
22931
23708
|
const direct = this._mappingAccessToken(client);
|
|
22932
23709
|
if (direct) return direct;
|
|
22933
|
-
if (
|
|
23710
|
+
if (isRecord5(client._identity)) {
|
|
22934
23711
|
const token = this._mappingAccessToken(client._identity);
|
|
22935
23712
|
if (token) return token;
|
|
22936
23713
|
}
|
|
@@ -22963,7 +23740,7 @@ var ServiceProxyClient = class {
|
|
|
22963
23740
|
} catch (exc) {
|
|
22964
23741
|
throw new AuthError(`AUNClient authenticate failed for Service Proxy tunnel: ${formatError(exc)}`);
|
|
22965
23742
|
}
|
|
22966
|
-
const token = this._mappingAccessToken(
|
|
23743
|
+
const token = this._mappingAccessToken(isRecord5(result) ? result : null);
|
|
22967
23744
|
if (token) return token;
|
|
22968
23745
|
throw new AuthError("AUNClient authenticate did not return a valid access_token");
|
|
22969
23746
|
}
|
|
@@ -22992,7 +23769,7 @@ function isIPv4LoopbackHost(host) {
|
|
|
22992
23769
|
if (parts.length !== 4 || parts[0] !== "127") return false;
|
|
22993
23770
|
return parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) >= 0 && Number(part) <= 255);
|
|
22994
23771
|
}
|
|
22995
|
-
function
|
|
23772
|
+
function isRecord5(value) {
|
|
22996
23773
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
22997
23774
|
}
|
|
22998
23775
|
function isSensitiveMetadataKey(key) {
|
|
@@ -23003,15 +23780,15 @@ function sanitizeMetadata(metadata) {
|
|
|
23003
23780
|
const out = {};
|
|
23004
23781
|
for (const [key, value] of Object.entries(metadata ?? {})) {
|
|
23005
23782
|
if (isSensitiveMetadataKey(key)) continue;
|
|
23006
|
-
if (
|
|
23007
|
-
else if (Array.isArray(value)) out[key] = value.map((item) =>
|
|
23783
|
+
if (isRecord5(value)) out[key] = sanitizeMetadata(value);
|
|
23784
|
+
else if (Array.isArray(value)) out[key] = value.map((item) => isRecord5(item) ? sanitizeMetadata(item) : item);
|
|
23008
23785
|
else out[key] = value;
|
|
23009
23786
|
}
|
|
23010
23787
|
return out;
|
|
23011
23788
|
}
|
|
23012
23789
|
function headersMap(headers) {
|
|
23013
23790
|
const result = {};
|
|
23014
|
-
if (!
|
|
23791
|
+
if (!isRecord5(headers)) return result;
|
|
23015
23792
|
for (const [key, value] of Object.entries(headers)) result[key.toLowerCase()] = String(value);
|
|
23016
23793
|
return result;
|
|
23017
23794
|
}
|
|
@@ -23023,7 +23800,7 @@ function streamModeFrom(headers, record, message) {
|
|
|
23023
23800
|
return VALID_STREAM_MODES.has(value) ? value : "auto";
|
|
23024
23801
|
}
|
|
23025
23802
|
function detectRequestProtocol(message, record) {
|
|
23026
|
-
const headers = headersMap(
|
|
23803
|
+
const headers = headersMap(isRecord5(message.headers) ? message.headers : {});
|
|
23027
23804
|
const streamMode = streamModeFrom(headers, record, message);
|
|
23028
23805
|
let serviceType = String(message.service_type ?? "").trim().toLowerCase() || record.service_type.toLowerCase() || "http";
|
|
23029
23806
|
if (streamMode === "no_stream") {
|
|
@@ -23063,8 +23840,8 @@ function bodyHasJsonRpc(message) {
|
|
|
23063
23840
|
if (text3.includes('"jsonrpc"') || text3.includes("'jsonrpc'")) return true;
|
|
23064
23841
|
try {
|
|
23065
23842
|
const parsed = JSON.parse(text3);
|
|
23066
|
-
if (
|
|
23067
|
-
if (Array.isArray(parsed)) return parsed.some((item) =>
|
|
23843
|
+
if (isRecord5(parsed)) return String(parsed.jsonrpc ?? "") === "2.0";
|
|
23844
|
+
if (Array.isArray(parsed)) return parsed.some((item) => isRecord5(item) && String(item.jsonrpc ?? "") === "2.0");
|
|
23068
23845
|
} catch {
|
|
23069
23846
|
}
|
|
23070
23847
|
return false;
|
|
@@ -23130,7 +23907,7 @@ function streamMessage(requestId, index, status, headers, data, done) {
|
|
|
23130
23907
|
function parseTunnelMessage(raw) {
|
|
23131
23908
|
if (raw === null) throw new ConnectionError("Service Proxy tunnel closed");
|
|
23132
23909
|
const parsed = JSON.parse(raw);
|
|
23133
|
-
return
|
|
23910
|
+
return isRecord5(parsed) ? parsed : {};
|
|
23134
23911
|
}
|
|
23135
23912
|
function waitForWsOpen(ws) {
|
|
23136
23913
|
return new Promise((resolve, reject) => {
|
|
@@ -23307,10 +24084,14 @@ export {
|
|
|
23307
24084
|
EmbeddedServiceRegistry,
|
|
23308
24085
|
EndpointPolicy,
|
|
23309
24086
|
EventDispatcher,
|
|
24087
|
+
GROUP_INDEX_KEY,
|
|
24088
|
+
GROUP_INDEX_SCHEMA,
|
|
24089
|
+
GROUP_INDEX_SIG_ALG,
|
|
23310
24090
|
GatewayDiscovery,
|
|
23311
24091
|
GroupError,
|
|
23312
24092
|
GroupFSVFS,
|
|
23313
24093
|
GroupFacade,
|
|
24094
|
+
GroupIndexMetaCache,
|
|
23314
24095
|
GroupNotFoundError,
|
|
23315
24096
|
GroupStateError,
|
|
23316
24097
|
GroupThoughtFacade,
|
|
@@ -23350,6 +24131,8 @@ export {
|
|
|
23350
24131
|
ValidationError,
|
|
23351
24132
|
VERSION as __version__,
|
|
23352
24133
|
buildDiscoveryHost,
|
|
24134
|
+
buildSignedGroupIndex,
|
|
24135
|
+
computeGroupIndexBodyHash,
|
|
23353
24136
|
computeStateCommitment,
|
|
23354
24137
|
convertToGroupAid,
|
|
23355
24138
|
createConfig,
|
|
@@ -23358,18 +24141,23 @@ export {
|
|
|
23358
24141
|
encryptGroupMessage,
|
|
23359
24142
|
encryptP2PMessage,
|
|
23360
24143
|
getDeviceId,
|
|
24144
|
+
groupIndexEtag,
|
|
24145
|
+
groupIndexSigningPayload,
|
|
23361
24146
|
isGroupRemotePath,
|
|
23362
24147
|
isJsonObject,
|
|
23363
24148
|
mapCollabError,
|
|
23364
24149
|
mapRemoteError,
|
|
23365
24150
|
normalizeGroupAid,
|
|
23366
24151
|
normalizeGroupId,
|
|
24152
|
+
parseGroupIndex,
|
|
24153
|
+
prepareGroupSettingsWithIndex,
|
|
23367
24154
|
resultErr,
|
|
23368
24155
|
resultOk,
|
|
23369
24156
|
splitGroupId,
|
|
23370
24157
|
validateAIDFormat,
|
|
23371
24158
|
validateGroupAIDFormat,
|
|
23372
|
-
validateGroupIDFormat
|
|
24159
|
+
validateGroupIDFormat,
|
|
24160
|
+
verifyGroupIndex
|
|
23373
24161
|
};
|
|
23374
24162
|
/*! Bundled license information:
|
|
23375
24163
|
|