@agentunion/fastaun-browser 0.4.10 → 0.4.12
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 +50 -0
- package/_packed_docs/CHANGELOG.md +50 -0
- package/_packed_docs/sdk/05-E2EE/345/212/240/345/257/206/351/200/232/344/277/241.md +4 -2
- package/_packed_docs/sdk/06-API/346/211/213/345/206/214.md +2 -2
- package/_packed_docs/sdk/07-/351/224/231/350/257/257/345/244/204/347/220/206.md +7 -4
- package/_packed_docs/sdk/09-group-rpc-manual.md +222 -38
- package/_packed_docs/sdk/09-message-rpc-manual.md +50 -26
- package/_packed_docs/sdk/09-payload-reference.md +1 -1
- package/_packed_docs/sdk/09-storage-rpc-manual.md +259 -37
- package/dist/bundle.js +320 -50
- package/dist/client/delivery.d.ts +10 -1
- package/dist/client/delivery.d.ts.map +1 -1
- package/dist/client/delivery.js +166 -20
- package/dist/client/delivery.js.map +1 -1
- package/dist/client/group-state.js +2 -2
- package/dist/client/group-state.js.map +1 -1
- package/dist/client/rpc-pipeline.d.ts.map +1 -1
- package/dist/client/rpc-pipeline.js +31 -5
- package/dist/client/rpc-pipeline.js.map +1 -1
- package/dist/client/v2-e2ee.d.ts.map +1 -1
- package/dist/client/v2-e2ee.js +9 -2
- package/dist/client/v2-e2ee.js.map +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +55 -14
- package/dist/client.js.map +1 -1
- package/dist/service-proxy.d.ts.map +1 -1
- package/dist/service-proxy.js +17 -2
- package/dist/service-proxy.js.map +1 -1
- package/dist/v2/e2ee/encrypt-p2p.js +1 -1
- package/dist/v2/e2ee/encrypt-p2p.js.map +1 -1
- package/dist/v2/session/keystore.d.ts.map +1 -1
- package/dist/v2/session/keystore.js +8 -9
- package/dist/v2/session/keystore.js.map +1 -1
- package/dist/v2/session/session.d.ts +4 -2
- package/dist/v2/session/session.d.ts.map +1 -1
- package/dist/v2/session/session.js +20 -4
- package/dist/v2/session/session.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
|
@@ -454,7 +454,7 @@ var init_indexeddb_store = __esm({
|
|
|
454
454
|
});
|
|
455
455
|
|
|
456
456
|
// src/version.ts
|
|
457
|
-
var VERSION = "0.4.
|
|
457
|
+
var VERSION = "0.4.12";
|
|
458
458
|
|
|
459
459
|
// src/types.ts
|
|
460
460
|
var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
|
|
@@ -3710,6 +3710,53 @@ var ClientRuntime = class {
|
|
|
3710
3710
|
var PUSHED_SEQS_LIMIT = 5e4;
|
|
3711
3711
|
var PENDING_ORDERED_LIMIT = 5e4;
|
|
3712
3712
|
var GROUP_RECALL_SEEN_LIMIT = 1e4;
|
|
3713
|
+
var APP_MESSAGE_ENVELOPE_KEYS = [
|
|
3714
|
+
"module_id",
|
|
3715
|
+
"message_id",
|
|
3716
|
+
"id",
|
|
3717
|
+
"seq",
|
|
3718
|
+
"msg_seq",
|
|
3719
|
+
"message_type",
|
|
3720
|
+
"type",
|
|
3721
|
+
"kind",
|
|
3722
|
+
"version",
|
|
3723
|
+
"from",
|
|
3724
|
+
"from_aid",
|
|
3725
|
+
"sender_aid",
|
|
3726
|
+
"to",
|
|
3727
|
+
"to_aid",
|
|
3728
|
+
"group_id",
|
|
3729
|
+
"timestamp",
|
|
3730
|
+
"created_at",
|
|
3731
|
+
"t_server",
|
|
3732
|
+
"encrypted",
|
|
3733
|
+
"status",
|
|
3734
|
+
"delivery_mode",
|
|
3735
|
+
"dispatch_mode",
|
|
3736
|
+
"dispatch",
|
|
3737
|
+
"device_id",
|
|
3738
|
+
"slot_id"
|
|
3739
|
+
];
|
|
3740
|
+
var APP_GROUP_EVENT_ENVELOPE_KEYS = [
|
|
3741
|
+
"module_id",
|
|
3742
|
+
"event_id",
|
|
3743
|
+
"event_seq",
|
|
3744
|
+
"seq",
|
|
3745
|
+
"event_type",
|
|
3746
|
+
"action",
|
|
3747
|
+
"group_id",
|
|
3748
|
+
"actor_aid",
|
|
3749
|
+
"sender_aid",
|
|
3750
|
+
"member_aid",
|
|
3751
|
+
"target_aid",
|
|
3752
|
+
"operator_aid",
|
|
3753
|
+
"created_at",
|
|
3754
|
+
"timestamp",
|
|
3755
|
+
"t_server",
|
|
3756
|
+
"status",
|
|
3757
|
+
"device_id",
|
|
3758
|
+
"slot_id"
|
|
3759
|
+
];
|
|
3713
3760
|
function formatDeliveryError(error) {
|
|
3714
3761
|
return error instanceof Error ? error : String(error);
|
|
3715
3762
|
}
|
|
@@ -3753,6 +3800,29 @@ var MessageDeliveryEngine = class {
|
|
|
3753
3800
|
for (const oldSeq of drop) queue.delete(oldSeq);
|
|
3754
3801
|
}
|
|
3755
3802
|
}
|
|
3803
|
+
isGroupEventNamespace(ns) {
|
|
3804
|
+
return ns.startsWith("group_event:");
|
|
3805
|
+
}
|
|
3806
|
+
async publishOrderedQueueItem(ns, event, seq, payload) {
|
|
3807
|
+
const client = this.runtime.client;
|
|
3808
|
+
if (event === "group.changed" && this.isGroupEventNamespace(ns)) {
|
|
3809
|
+
await this.publishOrderedGroupChanged(payload);
|
|
3810
|
+
return;
|
|
3811
|
+
}
|
|
3812
|
+
await client._publishAppEvent(event, payload);
|
|
3813
|
+
}
|
|
3814
|
+
async publishOrderedGroupChanged(payload) {
|
|
3815
|
+
const client = this.runtime.client;
|
|
3816
|
+
if (isJsonObject(payload)) {
|
|
3817
|
+
const eventPayload = payload;
|
|
3818
|
+
client._groupState?.handleGroupChangedV2Membership?.(eventPayload);
|
|
3819
|
+
if (eventPayload.action === "dissolved") {
|
|
3820
|
+
const groupId = normalizeGroupId(String(eventPayload.group_id ?? "")) || String(eventPayload.group_id ?? "").trim();
|
|
3821
|
+
if (groupId) client._cleanupDissolvedGroup?.(groupId);
|
|
3822
|
+
}
|
|
3823
|
+
}
|
|
3824
|
+
await client._publishAppEvent("group.changed", payload);
|
|
3825
|
+
}
|
|
3756
3826
|
isInstanceScopedMessageEvent(event) {
|
|
3757
3827
|
return event === "message.received" || event === "message.recalled" || event === "message.undecryptable" || event === "group.message_created" || event === "group.message_recalled" || event === "group.message_undecryptable";
|
|
3758
3828
|
}
|
|
@@ -3769,8 +3839,46 @@ var MessageDeliveryEngine = class {
|
|
|
3769
3839
|
return result;
|
|
3770
3840
|
}
|
|
3771
3841
|
normalizePublishedMessagePayload(event, payload) {
|
|
3772
|
-
if (
|
|
3773
|
-
|
|
3842
|
+
if (this.isInstanceScopedMessageEvent(event)) {
|
|
3843
|
+
return this.attachAppMessageEnvelope(this.stripInternalSenderDeviceFields(this.attachCurrentInstanceContext(payload)));
|
|
3844
|
+
}
|
|
3845
|
+
if (this.isGroupScopedEvent(event)) {
|
|
3846
|
+
return this.attachAppGroupEventEnvelope(this.attachCurrentInstanceContext(payload));
|
|
3847
|
+
}
|
|
3848
|
+
return payload;
|
|
3849
|
+
}
|
|
3850
|
+
appMessageEnvelope(payload) {
|
|
3851
|
+
if (!isJsonObject(payload)) return {};
|
|
3852
|
+
const message = payload;
|
|
3853
|
+
const envelope = {};
|
|
3854
|
+
for (const key of APP_MESSAGE_ENVELOPE_KEYS) {
|
|
3855
|
+
if (Object.prototype.hasOwnProperty.call(message, key)) envelope[key] = message[key];
|
|
3856
|
+
}
|
|
3857
|
+
return envelope;
|
|
3858
|
+
}
|
|
3859
|
+
isGroupScopedEvent(event) {
|
|
3860
|
+
return event === "group.changed";
|
|
3861
|
+
}
|
|
3862
|
+
appGroupEventEnvelope(payload) {
|
|
3863
|
+
if (!isJsonObject(payload)) return {};
|
|
3864
|
+
const groupEvent = payload;
|
|
3865
|
+
const envelope = {};
|
|
3866
|
+
for (const key of APP_GROUP_EVENT_ENVELOPE_KEYS) {
|
|
3867
|
+
if (Object.prototype.hasOwnProperty.call(groupEvent, key)) envelope[key] = groupEvent[key];
|
|
3868
|
+
}
|
|
3869
|
+
return envelope;
|
|
3870
|
+
}
|
|
3871
|
+
attachAppMessageEnvelope(payload) {
|
|
3872
|
+
if (!isJsonObject(payload)) return payload;
|
|
3873
|
+
const result = { ...payload };
|
|
3874
|
+
result.envelope = this.appMessageEnvelope(result);
|
|
3875
|
+
return result;
|
|
3876
|
+
}
|
|
3877
|
+
attachAppGroupEventEnvelope(payload) {
|
|
3878
|
+
if (!isJsonObject(payload)) return payload;
|
|
3879
|
+
const result = { ...payload };
|
|
3880
|
+
result.envelope = this.appGroupEventEnvelope(result);
|
|
3881
|
+
return result;
|
|
3774
3882
|
}
|
|
3775
3883
|
stripInternalSenderDeviceFields(payload) {
|
|
3776
3884
|
if (!isJsonObject(payload)) return payload;
|
|
@@ -3790,6 +3898,11 @@ var MessageDeliveryEngine = class {
|
|
|
3790
3898
|
const payloadType = String(payload.type ?? payload.kind ?? "").trim();
|
|
3791
3899
|
if (msgType !== "message.recalled" && payloadType !== "message.recalled") return null;
|
|
3792
3900
|
const event = { ...payload };
|
|
3901
|
+
for (const key of APP_MESSAGE_ENVELOPE_KEYS) {
|
|
3902
|
+
if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
|
|
3903
|
+
event[key] = msg[key];
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3793
3906
|
const rawIds = event.message_ids;
|
|
3794
3907
|
let messageIds = Array.isArray(rawIds) ? rawIds.map((item) => String(item ?? "").trim()).filter(Boolean) : [];
|
|
3795
3908
|
if (messageIds.length === 0) {
|
|
@@ -3808,7 +3921,10 @@ var MessageDeliveryEngine = class {
|
|
|
3808
3921
|
if (!("to" in event)) event.to = msg.to ?? msg.to_aid ?? "";
|
|
3809
3922
|
if (!("timestamp" in event)) event.timestamp = msg.timestamp ?? msg.t_server ?? event.recalled_at ?? 0;
|
|
3810
3923
|
if ("seq" in msg && !("seq" in event)) event.seq = msg.seq;
|
|
3811
|
-
if ("message_id" in msg
|
|
3924
|
+
if ("message_id" in msg) {
|
|
3925
|
+
event.message_id = msg.message_id;
|
|
3926
|
+
if (!("tombstone_message_id" in event)) event.tombstone_message_id = msg.message_id;
|
|
3927
|
+
}
|
|
3812
3928
|
if ("device_id" in msg && !("device_id" in event)) event.device_id = msg.device_id;
|
|
3813
3929
|
if ("slot_id" in msg && !("slot_id" in event)) event.slot_id = msg.slot_id;
|
|
3814
3930
|
return event;
|
|
@@ -3827,6 +3943,11 @@ var MessageDeliveryEngine = class {
|
|
|
3827
3943
|
const payloadType = String(payload.type ?? payload.kind ?? "").trim();
|
|
3828
3944
|
if (msgType !== "group.message_recalled" && payloadType !== "group.message_recalled") return null;
|
|
3829
3945
|
const event = { ...payload };
|
|
3946
|
+
for (const key of APP_MESSAGE_ENVELOPE_KEYS) {
|
|
3947
|
+
if (Object.prototype.hasOwnProperty.call(msg, key) && !(key in event)) {
|
|
3948
|
+
event[key] = msg[key];
|
|
3949
|
+
}
|
|
3950
|
+
}
|
|
3830
3951
|
const rawIds = event.message_ids;
|
|
3831
3952
|
let messageIds = Array.isArray(rawIds) ? rawIds.map((item) => String(item ?? "").trim()).filter(Boolean) : [];
|
|
3832
3953
|
if (messageIds.length === 0) {
|
|
@@ -3844,7 +3965,10 @@ var MessageDeliveryEngine = class {
|
|
|
3844
3965
|
if (!("group_id" in event)) event.group_id = msg.group_id ?? "";
|
|
3845
3966
|
if (!("timestamp" in event)) event.timestamp = msg.timestamp ?? msg.t_server ?? event.recalled_at ?? 0;
|
|
3846
3967
|
if ("seq" in msg) event.seq = msg.seq;
|
|
3847
|
-
if ("message_id" in msg
|
|
3968
|
+
if ("message_id" in msg) {
|
|
3969
|
+
event.message_id = msg.message_id;
|
|
3970
|
+
if (!("tombstone_message_id" in event)) event.tombstone_message_id = msg.message_id;
|
|
3971
|
+
}
|
|
3848
3972
|
return event;
|
|
3849
3973
|
}
|
|
3850
3974
|
groupRecallDedupKey(groupId, payload) {
|
|
@@ -4273,10 +4397,12 @@ var MessageDeliveryEngine = class {
|
|
|
4273
4397
|
}
|
|
4274
4398
|
}
|
|
4275
4399
|
const eventSeqs = [];
|
|
4400
|
+
let hasDissolvedEvent = false;
|
|
4276
4401
|
for (const evt of eventObjects) {
|
|
4277
4402
|
const eventSeq = Number(evt.event_seq ?? 0);
|
|
4278
4403
|
if (Number.isFinite(eventSeq) && eventSeq > 0) eventSeqs.push(eventSeq);
|
|
4279
4404
|
evt._from_gap_fill = true;
|
|
4405
|
+
if (evt.action === "dissolved") hasDissolvedEvent = true;
|
|
4280
4406
|
const et = String(evt.event_type ?? "");
|
|
4281
4407
|
if (et === "group.message_created") continue;
|
|
4282
4408
|
const cs = evt.client_signature;
|
|
@@ -4284,17 +4410,21 @@ var MessageDeliveryEngine = class {
|
|
|
4284
4410
|
if (client._shouldSkipEventSignature(evt)) {
|
|
4285
4411
|
delete evt.client_signature;
|
|
4286
4412
|
} else {
|
|
4287
|
-
|
|
4413
|
+
const verified = await client._verifyEventSignature(evt, cs);
|
|
4414
|
+
evt._verified = client._isEventSignatureVerified(verified);
|
|
4288
4415
|
}
|
|
4289
4416
|
}
|
|
4290
|
-
|
|
4417
|
+
if (Number.isFinite(eventSeq) && eventSeq > 0 && !client._pushedSeqs.get(ns)?.has(eventSeq)) {
|
|
4418
|
+
this.enqueueOrderedMessage(ns, "group.changed", eventSeq, evt);
|
|
4419
|
+
}
|
|
4291
4420
|
}
|
|
4292
|
-
const
|
|
4293
|
-
|
|
4421
|
+
const ackContig = client._seqTracker.getContiguousSeq(ns);
|
|
4422
|
+
await this.drainOrderedMessages(ns);
|
|
4423
|
+
if (ackContig !== pageContigBefore && !hasDissolvedEvent) {
|
|
4294
4424
|
this.saveSeqTrackerState();
|
|
4295
4425
|
}
|
|
4296
|
-
if (eventObjects.length > 0 &&
|
|
4297
|
-
const ackSeq = this.clampAckSeq("group.ack_events", "event_seq", ns,
|
|
4426
|
+
if (eventObjects.length > 0 && ackContig > 0 && ackContig !== pageContigBefore) {
|
|
4427
|
+
const ackSeq = this.clampAckSeq("group.ack_events", "event_seq", ns, ackContig);
|
|
4298
4428
|
client._transport.call("group.ack_events", {
|
|
4299
4429
|
group_id: groupId,
|
|
4300
4430
|
event_seq: ackSeq,
|
|
@@ -4318,20 +4448,59 @@ var MessageDeliveryEngine = class {
|
|
|
4318
4448
|
this.runtime.delivery.setGapFillActive(false);
|
|
4319
4449
|
}
|
|
4320
4450
|
}
|
|
4321
|
-
handleGroupChangedEventSeq(data, groupId) {
|
|
4451
|
+
async handleGroupChangedEventSeq(data, groupId) {
|
|
4322
4452
|
const client = this.runtime.client;
|
|
4323
4453
|
let needPull = false;
|
|
4324
4454
|
const rawEventSeq = data.event_seq;
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4455
|
+
const eventSeq = Number(rawEventSeq);
|
|
4456
|
+
if (!groupId || !Number.isFinite(eventSeq) || !Number.isInteger(eventSeq) || eventSeq <= 0) {
|
|
4457
|
+
await this.publishOrderedGroupChanged(data);
|
|
4458
|
+
return;
|
|
4459
|
+
}
|
|
4460
|
+
const ns = `group_event:${groupId}`;
|
|
4461
|
+
if (this.isSelfJoinGroupChanged(data)) {
|
|
4462
|
+
const contig = client._seqTracker.getContiguousSeq(ns);
|
|
4463
|
+
const maxSeen = client._seqTracker.getMaxSeenSeq(ns);
|
|
4464
|
+
if (contig === 0 && maxSeen === 0 && eventSeq > 1) {
|
|
4465
|
+
client._clientLog.debug(`group.changed self-join baseline: group=${groupId}, event_seq=${eventSeq}, baseline=${eventSeq - 1}`);
|
|
4466
|
+
client._seqTracker.forceContiguousSeq(ns, eventSeq - 1);
|
|
4329
4467
|
}
|
|
4330
4468
|
}
|
|
4469
|
+
const contigBefore = client._seqTracker.getContiguousSeq(ns);
|
|
4470
|
+
if (eventSeq <= contigBefore || client._pushedSeqs.get(ns)?.has(eventSeq)) {
|
|
4471
|
+
client._clientLog.debug(`group.changed skipped duplicate/stale: group=${groupId}, event_seq=${eventSeq}, contiguous=${contigBefore}`);
|
|
4472
|
+
return;
|
|
4473
|
+
}
|
|
4474
|
+
this.enqueueOrderedMessage(ns, "group.changed", eventSeq, data);
|
|
4475
|
+
needPull = client._seqTracker.onMessageSeq(ns, eventSeq);
|
|
4476
|
+
const ackContig = client._seqTracker.getContiguousSeq(ns);
|
|
4477
|
+
await this.drainOrderedMessages(ns);
|
|
4478
|
+
if (ackContig > 0 && ackContig !== contigBefore) {
|
|
4479
|
+
if (data.action !== "dissolved") this.saveSeqTrackerState();
|
|
4480
|
+
const ackSeq = this.clampAckSeq("group.ack_events", "event_seq", ns, ackContig);
|
|
4481
|
+
client._transport.call("group.ack_events", {
|
|
4482
|
+
group_id: groupId,
|
|
4483
|
+
event_seq: ackSeq,
|
|
4484
|
+
device_id: client._deviceId,
|
|
4485
|
+
slot_id: client._slotId
|
|
4486
|
+
}).catch((e) => {
|
|
4487
|
+
client._clientLog.warn("group event push auto-ack failed: group=" + groupId, e);
|
|
4488
|
+
});
|
|
4489
|
+
}
|
|
4331
4490
|
if (needPull && groupId && !data._from_gap_fill) {
|
|
4332
4491
|
client._safeAsync(this.fillGroupEventGap(groupId));
|
|
4333
4492
|
}
|
|
4334
4493
|
}
|
|
4494
|
+
isSelfJoinGroupChanged(data) {
|
|
4495
|
+
const action = String(data.action ?? "").trim();
|
|
4496
|
+
if (!["member_added", "joined", "join_approved", "invite_code_used"].includes(action)) return false;
|
|
4497
|
+
const selfAid = String(this.runtime.client._aid ?? "").trim();
|
|
4498
|
+
if (!selfAid) return false;
|
|
4499
|
+
const joinedAid = String(data.joined_aid ?? data.member_aid ?? data.aid ?? "").trim();
|
|
4500
|
+
if (joinedAid === selfAid) return true;
|
|
4501
|
+
const actorAid = String(data.actor_aid ?? "").trim();
|
|
4502
|
+
return !joinedAid && ["joined", "invite_code_used"].includes(action) && actorAid === selfAid;
|
|
4503
|
+
}
|
|
4335
4504
|
enqueueOnlineUnreadHint(data) {
|
|
4336
4505
|
const client = this.runtime.client;
|
|
4337
4506
|
const groupId = String(data.group_id ?? "").trim();
|
|
@@ -4750,7 +4919,7 @@ var MessageDeliveryEngine = class {
|
|
|
4750
4919
|
const item = queue.get(seq);
|
|
4751
4920
|
queue.delete(seq);
|
|
4752
4921
|
if (!item || client._pushedSeqs.get(ns)?.has(seq)) continue;
|
|
4753
|
-
await
|
|
4922
|
+
await this.publishOrderedQueueItem(ns, item.event, seq, item.payload);
|
|
4754
4923
|
this.markPublishedSeq(ns, seq);
|
|
4755
4924
|
}
|
|
4756
4925
|
if (queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
@@ -4759,7 +4928,7 @@ var MessageDeliveryEngine = class {
|
|
|
4759
4928
|
const client = this.runtime.client;
|
|
4760
4929
|
const seqNum = Number(seq);
|
|
4761
4930
|
if (!Number.isFinite(seqNum) || !Number.isInteger(seqNum) || seqNum <= 0) {
|
|
4762
|
-
await
|
|
4931
|
+
await this.publishOrderedQueueItem(ns, event, seqNum, payload);
|
|
4763
4932
|
return true;
|
|
4764
4933
|
}
|
|
4765
4934
|
if (client._pushedSeqs.get(ns)?.has(seqNum)) {
|
|
@@ -4778,7 +4947,7 @@ var MessageDeliveryEngine = class {
|
|
|
4778
4947
|
const queue = client._pendingOrderedMsgs.get(ns);
|
|
4779
4948
|
queue?.delete(seqNum);
|
|
4780
4949
|
if (queue && queue.size === 0) client._pendingOrderedMsgs.delete(ns);
|
|
4781
|
-
await
|
|
4950
|
+
await this.publishOrderedQueueItem(ns, event, seqNum, payload);
|
|
4782
4951
|
this.markPublishedSeq(ns, seqNum);
|
|
4783
4952
|
await this.drainOrderedMessages(ns);
|
|
4784
4953
|
return true;
|
|
@@ -5173,12 +5342,37 @@ var SIGNED_METHODS = /* @__PURE__ */ new Set([
|
|
|
5173
5342
|
"message.thought.put",
|
|
5174
5343
|
"group.set_settings",
|
|
5175
5344
|
"group.resources.put",
|
|
5345
|
+
"group.resources.create_folder",
|
|
5346
|
+
"group.resources.rename",
|
|
5347
|
+
"group.resources.move",
|
|
5348
|
+
"group.resources.mount_object",
|
|
5176
5349
|
"group.resources.update",
|
|
5177
5350
|
"group.resources.delete",
|
|
5351
|
+
"group.resources.cleanup_by_storage_ref",
|
|
5178
5352
|
"group.resources.request_add",
|
|
5353
|
+
"group.resources.request_mount_object",
|
|
5179
5354
|
"group.resources.direct_add",
|
|
5180
5355
|
"group.resources.approve_request",
|
|
5181
5356
|
"group.resources.reject_request",
|
|
5357
|
+
"group.resources.unmount",
|
|
5358
|
+
"group.resources.get_access",
|
|
5359
|
+
"group.resources.resolve_access_ticket",
|
|
5360
|
+
"storage.put_object",
|
|
5361
|
+
"storage.delete_object",
|
|
5362
|
+
"storage.get_by_share",
|
|
5363
|
+
"storage.create_share_link",
|
|
5364
|
+
"storage.revoke_share_link",
|
|
5365
|
+
"storage.create_upload_session",
|
|
5366
|
+
"storage.complete_upload",
|
|
5367
|
+
"storage.create_folder",
|
|
5368
|
+
"storage.rename_folder",
|
|
5369
|
+
"storage.move_folder",
|
|
5370
|
+
"storage.delete_folder",
|
|
5371
|
+
"storage.move_object",
|
|
5372
|
+
"storage.copy_object",
|
|
5373
|
+
"storage.batch_delete",
|
|
5374
|
+
"storage.set_object_meta",
|
|
5375
|
+
"storage.append_object",
|
|
5182
5376
|
"group.commit_state",
|
|
5183
5377
|
"group.ban",
|
|
5184
5378
|
"group.unban",
|
|
@@ -5201,15 +5395,44 @@ var NON_IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
|
|
|
5201
5395
|
"group.update_avatar",
|
|
5202
5396
|
"group.update_announcement",
|
|
5203
5397
|
"group.update_settings",
|
|
5398
|
+
"storage.put_object",
|
|
5399
|
+
"storage.delete_object",
|
|
5400
|
+
"storage.create_share_link",
|
|
5401
|
+
"storage.revoke_share_link",
|
|
5402
|
+
"storage.get_by_share",
|
|
5204
5403
|
"storage.create_upload_session",
|
|
5205
5404
|
"storage.complete_upload",
|
|
5206
|
-
"storage.
|
|
5405
|
+
"storage.create_folder",
|
|
5406
|
+
"storage.rename_folder",
|
|
5407
|
+
"storage.move_folder",
|
|
5408
|
+
"storage.delete_folder",
|
|
5409
|
+
"storage.move_object",
|
|
5410
|
+
"storage.copy_object",
|
|
5411
|
+
"storage.batch_delete",
|
|
5412
|
+
"storage.set_object_meta",
|
|
5413
|
+
"storage.append_object",
|
|
5207
5414
|
"auth.create_aid",
|
|
5208
5415
|
"auth.renew_cert",
|
|
5209
5416
|
"auth.rekey",
|
|
5210
5417
|
"message.thought.put",
|
|
5211
5418
|
"group.thought.put",
|
|
5212
|
-
"group.add_member"
|
|
5419
|
+
"group.add_member",
|
|
5420
|
+
"group.resources.put",
|
|
5421
|
+
"group.resources.create_folder",
|
|
5422
|
+
"group.resources.rename",
|
|
5423
|
+
"group.resources.move",
|
|
5424
|
+
"group.resources.mount_object",
|
|
5425
|
+
"group.resources.update",
|
|
5426
|
+
"group.resources.delete",
|
|
5427
|
+
"group.resources.cleanup_by_storage_ref",
|
|
5428
|
+
"group.resources.request_add",
|
|
5429
|
+
"group.resources.request_mount_object",
|
|
5430
|
+
"group.resources.direct_add",
|
|
5431
|
+
"group.resources.approve_request",
|
|
5432
|
+
"group.resources.reject_request",
|
|
5433
|
+
"group.resources.unmount",
|
|
5434
|
+
"group.resources.get_access",
|
|
5435
|
+
"group.resources.resolve_access_ticket"
|
|
5213
5436
|
]);
|
|
5214
5437
|
var RpcPipeline = class {
|
|
5215
5438
|
constructor(runtime) {
|
|
@@ -5378,7 +5601,8 @@ var RpcPipeline = class {
|
|
|
5378
5601
|
if (!client._instanceProtectedHeaders || !PROTECTED_HEADERS_METHODS.has(method)) {
|
|
5379
5602
|
return;
|
|
5380
5603
|
}
|
|
5381
|
-
const
|
|
5604
|
+
const existingValue = params.protected_headers ?? params.headers;
|
|
5605
|
+
const existing = isJsonObject(existingValue) ? existingValue : {};
|
|
5382
5606
|
params.protected_headers = { ...client._instanceProtectedHeaders, ...existing };
|
|
5383
5607
|
}
|
|
5384
5608
|
normalizeOutboundMessagePayload(params, method = "") {
|
|
@@ -8670,7 +8894,7 @@ function normalizeProtectedHeaders(headers, payload) {
|
|
|
8670
8894
|
normalized["payload_type"] = payloadType;
|
|
8671
8895
|
}
|
|
8672
8896
|
normalized.sdk_lang = E2EE_SDK_LANG;
|
|
8673
|
-
delete normalized.
|
|
8897
|
+
delete normalized.sdk_version;
|
|
8674
8898
|
normalized.sdk_version = VERSION;
|
|
8675
8899
|
return normalized;
|
|
8676
8900
|
}
|
|
@@ -9484,19 +9708,18 @@ var V2KeyStore = class _V2KeyStore {
|
|
|
9484
9708
|
public_key: pubDer,
|
|
9485
9709
|
created_at: Date.now()
|
|
9486
9710
|
};
|
|
9487
|
-
await
|
|
9488
|
-
const req = this.store("readwrite").put(record);
|
|
9489
|
-
req.onsuccess = () => resolve();
|
|
9490
|
-
req.onerror = () => reject(req.error);
|
|
9491
|
-
});
|
|
9711
|
+
const aliasKeyId = await spkIdForPubDer(pubDer);
|
|
9492
9712
|
const alias = {
|
|
9493
9713
|
...record,
|
|
9494
|
-
key_id:
|
|
9714
|
+
key_id: aliasKeyId
|
|
9495
9715
|
};
|
|
9496
9716
|
return new Promise((resolve, reject) => {
|
|
9497
|
-
const
|
|
9498
|
-
|
|
9499
|
-
|
|
9717
|
+
const tx = this.db.transaction(V2_STORE_NAME, "readwrite");
|
|
9718
|
+
const store = tx.objectStore(V2_STORE_NAME);
|
|
9719
|
+
store.put(record);
|
|
9720
|
+
store.put(alias);
|
|
9721
|
+
tx.oncomplete = () => resolve();
|
|
9722
|
+
tx.onerror = () => reject(tx.error);
|
|
9500
9723
|
});
|
|
9501
9724
|
}
|
|
9502
9725
|
async loadIK(deviceId) {
|
|
@@ -9584,6 +9807,7 @@ var V2Session = class {
|
|
|
9584
9807
|
__publicField(this, "_oldSPKMaxSeq", /* @__PURE__ */ new Map());
|
|
9585
9808
|
__publicField(this, "_spkCache", /* @__PURE__ */ new Map());
|
|
9586
9809
|
__publicField(this, "_nowFn", () => Date.now());
|
|
9810
|
+
__publicField(this, "_registeringPromise", null);
|
|
9587
9811
|
if (!ikPriv || !ikPubDer) {
|
|
9588
9812
|
throw new Error("V2Session requires AID priv/pub keys (IK = AID identity)");
|
|
9589
9813
|
}
|
|
@@ -9664,9 +9888,21 @@ var V2Session = class {
|
|
|
9664
9888
|
spk_timestamp: spkTimestamp
|
|
9665
9889
|
});
|
|
9666
9890
|
}
|
|
9667
|
-
/** 注册本设备 SPK 到服务端。IK = AID
|
|
9891
|
+
/** 注册本设备 SPK 到服务端。IK = AID 长期密钥,无需注册。幂等,并发安全。 */
|
|
9668
9892
|
async ensureRegistered(callFn) {
|
|
9669
9893
|
if (this._registered) return;
|
|
9894
|
+
if (this._registeringPromise) {
|
|
9895
|
+
await this._registeringPromise;
|
|
9896
|
+
return;
|
|
9897
|
+
}
|
|
9898
|
+
this._registeringPromise = this._doRegister(callFn);
|
|
9899
|
+
try {
|
|
9900
|
+
await this._registeringPromise;
|
|
9901
|
+
} finally {
|
|
9902
|
+
this._registeringPromise = null;
|
|
9903
|
+
}
|
|
9904
|
+
}
|
|
9905
|
+
async _doRegister(callFn) {
|
|
9670
9906
|
await this.ensureKeys();
|
|
9671
9907
|
const uploadedSPKId = await this._store.loadLatestUploadedSPKId(this._storeDeviceId);
|
|
9672
9908
|
if (uploadedSPKId) {
|
|
@@ -9777,8 +10013,8 @@ var V2Session = class {
|
|
|
9777
10013
|
if (now - info.lastSeenAt < DESTROY_DELAY_MS) continue;
|
|
9778
10014
|
if (recentKeep.has(spkId)) continue;
|
|
9779
10015
|
try {
|
|
9780
|
-
await this._store.deleteSPK(this._deviceId, spkId);
|
|
9781
10016
|
await this._store.deleteSPK(this._storeDeviceId, spkId);
|
|
10017
|
+
await this._store.deleteSPK(this._deviceId, spkId);
|
|
9782
10018
|
} catch (err) {
|
|
9783
10019
|
console.warn("[V2Session] deleteSPK failed", { spkId, err });
|
|
9784
10020
|
continue;
|
|
@@ -9797,7 +10033,8 @@ var V2Session = class {
|
|
|
9797
10033
|
try {
|
|
9798
10034
|
await this._store.deleteSPK(this._storeDeviceId, spkId);
|
|
9799
10035
|
await this._store.deleteSPK(this._deviceId, spkId);
|
|
9800
|
-
} catch {
|
|
10036
|
+
} catch (err) {
|
|
10037
|
+
console.warn("[V2Session] deleteSPK (hard-limit) failed", { spkId, err });
|
|
9801
10038
|
continue;
|
|
9802
10039
|
}
|
|
9803
10040
|
this._oldSPKMaxSeq.delete(spkId);
|
|
@@ -10214,7 +10451,7 @@ var V2E2EECoordinator = class {
|
|
|
10214
10451
|
const gid = String(groupId ?? "").trim();
|
|
10215
10452
|
if (!gid || !client._v2Session) return;
|
|
10216
10453
|
const inflight = this.runtime.v2.groupSpkRegistrationInflight;
|
|
10217
|
-
if (inflight.has(gid)) return;
|
|
10454
|
+
if (inflight.has(gid) || this.runtime.v2.groupSpkRotationInflight.has(gid)) return;
|
|
10218
10455
|
inflight.add(gid);
|
|
10219
10456
|
client._safeAsync((async () => {
|
|
10220
10457
|
try {
|
|
@@ -10232,7 +10469,7 @@ var V2E2EECoordinator = class {
|
|
|
10232
10469
|
const gid = String(groupId ?? "").trim();
|
|
10233
10470
|
if (!gid || !client._v2Session) return;
|
|
10234
10471
|
const inflight = this.runtime.v2.groupSpkRotationInflight;
|
|
10235
|
-
if (inflight.has(gid)) return;
|
|
10472
|
+
if (inflight.has(gid) || this.runtime.v2.groupSpkRegistrationInflight.has(gid)) return;
|
|
10236
10473
|
inflight.add(gid);
|
|
10237
10474
|
client._safeAsync((async () => {
|
|
10238
10475
|
try {
|
|
@@ -10352,6 +10589,13 @@ var V2E2EECoordinator = class {
|
|
|
10352
10589
|
client._v2SenderIKPending.delete(key);
|
|
10353
10590
|
if (plaintext === null) {
|
|
10354
10591
|
client._clientLog.debug(`V2 sender IK pending retry failed: key=${key}`);
|
|
10592
|
+
client.emit("message.undecryptable", {
|
|
10593
|
+
from: entry.fromAid,
|
|
10594
|
+
sender_device_id: entry.senderDeviceId,
|
|
10595
|
+
group_id: entry.groupId || void 0,
|
|
10596
|
+
seq: Number(entry.msg.seq ?? 0),
|
|
10597
|
+
reason: "sender_ik_retry_failed"
|
|
10598
|
+
});
|
|
10355
10599
|
continue;
|
|
10356
10600
|
}
|
|
10357
10601
|
const seq = Number(entry.msg.seq ?? 0);
|
|
@@ -11787,11 +12031,11 @@ var GroupStateCoordinator = class {
|
|
|
11787
12031
|
delete data.client_signature;
|
|
11788
12032
|
} else {
|
|
11789
12033
|
const verified = await client._verifyEventSignature(data, cs);
|
|
11790
|
-
if (verified
|
|
12034
|
+
if (!client._isEventSignatureVerified(verified)) {
|
|
11791
12035
|
client._clientLog.warn(`state_committed committer signature verify failed group=%s${String(groupId)}`);
|
|
11792
12036
|
return;
|
|
11793
12037
|
}
|
|
11794
|
-
data._verified =
|
|
12038
|
+
data._verified = true;
|
|
11795
12039
|
}
|
|
11796
12040
|
}
|
|
11797
12041
|
const stateVersion = Number(data.state_version ?? 0);
|
|
@@ -15828,18 +16072,12 @@ var _AUNClient = class _AUNClient {
|
|
|
15828
16072
|
if (this._shouldSkipEventSignature(d)) {
|
|
15829
16073
|
delete d.client_signature;
|
|
15830
16074
|
} else {
|
|
15831
|
-
|
|
16075
|
+
const verified = await this._verifyEventSignature(d, cs);
|
|
16076
|
+
d._verified = this._isEventSignatureVerified(verified);
|
|
15832
16077
|
}
|
|
15833
16078
|
}
|
|
15834
|
-
await this._dispatcher.publish("group.changed", d);
|
|
15835
16079
|
const groupId = d.group_id ?? "";
|
|
15836
|
-
this.
|
|
15837
|
-
this._delivery.handleGroupChangedEventSeq(d, groupId);
|
|
15838
|
-
if (d.action === "dissolved") {
|
|
15839
|
-
if (groupId) {
|
|
15840
|
-
this._cleanupDissolvedGroup(groupId);
|
|
15841
|
-
}
|
|
15842
|
-
}
|
|
16080
|
+
await this._delivery.handleGroupChangedEventSeq(d, groupId);
|
|
15843
16081
|
} else {
|
|
15844
16082
|
await this._dispatcher.publish("group.changed", data);
|
|
15845
16083
|
}
|
|
@@ -15878,6 +16116,7 @@ var _AUNClient = class _AUNClient {
|
|
|
15878
16116
|
this._pushedSeqs.delete(`group:${groupId}`);
|
|
15879
16117
|
this._pushedSeqs.delete(`group_event:${groupId}`);
|
|
15880
16118
|
this._pendingOrderedMsgs.delete(`group:${groupId}`);
|
|
16119
|
+
this._pendingOrderedMsgs.delete(`group_event:${groupId}`);
|
|
15881
16120
|
this._clientLog.info(`cleanup dissolved group ${groupId} local state`);
|
|
15882
16121
|
}
|
|
15883
16122
|
async _verifyEventSignature(_event, cs) {
|
|
@@ -15926,6 +16165,9 @@ var _AUNClient = class _AUNClient {
|
|
|
15926
16165
|
return false;
|
|
15927
16166
|
}
|
|
15928
16167
|
}
|
|
16168
|
+
_isEventSignatureVerified(value) {
|
|
16169
|
+
return value === true;
|
|
16170
|
+
}
|
|
15929
16171
|
_protectedHeadersFromParams(params) {
|
|
15930
16172
|
const value = params.protected_headers ?? params.headers;
|
|
15931
16173
|
if (value == null) return null;
|
|
@@ -16562,6 +16804,21 @@ var _AUNClient = class _AUNClient {
|
|
|
16562
16804
|
if (!this._sessionParams) {
|
|
16563
16805
|
throw new StateError("missing connect params for reconnect");
|
|
16564
16806
|
}
|
|
16807
|
+
{
|
|
16808
|
+
const identity = this._identity;
|
|
16809
|
+
if (identity) {
|
|
16810
|
+
const cachedToken = String(identity.access_token ?? "");
|
|
16811
|
+
const expiresAt = this._auth.getAccessTokenExpiry(identity);
|
|
16812
|
+
if (cachedToken && (expiresAt === null || expiresAt > Date.now() / 1e3 + 30)) {
|
|
16813
|
+
this._sessionParams.access_token = cachedToken;
|
|
16814
|
+
} else {
|
|
16815
|
+
this._clientLog.debug(`reconnect: cached token expired or missing for aid=${this._aid ?? ""}, clearing to trigger re-login`);
|
|
16816
|
+
this._sessionParams.access_token = "";
|
|
16817
|
+
}
|
|
16818
|
+
} else {
|
|
16819
|
+
this._sessionParams.access_token = "";
|
|
16820
|
+
}
|
|
16821
|
+
}
|
|
16565
16822
|
await this._connectOnce(this._sessionParams, true);
|
|
16566
16823
|
this._lastError = null;
|
|
16567
16824
|
this._lastErrorCode = null;
|
|
@@ -19127,6 +19384,8 @@ var ServiceProxyClient = class {
|
|
|
19127
19384
|
const stats = { connection_mode: mode, connections: 0, registered: 0, handled_requests: 0, wakeup_count: 0 };
|
|
19128
19385
|
try {
|
|
19129
19386
|
if (mode === "persistent") {
|
|
19387
|
+
const maxReconnectDelay = 6e4;
|
|
19388
|
+
let _delay = Math.max(0, opts.reconnectDelaySeconds ?? 1) * 1e3;
|
|
19130
19389
|
while (this._running) {
|
|
19131
19390
|
try {
|
|
19132
19391
|
await this._autoRegisterServicesWithGateway();
|
|
@@ -19139,10 +19398,21 @@ var ServiceProxyClient = class {
|
|
|
19139
19398
|
stats.connections = Number(stats.connections) + 1;
|
|
19140
19399
|
stats.registered = Number(result.registered ?? stats.registered);
|
|
19141
19400
|
stats.handled_requests = Number(stats.handled_requests) + Number(result.handled_requests ?? 0);
|
|
19401
|
+
_delay = Math.max(0, opts.reconnectDelaySeconds ?? 1) * 1e3;
|
|
19142
19402
|
} catch (exc) {
|
|
19143
19403
|
if (!this._running) break;
|
|
19144
|
-
|
|
19145
|
-
|
|
19404
|
+
if (exc instanceof AuthError) {
|
|
19405
|
+
this._logWarn(`persistent tunnel auth error, re-authenticating: ${formatError(exc)}`);
|
|
19406
|
+
try {
|
|
19407
|
+
await this._authenticateForAccessToken();
|
|
19408
|
+
} catch (reAuthExc) {
|
|
19409
|
+
this._logWarn(`re-authentication failed: ${formatError(reAuthExc)}`);
|
|
19410
|
+
}
|
|
19411
|
+
} else {
|
|
19412
|
+
this._logWarn(`persistent tunnel reconnect scheduled after error: ${formatError(exc)}`);
|
|
19413
|
+
}
|
|
19414
|
+
await sleep(_delay);
|
|
19415
|
+
_delay = Math.min(_delay * 2, maxReconnectDelay);
|
|
19146
19416
|
} finally {
|
|
19147
19417
|
this._activeTunnel?.close();
|
|
19148
19418
|
this._activeTunnel = null;
|
|
@@ -7,9 +7,17 @@ export declare class MessageDeliveryEngine {
|
|
|
7
7
|
prunePushedSeqs(ns: string): void;
|
|
8
8
|
markPublishedSeq(ns: string, seq: number): void;
|
|
9
9
|
enqueueOrderedMessage(ns: string, event: string, seq: number, payload: EventPayload): void;
|
|
10
|
+
isGroupEventNamespace(ns: string): boolean;
|
|
11
|
+
publishOrderedQueueItem(ns: string, event: string, seq: number, payload: EventPayload): Promise<void>;
|
|
12
|
+
publishOrderedGroupChanged(payload: EventPayload): Promise<void>;
|
|
10
13
|
isInstanceScopedMessageEvent(event: string): boolean;
|
|
11
14
|
attachCurrentInstanceContext(payload: EventPayload): EventPayload;
|
|
12
15
|
normalizePublishedMessagePayload(event: string, payload: EventPayload): EventPayload;
|
|
16
|
+
appMessageEnvelope(payload: EventPayload): JsonObject;
|
|
17
|
+
isGroupScopedEvent(event: string): boolean;
|
|
18
|
+
appGroupEventEnvelope(payload: EventPayload): JsonObject;
|
|
19
|
+
attachAppMessageEnvelope(payload: EventPayload): EventPayload;
|
|
20
|
+
attachAppGroupEventEnvelope(payload: EventPayload): EventPayload;
|
|
13
21
|
stripInternalSenderDeviceFields(payload: EventPayload): EventPayload;
|
|
14
22
|
recallEventFromMessage(message: EventPayload): JsonObject | null;
|
|
15
23
|
p2pAppEventForMessage(message: EventPayload): {
|
|
@@ -30,7 +38,8 @@ export declare class MessageDeliveryEngine {
|
|
|
30
38
|
fillP2pGap(): Promise<void>;
|
|
31
39
|
fillGroupGap(groupId: string): Promise<void>;
|
|
32
40
|
fillGroupEventGap(groupId: string): Promise<void>;
|
|
33
|
-
handleGroupChangedEventSeq(data: JsonObject, groupId: string): void
|
|
41
|
+
handleGroupChangedEventSeq(data: JsonObject, groupId: string): Promise<void>;
|
|
42
|
+
isSelfJoinGroupChanged(data: JsonObject): boolean;
|
|
34
43
|
enqueueOnlineUnreadHint(data: JsonObject): void;
|
|
35
44
|
drainOnlineUnreadHints(): Promise<void>;
|
|
36
45
|
onRawGroupV2MessageCreated(data: EventPayload): Promise<void>;
|