@agentunion/fastaun-browser 0.4.8 → 0.4.9

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.
Files changed (38) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/_packed_docs/CHANGELOG.md +17 -0
  3. package/_packed_docs/INDEX.md +15 -9
  4. package/_packed_docs/KITE_DOCS_GUIDE.md +8 -7
  5. package/_packed_docs/sdk/AUN_DOCS_GUIDE.md +5 -4
  6. package/_packed_docs/sdk/INDEX.md +15 -9
  7. package/_packed_docs/sdk/README.md +3 -2
  8. package/dist/bundle.js +529 -395
  9. package/dist/client/delivery.js +15 -15
  10. package/dist/client/delivery.js.map +1 -1
  11. package/dist/client/group-state.d.ts.map +1 -1
  12. package/dist/client/group-state.js +13 -21
  13. package/dist/client/group-state.js.map +1 -1
  14. package/dist/client/identity.d.ts.map +1 -1
  15. package/dist/client/identity.js +5 -11
  16. package/dist/client/identity.js.map +1 -1
  17. package/dist/client/lifecycle.d.ts +2 -0
  18. package/dist/client/lifecycle.d.ts.map +1 -1
  19. package/dist/client/lifecycle.js +86 -26
  20. package/dist/client/lifecycle.js.map +1 -1
  21. package/dist/client/rpc-pipeline.d.ts +4 -1
  22. package/dist/client/rpc-pipeline.d.ts.map +1 -1
  23. package/dist/client/rpc-pipeline.js +131 -0
  24. package/dist/client/rpc-pipeline.js.map +1 -1
  25. package/dist/client/runtime.d.ts +84 -0
  26. package/dist/client/runtime.d.ts.map +1 -1
  27. package/dist/client/runtime.js +234 -0
  28. package/dist/client/runtime.js.map +1 -1
  29. package/dist/client/v2-e2ee.d.ts.map +1 -1
  30. package/dist/client/v2-e2ee.js +8 -15
  31. package/dist/client/v2-e2ee.js.map +1 -1
  32. package/dist/client.d.ts +1 -33
  33. package/dist/client.d.ts.map +1 -1
  34. package/dist/client.js +3 -307
  35. package/dist/client.js.map +1 -1
  36. package/dist/version.d.ts +1 -1
  37. package/dist/version.js +1 -1
  38. 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.8";
457
+ var VERSION = "0.4.9";
458
458
 
459
459
  // src/types.ts
460
460
  var ConnectionState = /* @__PURE__ */ ((ConnectionState2) => {
@@ -3421,10 +3421,255 @@ var SeqTracker = class {
3421
3421
  };
3422
3422
 
3423
3423
  // src/client/runtime.ts
3424
+ var RuntimeSection = class {
3425
+ constructor(runtime) {
3426
+ __publicField(this, "runtime");
3427
+ this.runtime = runtime;
3428
+ }
3429
+ get client() {
3430
+ return this.runtime.client;
3431
+ }
3432
+ };
3433
+ var RuntimeIdentityState = class extends RuntimeSection {
3434
+ get aid() {
3435
+ return this.client._aid ?? null;
3436
+ }
3437
+ get currentAid() {
3438
+ return this.client._currentAid ?? null;
3439
+ }
3440
+ get identity() {
3441
+ return this.client._identity ?? null;
3442
+ }
3443
+ get deviceId() {
3444
+ return String(this.client._deviceId ?? "");
3445
+ }
3446
+ get slotId() {
3447
+ return String(this.client._slotId ?? "");
3448
+ }
3449
+ setLoadedIdentity(aid, identity) {
3450
+ this.client._currentAid = aid;
3451
+ this.client._aid = aid.aid;
3452
+ this.client._identity = identity;
3453
+ this.client._auth?.setIdentity?.(identity);
3454
+ }
3455
+ setIdentity(identity) {
3456
+ this.client._identity = identity;
3457
+ }
3458
+ setAid(aid) {
3459
+ this.client._aid = aid;
3460
+ }
3461
+ setInstanceContext(deviceId, slotId) {
3462
+ this.client._deviceId = deviceId;
3463
+ this.client._slotId = slotId;
3464
+ this.client._auth?.setInstanceContext?.(deviceId, slotId);
3465
+ }
3466
+ clear() {
3467
+ this.client._currentAid = null;
3468
+ this.client._aid = null;
3469
+ this.client._identity = null;
3470
+ }
3471
+ };
3472
+ var RuntimeLifecycleState = class extends RuntimeSection {
3473
+ get state() {
3474
+ return String(this.client._state ?? "");
3475
+ }
3476
+ setState(state) {
3477
+ this.client._state = state;
3478
+ }
3479
+ setClosing(closing) {
3480
+ this.client._closing = closing;
3481
+ }
3482
+ setGatewayUrl(gatewayUrl) {
3483
+ this.client._gatewayUrl = gatewayUrl;
3484
+ }
3485
+ setSession(params, options) {
3486
+ this.client._sessionParams = params;
3487
+ if (options !== void 0) this.client._sessionOptions = options;
3488
+ }
3489
+ clearRetryState() {
3490
+ this.client._nextRetryAt = null;
3491
+ this.client._retryAttempt = 0;
3492
+ this.client._lastError = null;
3493
+ this.client._lastErrorCode = null;
3494
+ }
3495
+ setNextRetryAt(nextRetryAt) {
3496
+ this.client._nextRetryAt = nextRetryAt;
3497
+ }
3498
+ setRetryAttempt(attempt) {
3499
+ this.client._retryAttempt = attempt;
3500
+ }
3501
+ setError(error, code) {
3502
+ this.client._lastError = error;
3503
+ this.client._lastErrorCode = code;
3504
+ }
3505
+ clearReconnectState() {
3506
+ this.client._reconnectAbort = null;
3507
+ this.client._reconnectActive = false;
3508
+ }
3509
+ resetForDisconnect(nextState) {
3510
+ this.client._state = nextState;
3511
+ this.client._nextRetryAt = null;
3512
+ this.client._retryAttempt = 0;
3513
+ this.client._lastError = null;
3514
+ this.client._lastErrorCode = null;
3515
+ }
3516
+ resetForClose() {
3517
+ this.client._state = "closed";
3518
+ this.client._currentAid = null;
3519
+ this.client._aid = null;
3520
+ this.client._identity = null;
3521
+ this.client._gatewayUrl = null;
3522
+ this.client._sessionParams = null;
3523
+ this.clearRetryState();
3524
+ }
3525
+ };
3526
+ var RuntimeRpcState = class extends RuntimeSection {
3527
+ get protectedHeaders() {
3528
+ return this.client._instanceProtectedHeaders ?? null;
3529
+ }
3530
+ set protectedHeaders(value) {
3531
+ this.client._instanceProtectedHeaders = value;
3532
+ }
3533
+ get pullGates() {
3534
+ if (!this.client._pullGates) {
3535
+ this.client._pullGates = /* @__PURE__ */ new Map();
3536
+ }
3537
+ return this.client._pullGates;
3538
+ }
3539
+ };
3540
+ var RuntimeDeliveryState = class extends RuntimeSection {
3541
+ get seqTracker() {
3542
+ return this.client._seqTracker;
3543
+ }
3544
+ set seqTracker(value) {
3545
+ this.client._seqTracker = value;
3546
+ }
3547
+ setGapFillActive(active) {
3548
+ this.client._gapFillActive = active;
3549
+ }
3550
+ setOnlineUnreadHintTimer(timer) {
3551
+ this.client._onlineUnreadHintTimer = timer;
3552
+ }
3553
+ setOnlineUnreadHintDrainActive(active) {
3554
+ this.client._onlineUnreadHintDrainActive = active;
3555
+ }
3556
+ setV2PullPending(pending) {
3557
+ this.client._v2PullPending = pending;
3558
+ }
3559
+ setV2PullInflight(inflight) {
3560
+ this.client._v2PullInflight = inflight;
3561
+ }
3562
+ };
3563
+ var RuntimeV2State = class extends RuntimeSection {
3564
+ get session() {
3565
+ return this.client._v2Session;
3566
+ }
3567
+ set session(value) {
3568
+ this.client._v2Session = value;
3569
+ }
3570
+ get bootstrapCache() {
3571
+ if (!this.client._v2BootstrapCache) {
3572
+ this.client._v2BootstrapCache = /* @__PURE__ */ new Map();
3573
+ }
3574
+ return this.client._v2BootstrapCache;
3575
+ }
3576
+ setBootstrapCache(cache) {
3577
+ this.client._v2BootstrapCache = cache;
3578
+ }
3579
+ setSessionState(keyStore, session) {
3580
+ this.client._v2KeyStore = keyStore;
3581
+ this.client._v2Session = session;
3582
+ }
3583
+ resetForIdentity() {
3584
+ this.client._v2Session = void 0;
3585
+ this.client._v2KeyStore = void 0;
3586
+ this.client._v2SessionInitInFlight = null;
3587
+ this.client._v2BootstrapCache = /* @__PURE__ */ new Map();
3588
+ this.client._v2SigCache = /* @__PURE__ */ new Map();
3589
+ this.client._v2SenderIKPending = /* @__PURE__ */ new Map();
3590
+ this.client._v2SenderIKFetching = /* @__PURE__ */ new Set();
3591
+ }
3592
+ get groupSpkRegistrationInflight() {
3593
+ if (!this.client._groupSpkRegistrationInflight) {
3594
+ this.client._groupSpkRegistrationInflight = /* @__PURE__ */ new Set();
3595
+ }
3596
+ return this.client._groupSpkRegistrationInflight;
3597
+ }
3598
+ get groupSpkRotationInflight() {
3599
+ if (!this.client._groupSpkRotationInflight) {
3600
+ this.client._groupSpkRotationInflight = /* @__PURE__ */ new Set();
3601
+ }
3602
+ return this.client._groupSpkRotationInflight;
3603
+ }
3604
+ get groupSpkPeerFallbackRegistered() {
3605
+ if (!this.client._groupSpkPeerFallbackRegistered) {
3606
+ this.client._groupSpkPeerFallbackRegistered = /* @__PURE__ */ new Set();
3607
+ }
3608
+ return this.client._groupSpkPeerFallbackRegistered;
3609
+ }
3610
+ };
3611
+ var RuntimeGroupState = class extends RuntimeSection {
3612
+ get chains() {
3613
+ if (!this.client._v2StateChains) this.client._v2StateChains = /* @__PURE__ */ new Map();
3614
+ return this.client._v2StateChains;
3615
+ }
3616
+ get securityLevels() {
3617
+ if (!this.client._v2GroupSecurityLevels) this.client._v2GroupSecurityLevels = /* @__PURE__ */ new Map();
3618
+ return this.client._v2GroupSecurityLevels;
3619
+ }
3620
+ get sigCache() {
3621
+ if (!this.client._v2SigCache) this.client._v2SigCache = /* @__PURE__ */ new Map();
3622
+ return this.client._v2SigCache;
3623
+ }
3624
+ get lazyProposeTriggered() {
3625
+ if (!this.client._v2LazyProposeTriggered) this.client._v2LazyProposeTriggered = /* @__PURE__ */ new Map();
3626
+ return this.client._v2LazyProposeTriggered;
3627
+ }
3628
+ };
3629
+ var RuntimeServices = class extends RuntimeSection {
3630
+ get logger() {
3631
+ return this.client._logger;
3632
+ }
3633
+ get clientLog() {
3634
+ return this.client._clientLog;
3635
+ }
3636
+ get dispatcher() {
3637
+ return this.client._dispatcher;
3638
+ }
3639
+ get tokenStore() {
3640
+ return this.client._tokenStore;
3641
+ }
3642
+ get auth() {
3643
+ return this.client._auth;
3644
+ }
3645
+ get transport() {
3646
+ return this.client._transport;
3647
+ }
3648
+ get discovery() {
3649
+ return this.client._discovery;
3650
+ }
3651
+ get agentMdManager() {
3652
+ return this.client._agentMdManager;
3653
+ }
3654
+ };
3424
3655
  var ClientRuntime = class {
3425
3656
  constructor(client) {
3426
3657
  __publicField(this, "client");
3658
+ __publicField(this, "identity");
3659
+ __publicField(this, "lifecycle");
3660
+ __publicField(this, "rpc");
3661
+ __publicField(this, "delivery");
3662
+ __publicField(this, "v2");
3663
+ __publicField(this, "groupState");
3664
+ __publicField(this, "services");
3427
3665
  this.client = client;
3666
+ this.identity = new RuntimeIdentityState(this);
3667
+ this.lifecycle = new RuntimeLifecycleState(this);
3668
+ this.rpc = new RuntimeRpcState(this);
3669
+ this.delivery = new RuntimeDeliveryState(this);
3670
+ this.v2 = new RuntimeV2State(this);
3671
+ this.groupState = new RuntimeGroupState(this);
3672
+ this.services = new RuntimeServices(this);
3428
3673
  }
3429
3674
  };
3430
3675
 
@@ -3774,7 +4019,7 @@ var MessageDeliveryEngine = class {
3774
4019
  const dedupKey = `p2p_pull:${ns}`;
3775
4020
  if (client._gapFillDone.has(dedupKey)) return;
3776
4021
  client._gapFillDone.add(dedupKey);
3777
- client._gapFillActive = true;
4022
+ this.runtime.delivery.setGapFillActive(true);
3778
4023
  let filled = 0;
3779
4024
  try {
3780
4025
  const messages = await client._pullV2(afterSeq, 50);
@@ -3784,7 +4029,7 @@ var MessageDeliveryEngine = class {
3784
4029
  client._clientLog.warn(`P2P message gap-fill failed:${String(formatDeliveryError(exc))}`);
3785
4030
  } finally {
3786
4031
  client._gapFillDone.delete(dedupKey);
3787
- client._gapFillActive = false;
4032
+ this.runtime.delivery.setGapFillActive(false);
3788
4033
  if (filled > 0 && client._seqTracker.getContiguousSeq(ns) > afterSeq) {
3789
4034
  client._safeAsync(this.fillP2pGap());
3790
4035
  }
@@ -3800,7 +4045,7 @@ var MessageDeliveryEngine = class {
3800
4045
  const dedupKey = `group_pull:${ns}`;
3801
4046
  if (client._gapFillDone.has(dedupKey)) return;
3802
4047
  client._gapFillDone.add(dedupKey);
3803
- client._gapFillActive = true;
4048
+ this.runtime.delivery.setGapFillActive(true);
3804
4049
  let filled = 0;
3805
4050
  try {
3806
4051
  const messages = await client._pullGroupV2(groupId, afterSeq, 50);
@@ -3810,7 +4055,7 @@ var MessageDeliveryEngine = class {
3810
4055
  client._clientLog.warn(`group message gap-fill failed:${String(exc)}`);
3811
4056
  } finally {
3812
4057
  client._gapFillDone.delete(dedupKey);
3813
- client._gapFillActive = false;
4058
+ this.runtime.delivery.setGapFillActive(false);
3814
4059
  if (filled > 0 && client._seqTracker.getContiguousSeq(ns) > afterSeq) {
3815
4060
  client._safeAsync(this.fillGroupGap(groupId));
3816
4061
  }
@@ -3824,7 +4069,7 @@ var MessageDeliveryEngine = class {
3824
4069
  const dedupKey = `group_event_pull:${ns}`;
3825
4070
  if (client._gapFillDone.has(dedupKey)) return;
3826
4071
  client._gapFillDone.add(dedupKey);
3827
- client._gapFillActive = true;
4072
+ this.runtime.delivery.setGapFillActive(true);
3828
4073
  try {
3829
4074
  let nextAfterSeq = afterSeq;
3830
4075
  const maxPages = 100;
@@ -3897,7 +4142,7 @@ var MessageDeliveryEngine = class {
3897
4142
  client._clientLog.warn(`group event gap-fill failed:${String(exc)}`);
3898
4143
  } finally {
3899
4144
  client._gapFillDone.delete(dedupKey);
3900
- client._gapFillActive = false;
4145
+ this.runtime.delivery.setGapFillActive(false);
3901
4146
  }
3902
4147
  }
3903
4148
  handleGroupChangedEventSeq(data, groupId) {
@@ -3921,15 +4166,15 @@ var MessageDeliveryEngine = class {
3921
4166
  client._onlineUnreadHintQueue.set(groupId, { ...data });
3922
4167
  if (client._onlineUnreadHintTimer || client._onlineUnreadHintDrainActive) return;
3923
4168
  const delayMs = Math.max(0, Number(client._onlineUnreadHintInitialDelayMs ?? 750) || 0);
3924
- client._onlineUnreadHintTimer = setTimeout(() => {
3925
- client._onlineUnreadHintTimer = null;
4169
+ this.runtime.delivery.setOnlineUnreadHintTimer(setTimeout(() => {
4170
+ this.runtime.delivery.setOnlineUnreadHintTimer(null);
3926
4171
  client._safeAsync(this.drainOnlineUnreadHints());
3927
- }, delayMs);
4172
+ }, delayMs));
3928
4173
  }
3929
4174
  async drainOnlineUnreadHints() {
3930
4175
  const client = this.runtime.client;
3931
4176
  if (client._onlineUnreadHintDrainActive) return;
3932
- client._onlineUnreadHintDrainActive = true;
4177
+ this.runtime.delivery.setOnlineUnreadHintDrainActive(true);
3933
4178
  try {
3934
4179
  while (client._onlineUnreadHintQueue.size > 0) {
3935
4180
  if (client.state !== "ready") return;
@@ -3948,7 +4193,7 @@ var MessageDeliveryEngine = class {
3948
4193
  } catch (exc) {
3949
4194
  client._clientLog.debug(`online unread hint drain failed: ${formatDeliveryError(exc)}`);
3950
4195
  } finally {
3951
- client._onlineUnreadHintDrainActive = false;
4196
+ this.runtime.delivery.setOnlineUnreadHintDrainActive(false);
3952
4197
  }
3953
4198
  }
3954
4199
  async onRawGroupV2MessageCreated(data) {
@@ -4079,15 +4324,15 @@ var MessageDeliveryEngine = class {
4079
4324
  );
4080
4325
  }
4081
4326
  if (client._v2PullInflight) {
4082
- client._v2PullPending = true;
4327
+ this.runtime.delivery.setV2PullPending(true);
4083
4328
  return;
4084
4329
  }
4085
- client._v2PullInflight = true;
4330
+ this.runtime.delivery.setV2PullInflight(true);
4086
4331
  const dedupKey = `p2p_pull:${ns}`;
4087
4332
  client._gapFillDone.add(dedupKey);
4088
4333
  try {
4089
4334
  do {
4090
- client._v2PullPending = false;
4335
+ this.runtime.delivery.setV2PullPending(false);
4091
4336
  await client._pullV2();
4092
4337
  const newContig = ns ? client._seqTracker.getContiguousSeq(ns) : -1;
4093
4338
  client._clientLog.debug(
@@ -4100,7 +4345,7 @@ var MessageDeliveryEngine = class {
4100
4345
  `V2 push auto-pull failed: contiguous_seq=${contigBefore}->${newContig} err=${exc}`
4101
4346
  );
4102
4347
  } finally {
4103
- client._v2PullInflight = false;
4348
+ this.runtime.delivery.setV2PullInflight(false);
4104
4349
  client._gapFillDone.delete(dedupKey);
4105
4350
  }
4106
4351
  }
@@ -4399,21 +4644,15 @@ var IdentityRuntimeManager = class {
4399
4644
  throw new StateError(`loadIdentity not allowed in state ${publicState}`);
4400
4645
  }
4401
4646
  client._applyAidRuntimeContext(aid);
4402
- client._currentAid = aid;
4403
- client._aid = aid.aid;
4404
- client._identity = {
4647
+ this.runtime.identity.setLoadedIdentity(aid, {
4405
4648
  aid: aid.aid,
4406
4649
  private_key_pem: aid.privateKeyPem,
4407
4650
  public_key_der_b64: aid.publicKey,
4408
4651
  cert: aid.certPem
4409
- };
4410
- client._auth.setIdentity(client._identity);
4411
- client._state = "standby";
4412
- client._closing = false;
4413
- client._lastError = null;
4414
- client._lastErrorCode = null;
4415
- client._retryAttempt = 0;
4416
- client._nextRetryAt = null;
4652
+ });
4653
+ this.runtime.lifecycle.setState("standby");
4654
+ this.runtime.lifecycle.setClosing(false);
4655
+ this.runtime.lifecycle.clearRetryState();
4417
4656
  }
4418
4657
  };
4419
4658
 
@@ -4421,6 +4660,7 @@ var IdentityRuntimeManager = class {
4421
4660
  var PUBLIC_CONNECTION_OPTION_KEYS = /* @__PURE__ */ new Set([
4422
4661
  "auto_reconnect",
4423
4662
  "connect_timeout",
4663
+ "retry",
4424
4664
  "retry_initial_delay",
4425
4665
  "retry_max_delay",
4426
4666
  "retry_max_attempts",
@@ -4494,27 +4734,25 @@ var LifecycleController = class {
4494
4734
  if ("aid" in options || "access_token" in options || "token" in options || "kite_token" in options) {
4495
4735
  throw new ValidationError("authenticate options must not include aid or token fields; load an AID object first");
4496
4736
  }
4497
- client._state = "connecting";
4737
+ this.runtime.lifecycle.setState("connecting");
4498
4738
  try {
4499
4739
  const gateway = String(client._gatewayUrl ?? await client._resolveGatewayForAid(target)).trim();
4500
4740
  const result = await client._auth.authenticate(gateway, target);
4501
- client._gatewayUrl = gatewayFromAuthResult(result, gateway);
4741
+ this.runtime.lifecycle.setGatewayUrl(gatewayFromAuthResult(result, gateway));
4502
4742
  let loadedIdentity = null;
4503
4743
  try {
4504
4744
  loadedIdentity = await client._auth.loadIdentityOrNone(target);
4505
4745
  } catch (exc) {
4506
4746
  client._clientLog.debug(`authenticate identity reload skipped: ${exc instanceof Error ? exc.message : String(exc)}`);
4507
4747
  }
4508
- client._identity = loadedIdentity ?? identityFromAuthResult(client, result, target);
4509
- client._state = "authenticated";
4510
- client._lastError = null;
4511
- client._lastErrorCode = null;
4748
+ this.runtime.identity.setIdentity(loadedIdentity ?? identityFromAuthResult(client, result, target));
4749
+ this.runtime.lifecycle.setState("authenticated");
4750
+ this.runtime.lifecycle.setError(null, null);
4512
4751
  client._clientLog.debug(`authenticate exit: elapsed=${Date.now() - tStart}ms aid=${target}`);
4513
4752
  return result;
4514
4753
  } catch (err) {
4515
- client._state = "standby";
4516
- client._lastError = err instanceof Error ? err : new Error(String(err));
4517
- client._lastErrorCode = "AUTHENTICATE_FAILED";
4754
+ this.runtime.lifecycle.setState("standby");
4755
+ this.runtime.lifecycle.setError(err instanceof Error ? err : new Error(String(err)), "AUTHENTICATE_FAILED");
4518
4756
  client._clientLog.debug(`authenticate exit (error): elapsed=${Date.now() - tStart}ms err=${err instanceof Error ? err.message : String(err)}`);
4519
4757
  throw err;
4520
4758
  }
@@ -4542,11 +4780,14 @@ var LifecycleController = class {
4542
4780
  ...opts.call_timeout !== void 0 ? { call: opts.call_timeout } : {}
4543
4781
  };
4544
4782
  }
4783
+ if (opts?.retry !== void 0) options.retry = opts.retry;
4545
4784
  if (opts?.retry_initial_delay !== void 0 || opts?.retry_max_delay !== void 0 || opts?.retry_max_attempts !== void 0) {
4785
+ const baseRetry = isRecord(options.retry) ? { ...options.retry } : { initial_delay: 1, max_delay: 64, max_attempts: 0 };
4546
4786
  options.retry = {
4547
- initial_delay: opts.retry_initial_delay ?? 1,
4548
- max_delay: opts.retry_max_delay ?? 64,
4549
- max_attempts: opts.retry_max_attempts ?? 0
4787
+ ...baseRetry,
4788
+ ...opts.retry_initial_delay !== void 0 ? { initial_delay: opts.retry_initial_delay } : {},
4789
+ ...opts.retry_max_delay !== void 0 ? { max_delay: opts.retry_max_delay } : {},
4790
+ ...opts.retry_max_attempts !== void 0 ? { max_attempts: opts.retry_max_attempts } : {}
4550
4791
  };
4551
4792
  }
4552
4793
  if (opts?.connection_kind !== void 0) options.connection_kind = opts.connection_kind;
@@ -4568,28 +4809,25 @@ var LifecycleController = class {
4568
4809
  }
4569
4810
  if (publicState === "retry_backoff" /* RETRY_BACKOFF */ && client._reconnectAbort) {
4570
4811
  client._reconnectAbort.abort();
4571
- client._reconnectAbort = null;
4572
- client._reconnectActive = false;
4812
+ this.runtime.lifecycle.clearReconnectState();
4573
4813
  }
4574
4814
  if (publicState === "connection_failed" /* CONNECTION_FAILED */) {
4575
- client._retryAttempt = 0;
4576
- client._lastError = null;
4577
- client._lastErrorCode = null;
4815
+ this.runtime.lifecycle.setRetryAttempt(0);
4816
+ this.runtime.lifecycle.setError(null, null);
4578
4817
  }
4579
- client._nextRetryAt = null;
4818
+ this.runtime.lifecycle.setNextRetryAt(null);
4580
4819
  let authResult = null;
4581
4820
  if (!client._gatewayUrl) {
4582
4821
  authResult = await client.authenticate();
4583
4822
  }
4584
- client._state = "connecting";
4823
+ this.runtime.lifecycle.setState("connecting");
4585
4824
  const gateway = String(client._gatewayUrl ?? "").trim();
4586
4825
  const accessToken = accessTokenFromAuthResult(authResult) || cachedAccessToken(client);
4587
4826
  const params = { ...options, gateway, ...accessToken ? { access_token: accessToken } : {} };
4588
4827
  const normalized = client._normalizeConnectParams(params);
4589
- client._sessionParams = normalized;
4590
- client._sessionOptions = client._buildSessionOptions(normalized);
4828
+ this.runtime.lifecycle.setSession(normalized, client._buildSessionOptions(normalized));
4591
4829
  client._transport.setTimeout(client._sessionOptions.timeouts.call);
4592
- client._closing = false;
4830
+ this.runtime.lifecycle.setClosing(false);
4593
4831
  const gateways = client._resolveGateways(normalized);
4594
4832
  let lastErr = null;
4595
4833
  for (const gw of gateways) {
@@ -4604,18 +4842,74 @@ var LifecycleController = class {
4604
4842
  client._clientLog.warn(`connect: gateway ${gw} failed, trying next: ${err instanceof Error ? err.message : String(err)}`);
4605
4843
  }
4606
4844
  if (client._state === "connecting" || client._state === "authenticating") {
4607
- client._state = "connecting";
4845
+ this.runtime.lifecycle.setState("connecting");
4608
4846
  }
4609
4847
  }
4610
4848
  }
4611
4849
  if (client._state === "connecting" || client._state === "authenticating") {
4612
- client._state = client._currentAid || client._aid ? "standby" : "idle";
4850
+ this.runtime.lifecycle.setState(client._currentAid || client._aid ? "standby" : "idle");
4613
4851
  }
4614
- client._lastError = lastErr instanceof Error ? lastErr : new Error(String(lastErr));
4615
- client._lastErrorCode = "CONNECT_FAILED";
4852
+ this.runtime.lifecycle.setError(lastErr instanceof Error ? lastErr : new Error(String(lastErr)), "CONNECT_FAILED");
4616
4853
  client._clientLog.debug(`connect exit (error): elapsed=${Date.now() - tStart}ms err=${lastErr instanceof Error ? lastErr.message : String(lastErr)}`);
4617
4854
  throw lastErr;
4618
4855
  }
4856
+ async disconnect() {
4857
+ const client = this.runtime.client;
4858
+ const tStart = Date.now();
4859
+ client._clientLog.debug(`disconnect enter: state=${client._state}`);
4860
+ if (client._closing) {
4861
+ client._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms reason=closing`);
4862
+ return;
4863
+ }
4864
+ if (![
4865
+ "authenticated" /* AUTHENTICATED */,
4866
+ "connecting" /* CONNECTING */,
4867
+ "ready" /* READY */,
4868
+ "retry_backoff" /* RETRY_BACKOFF */,
4869
+ "reconnecting" /* RECONNECTING */,
4870
+ "connection_failed" /* CONNECTION_FAILED */
4871
+ ].includes(client.state)) {
4872
+ client._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms reason=not_connected`);
4873
+ return;
4874
+ }
4875
+ client._saveSeqTrackerState();
4876
+ client._stopBackgroundTasks();
4877
+ if (client._reconnectAbort) {
4878
+ client._reconnectAbort.abort();
4879
+ this.runtime.lifecycle.clearReconnectState();
4880
+ }
4881
+ await client._transport.close();
4882
+ this.runtime.lifecycle.resetForDisconnect("standby");
4883
+ await client._dispatcher.publish("state_change", { state: client._publicState(client._state) });
4884
+ client._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms`);
4885
+ }
4886
+ async close() {
4887
+ const client = this.runtime.client;
4888
+ const tStart = Date.now();
4889
+ client._clientLog.debug(`close enter: state=${client._state}`);
4890
+ this.runtime.lifecycle.setClosing(true);
4891
+ client._saveSeqTrackerState();
4892
+ client._stopBackgroundTasks();
4893
+ if (client._reconnectAbort) {
4894
+ client._reconnectAbort.abort();
4895
+ this.runtime.lifecycle.clearReconnectState();
4896
+ }
4897
+ if (client._state === "idle" || client._state === "closed") {
4898
+ this.runtime.lifecycle.setState("closed");
4899
+ client._resetSeqTrackingState();
4900
+ client._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms reason=already_idle`);
4901
+ return;
4902
+ }
4903
+ try {
4904
+ await client._transport.call("auth.logout", {});
4905
+ } catch {
4906
+ }
4907
+ await client._transport.close();
4908
+ this.runtime.lifecycle.setState("closed");
4909
+ await client._dispatcher.publish("state_change", { state: client._publicState(client._state) });
4910
+ client._resetSeqTrackingState();
4911
+ client._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms`);
4912
+ }
4619
4913
  };
4620
4914
 
4621
4915
  // src/client/peers.ts
@@ -4720,11 +5014,169 @@ var SIGNED_METHODS = /* @__PURE__ */ new Set([
4720
5014
  "group.resume"
4721
5015
  ]);
4722
5016
  var PULL_GATE_STALE_MS = 3e4;
5017
+ var NON_IDEMPOTENT_TIMEOUT = 35;
5018
+ var NON_IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
5019
+ "message.send",
5020
+ "group.send",
5021
+ "group.create",
5022
+ "group.invite",
5023
+ "group.kick",
5024
+ "group.remove_member",
5025
+ "group.leave",
5026
+ "group.dissolve",
5027
+ "group.update_name",
5028
+ "group.update_avatar",
5029
+ "group.update_announcement",
5030
+ "group.update_settings",
5031
+ "storage.upload",
5032
+ "storage.complete_upload",
5033
+ "storage.delete",
5034
+ "auth.create_aid",
5035
+ "auth.renew_cert",
5036
+ "auth.rekey",
5037
+ "message.thought.put",
5038
+ "group.thought.put",
5039
+ "group.add_member"
5040
+ ]);
4723
5041
  var RpcPipeline = class {
4724
5042
  constructor(runtime) {
4725
5043
  __publicField(this, "runtime");
4726
5044
  this.runtime = runtime;
4727
5045
  }
5046
+ async call(method, params) {
5047
+ const client = this.runtime.client;
5048
+ const tStart = Date.now();
5049
+ client._clientLog.debug(`call enter: method=${method}`);
5050
+ try {
5051
+ const result = await this.callImpl(method, params);
5052
+ client._clientLog.debug(`call exit: elapsed=${Date.now() - tStart}ms method=${method}`);
5053
+ return result;
5054
+ } catch (err) {
5055
+ client._clientLog.debug(`call exit (error): elapsed=${Date.now() - tStart}ms method=${method} err=${err instanceof Error ? err.message : String(err)}`);
5056
+ throw err;
5057
+ }
5058
+ }
5059
+ async callImpl(method, params) {
5060
+ const client = this.runtime.client;
5061
+ const p = this.preflight(method, params).params;
5062
+ if (method === "message.send") {
5063
+ const encrypt = p.encrypt !== void 0 ? p.encrypt : true;
5064
+ delete p.encrypt;
5065
+ if (encrypt) {
5066
+ await client._ensureV2SessionReady(
5067
+ "message.send",
5068
+ "V2 session not initialized; encrypted message.send requires V2 (V1 E2EE removed)"
5069
+ );
5070
+ client._clientLog.debug("call route: message.send -> V2 encrypted send");
5071
+ return await client._sendV2(String(p.to ?? ""), p.payload ?? {}, {
5072
+ messageId: String(p.message_id ?? "") || void 0,
5073
+ timestamp: p.timestamp,
5074
+ protectedHeaders: client._protectedHeadersFromParams(p),
5075
+ context: isJsonObject(p.context) ? p.context : void 0
5076
+ });
5077
+ }
5078
+ client._maybeAppendEchoTraceSend(p);
5079
+ }
5080
+ if (method === "group.send") {
5081
+ const encrypt = p.encrypt !== void 0 ? p.encrypt : true;
5082
+ delete p.encrypt;
5083
+ if (encrypt) {
5084
+ await client._ensureV2SessionReady(
5085
+ "group.send",
5086
+ "V2 session not initialized; encrypted group.send requires V2 (V1 E2EE removed)"
5087
+ );
5088
+ client._clientLog.debug("call route: group.send -> V2 encrypted send");
5089
+ return await client._sendGroupV2(String(p.group_id ?? ""), p.payload ?? {}, {
5090
+ messageId: String(p.message_id ?? "") || void 0,
5091
+ timestamp: p.timestamp,
5092
+ protectedHeaders: client._protectedHeadersFromParams(p),
5093
+ context: isJsonObject(p.context) ? p.context : void 0
5094
+ });
5095
+ }
5096
+ client._maybeAppendEchoTraceSend(p);
5097
+ }
5098
+ if (method === "group.thought.put") {
5099
+ const encrypt = p.encrypt !== void 0 ? p.encrypt : true;
5100
+ delete p.encrypt;
5101
+ if (encrypt) {
5102
+ await client._ensureV2SessionReady(
5103
+ "group.thought.put",
5104
+ "V2 session not initialized; encrypted group.thought.put requires V2 (V1 E2EE removed)"
5105
+ );
5106
+ client._clientLog.debug("call route: group.thought.put -> V2 encrypted put");
5107
+ return await client._putGroupThoughtEncryptedV2(p);
5108
+ }
5109
+ }
5110
+ if (method === "message.thought.put") {
5111
+ const encrypt = p.encrypt !== void 0 ? p.encrypt : true;
5112
+ delete p.encrypt;
5113
+ if (encrypt) {
5114
+ await client._ensureV2SessionReady(
5115
+ "message.thought.put",
5116
+ "V2 session not initialized; encrypted message.thought.put requires V2 (V1 E2EE removed)"
5117
+ );
5118
+ client._clientLog.debug("call route: message.thought.put -> V2 encrypted put");
5119
+ return await client._putMessageThoughtEncryptedV2(p);
5120
+ }
5121
+ }
5122
+ const pullGateKey = this.pullGateKeyForCall(method, p);
5123
+ if (pullGateKey) {
5124
+ return await this.runPullSerialized(pullGateKey, async () => {
5125
+ return await this.callImplInner(method, p);
5126
+ });
5127
+ }
5128
+ return await this.callImplInner(method, p);
5129
+ }
5130
+ async callImplInner(method, p) {
5131
+ const client = this.runtime.client;
5132
+ if (method === "message.pull") {
5133
+ await client._ensureV2SessionReady("message.pull");
5134
+ client._clientLog.debug("call route: message.pull -> V2 pull");
5135
+ const messages = await client._pullV2(Number(p.after_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, { force: p.force === true });
5136
+ return { messages };
5137
+ }
5138
+ if (method === "message.ack") {
5139
+ await client._ensureV2SessionReady("message.ack");
5140
+ client._clientLog.debug("call route: message.ack -> V2 ack");
5141
+ return await client._ackV2(Number(p.seq ?? p.up_to_seq ?? 0) || void 0);
5142
+ }
5143
+ if (method === "group.pull" && p.group_id) {
5144
+ await client._ensureV2SessionReady("group.pull");
5145
+ client._clientLog.debug("call route: group.pull -> V2 pull");
5146
+ const hasExplicitAfterSeq = "after_seq" in p || "after_message_seq" in p;
5147
+ const cursorParams = client._explicitGroupCursorParams(p);
5148
+ const ownsCursor = Object.keys(cursorParams).length === 0 || client._groupCursorTargetsCurrentInstance(cursorParams);
5149
+ const pullOpts = {};
5150
+ if (hasExplicitAfterSeq) pullOpts.explicitAfterSeq = true;
5151
+ if (Object.keys(cursorParams).length > 0) pullOpts.cursorParams = cursorParams;
5152
+ if (!ownsCursor) pullOpts.ownsCursor = false;
5153
+ const messages = await client._pullGroupV2(
5154
+ String(p.group_id),
5155
+ Number(p.after_seq ?? p.after_message_seq ?? 0) || 0,
5156
+ Number(p.limit ?? 50) || 50,
5157
+ Object.keys(pullOpts).length > 0 ? pullOpts : void 0
5158
+ );
5159
+ return { messages };
5160
+ }
5161
+ if (method === "group.ack_messages" && p.group_id) {
5162
+ await client._ensureV2SessionReady("group.ack_messages");
5163
+ client._clientLog.debug("call route: group.ack_messages -> V2 ack");
5164
+ const cursorParams = client._explicitGroupCursorParams(p);
5165
+ const ownsCursor = Object.keys(cursorParams).length === 0 || client._groupCursorTargetsCurrentInstance(cursorParams);
5166
+ if (!ownsCursor) {
5167
+ return await client._rawGroupAckMessages(p);
5168
+ }
5169
+ return await client._ackGroupV2(
5170
+ String(p.group_id),
5171
+ Number(p.seq ?? p.msg_seq ?? p.up_to_seq ?? 0) || void 0
5172
+ );
5173
+ }
5174
+ await this.applyClientSignature(method, p);
5175
+ const callTimeout = NON_IDEMPOTENT_METHODS.has(method) ? NON_IDEMPOTENT_TIMEOUT : void 0;
5176
+ let result = callTimeout ? await client._transport.call(method, p, callTimeout) : await client._transport.call(method, p);
5177
+ result = await this.postprocessResult(method, p, result);
5178
+ return result;
5179
+ }
4728
5180
  preflight(method, params) {
4729
5181
  const client = this.runtime.client;
4730
5182
  if (client._state !== "connected") {
@@ -9478,7 +9930,7 @@ var V2E2EECoordinator = class {
9478
9930
  get bootstrapCache() {
9479
9931
  const client = this.client;
9480
9932
  if (!(client._v2BootstrapCache instanceof Map)) {
9481
- client._v2BootstrapCache = /* @__PURE__ */ new Map();
9933
+ this.runtime.v2.setBootstrapCache(/* @__PURE__ */ new Map());
9482
9934
  }
9483
9935
  return client._v2BootstrapCache;
9484
9936
  }
@@ -9581,18 +10033,14 @@ var V2E2EECoordinator = class {
9581
10033
  if (openedKeyStore) keyStore.close();
9582
10034
  return;
9583
10035
  }
9584
- client._v2KeyStore = keyStore;
9585
- client._v2Session = session;
10036
+ this.runtime.v2.setSessionState(keyStore, session);
9586
10037
  client._clientLog.debug(`V2 session initialized aid=${aidAtStart} device=${deviceIdAtStart}`);
9587
10038
  }
9588
10039
  scheduleGroupSpkRegistration(groupId, opts) {
9589
10040
  const client = this.client;
9590
10041
  const gid = String(groupId ?? "").trim();
9591
10042
  if (!gid || !client._v2Session) return;
9592
- if (!(client._groupSpkRegistrationInflight instanceof Set)) {
9593
- client._groupSpkRegistrationInflight = /* @__PURE__ */ new Set();
9594
- }
9595
- const inflight = client._groupSpkRegistrationInflight;
10043
+ const inflight = this.runtime.v2.groupSpkRegistrationInflight;
9596
10044
  if (inflight.has(gid)) return;
9597
10045
  inflight.add(gid);
9598
10046
  client._safeAsync((async () => {
@@ -9610,10 +10058,7 @@ var V2E2EECoordinator = class {
9610
10058
  const client = this.client;
9611
10059
  const gid = String(groupId ?? "").trim();
9612
10060
  if (!gid || !client._v2Session) return;
9613
- if (!(client._groupSpkRotationInflight instanceof Set)) {
9614
- client._groupSpkRotationInflight = /* @__PURE__ */ new Set();
9615
- }
9616
- const inflight = client._groupSpkRotationInflight;
10061
+ const inflight = this.runtime.v2.groupSpkRotationInflight;
9617
10062
  if (inflight.has(gid)) return;
9618
10063
  inflight.add(gid);
9619
10064
  client._safeAsync((async () => {
@@ -9631,10 +10076,7 @@ var V2E2EECoordinator = class {
9631
10076
  const client = this.client;
9632
10077
  const gid = String(groupId ?? "").trim();
9633
10078
  if (!gid) return;
9634
- if (!(client._groupSpkPeerFallbackRegistered instanceof Set)) {
9635
- client._groupSpkPeerFallbackRegistered = /* @__PURE__ */ new Set();
9636
- }
9637
- const registered = client._groupSpkPeerFallbackRegistered;
10079
+ const registered = this.runtime.v2.groupSpkPeerFallbackRegistered;
9638
10080
  if (registered.has(gid)) return;
9639
10081
  registered.add(gid);
9640
10082
  this.scheduleGroupSpkRegistration(gid, { reason: "peer_device_prekey_fallback" });
@@ -9830,6 +10272,7 @@ var V2E2EECoordinator = class {
9830
10272
  payload: legacyPayload,
9831
10273
  encrypted: false
9832
10274
  };
10275
+ attachGatewayProximity(v1Msg, msg);
9833
10276
  const appEvent = client._delivery.p2pAppEventForMessage(v1Msg);
9834
10277
  if (ns) await client._publishPulledMessage(appEvent.event, ns, seq, appEvent.payload);
9835
10278
  else await client._publishAppEvent(appEvent.event, appEvent.payload);
@@ -10021,6 +10464,7 @@ var V2E2EECoordinator = class {
10021
10464
  payload,
10022
10465
  encrypted: false
10023
10466
  };
10467
+ attachGatewayProximity(v1Msg, msg);
10024
10468
  await client._publishPulledMessage("group.message_created", ns, seq, v1Msg);
10025
10469
  decrypted.push(v1Msg);
10026
10470
  continue;
@@ -10036,6 +10480,7 @@ var V2E2EECoordinator = class {
10036
10480
  payload,
10037
10481
  encrypted: false
10038
10482
  };
10483
+ attachGatewayProximity(v1Msg, msg);
10039
10484
  await client._publishPulledMessage("group.message_created", ns, seq, v1Msg);
10040
10485
  decrypted.push(v1Msg);
10041
10486
  continue;
@@ -11023,12 +11468,10 @@ var GroupStateCoordinator = class {
11023
11468
  const gid = normalizedGroupId(groupId);
11024
11469
  if (!gid) return;
11025
11470
  const level = String(bootstrap.e2ee_security_level ?? "").trim() || "end_to_end";
11026
- if (!(client._v2GroupSecurityLevels instanceof Map)) {
11027
- client._v2GroupSecurityLevels = /* @__PURE__ */ new Map();
11028
- }
11029
- const previous = client._v2GroupSecurityLevels.get(gid);
11471
+ const securityLevels = this.runtime.groupState.securityLevels;
11472
+ const previous = securityLevels.get(gid);
11030
11473
  if (previous === level) return;
11031
- client._v2GroupSecurityLevels.set(gid, level);
11474
+ securityLevels.set(gid, level);
11032
11475
  await client._dispatcher.publish("group.v2.security_level", {
11033
11476
  group_id: gid,
11034
11477
  level,
@@ -11068,11 +11511,9 @@ var GroupStateCoordinator = class {
11068
11511
  );
11069
11512
  const cacheHash = new Uint8Array(await crypto.subtle.digest("SHA-256", cacheInput));
11070
11513
  const cacheKey = bytesToHex6(cacheHash);
11071
- if (!(client._v2SigCache instanceof Map)) {
11072
- client._v2SigCache = /* @__PURE__ */ new Map();
11073
- }
11514
+ const sigCache = this.runtime.groupState.sigCache;
11074
11515
  const now = Date.now();
11075
- const cachedExp = client._v2SigCache.get(cacheKey);
11516
+ const cachedExp = sigCache.get(cacheKey);
11076
11517
  if (cachedExp !== void 0 && cachedExp > now) {
11077
11518
  client._clientLog.debug(`V2 state signature cache hit: group=${gid} sv=${stateVersion}`);
11078
11519
  } else {
@@ -11087,7 +11528,7 @@ var GroupStateCoordinator = class {
11087
11528
  client._clientLog.warn(`V2 state signature verification FAILED: group=${gid} sv=${stateVersion} actor=${actorAid}`);
11088
11529
  throw new E2EEError("V2 state signature verification failed");
11089
11530
  }
11090
- client._v2SigCache.set(cacheKey, now + V2_SIG_CACHE_TTL_MS);
11531
+ sigCache.set(cacheKey, now + V2_SIG_CACHE_TTL_MS);
11091
11532
  this.pruneSigCache(now);
11092
11533
  client._clientLog.debug(`V2 state signature verified: group=${gid} sv=${stateVersion} actor=${actorAid}`);
11093
11534
  }
@@ -11103,12 +11544,10 @@ var GroupStateCoordinator = class {
11103
11544
  const gid = normalizedGroupId(groupId);
11104
11545
  if (!gid || !serverChain) return;
11105
11546
  try {
11106
- if (!(client._v2StateChains instanceof Map)) {
11107
- client._v2StateChains = /* @__PURE__ */ new Map();
11108
- }
11109
- const local = client._v2StateChains.get(gid);
11547
+ const stateChains = this.runtime.groupState.chains;
11548
+ const local = stateChains.get(gid);
11110
11549
  if (local === void 0) {
11111
- client._v2StateChains.set(gid, [0, serverChain]);
11550
+ stateChains.set(gid, [0, serverChain]);
11112
11551
  return;
11113
11552
  }
11114
11553
  const [localSv, localChain] = local;
@@ -11118,7 +11557,7 @@ var GroupStateCoordinator = class {
11118
11557
  if (stateResp) {
11119
11558
  const serverSv = Number(stateResp.state_version ?? 0);
11120
11559
  if (serverSv > localSv) {
11121
- client._v2StateChains.set(gid, [serverSv, serverChain]);
11560
+ stateChains.set(gid, [serverSv, serverChain]);
11122
11561
  return;
11123
11562
  }
11124
11563
  if (serverSv < localSv) {
@@ -11141,13 +11580,11 @@ var GroupStateCoordinator = class {
11141
11580
  const client = this.client;
11142
11581
  const gid = normalizedGroupId(groupId);
11143
11582
  if (!gid) return;
11144
- if (!(client._v2LazyProposeTriggered instanceof Map)) {
11145
- client._v2LazyProposeTriggered = /* @__PURE__ */ new Map();
11146
- }
11583
+ const lazyProposeTriggered = this.runtime.groupState.lazyProposeTriggered;
11147
11584
  const now = Date.now();
11148
- const last = client._v2LazyProposeTriggered.get(gid) ?? 0;
11585
+ const last = lazyProposeTriggered.get(gid) ?? 0;
11149
11586
  if (now - last < 1e4) return;
11150
- client._v2LazyProposeTriggered.set(gid, now);
11587
+ lazyProposeTriggered.set(gid, now);
11151
11588
  client._safeAsync(client._v2AutoProposeState(gid, { leaderDelay: true }));
11152
11589
  }
11153
11590
  async onGroupStateCommitted(data) {
@@ -14230,30 +14667,6 @@ function clampHeartbeatInterval(value) {
14230
14667
  if (n > HEARTBEAT_MAX_INTERVAL_SECONDS) return HEARTBEAT_MAX_INTERVAL_SECONDS;
14231
14668
  return n;
14232
14669
  }
14233
- var NON_IDEMPOTENT_TIMEOUT = 35;
14234
- var NON_IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
14235
- "message.send",
14236
- "group.send",
14237
- "group.create",
14238
- "group.invite",
14239
- "group.kick",
14240
- "group.remove_member",
14241
- "group.leave",
14242
- "group.dissolve",
14243
- "group.update_name",
14244
- "group.update_avatar",
14245
- "group.update_announcement",
14246
- "group.update_settings",
14247
- "storage.upload",
14248
- "storage.complete_upload",
14249
- "storage.delete",
14250
- "auth.create_aid",
14251
- "auth.renew_cert",
14252
- "auth.rekey",
14253
- "message.thought.put",
14254
- "group.thought.put",
14255
- "group.add_member"
14256
- ]);
14257
14670
  function clampReconnectDelaySeconds(value, fallback, upper = RECONNECT_MAX_BASE_DELAY_SECONDS) {
14258
14671
  const parsed = Number(value);
14259
14672
  const seconds = Number.isFinite(parsed) ? parsed : fallback;
@@ -15002,62 +15415,11 @@ var _AUNClient = class _AUNClient {
15002
15415
  }
15003
15416
  /** 断开连接但保留本地状态,可再次 connect */
15004
15417
  async disconnect() {
15005
- const tStart = Date.now();
15006
- this._clientLog.debug(`disconnect enter: state=${this._state}`);
15007
- if (this._closing) {
15008
- this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms reason=closing`);
15009
- return;
15010
- }
15011
- if (![
15012
- "authenticated" /* AUTHENTICATED */,
15013
- "connecting" /* CONNECTING */,
15014
- "ready" /* READY */,
15015
- "retry_backoff" /* RETRY_BACKOFF */,
15016
- "reconnecting" /* RECONNECTING */,
15017
- "connection_failed" /* CONNECTION_FAILED */
15018
- ].includes(this.state)) {
15019
- this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms reason=not_connected`);
15020
- return;
15021
- }
15022
- this._saveSeqTrackerState();
15023
- this._stopBackgroundTasks();
15024
- if (this._reconnectAbort) {
15025
- this._reconnectAbort.abort();
15026
- this._reconnectAbort = null;
15027
- this._reconnectActive = false;
15028
- }
15029
- await this._transport.close();
15030
- this._state = "standby";
15031
- await this._dispatcher.publish("state_change", { state: this._publicState(this._state) });
15032
- this._clientLog.debug(`disconnect exit: elapsed=${Date.now() - tStart}ms`);
15418
+ return this._lifecycle.disconnect();
15033
15419
  }
15034
15420
  /** 关闭连接 */
15035
15421
  async close() {
15036
- const tStart = Date.now();
15037
- this._clientLog.debug(`close enter: state=${this._state}`);
15038
- this._closing = true;
15039
- this._saveSeqTrackerState();
15040
- this._stopBackgroundTasks();
15041
- if (this._reconnectAbort) {
15042
- this._reconnectAbort.abort();
15043
- this._reconnectAbort = null;
15044
- this._reconnectActive = false;
15045
- }
15046
- if (this._state === "idle" || this._state === "closed") {
15047
- this._state = "closed";
15048
- this._resetSeqTrackingState();
15049
- this._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms reason=already_idle`);
15050
- return;
15051
- }
15052
- try {
15053
- await this._transport.call("auth.logout", {});
15054
- } catch {
15055
- }
15056
- await this._transport.close();
15057
- this._state = "closed";
15058
- await this._dispatcher.publish("state_change", { state: this._publicState(this._state) });
15059
- this._resetSeqTrackingState();
15060
- this._clientLog.debug(`close exit: elapsed=${Date.now() - tStart}ms`);
15422
+ return this._lifecycle.close();
15061
15423
  }
15062
15424
  // ── RPC ───────────────────────────────────────────
15063
15425
  /**
@@ -15067,139 +15429,7 @@ var _AUNClient = class _AUNClient {
15067
15429
  * 自动解密 message.pull/group.pull、Group E2EE 生命周期编排。
15068
15430
  */
15069
15431
  async call(method, params) {
15070
- const tStart = Date.now();
15071
- this._clientLog.debug(`call enter: method=${method}`);
15072
- try {
15073
- const result = await this._callImpl(method, params);
15074
- this._clientLog.debug(`call exit: elapsed=${Date.now() - tStart}ms method=${method}`);
15075
- return result;
15076
- } catch (err) {
15077
- this._clientLog.debug(`call exit (error): elapsed=${Date.now() - tStart}ms method=${method} err=${err instanceof Error ? err.message : String(err)}`);
15078
- throw err;
15079
- }
15080
- }
15081
- async _callImpl(method, params) {
15082
- const p = this._rpcPipeline.preflight(method, params).params;
15083
- if (method === "message.send") {
15084
- const encrypt = p.encrypt !== void 0 ? p.encrypt : true;
15085
- delete p.encrypt;
15086
- if (encrypt) {
15087
- await this._ensureV2SessionReady(
15088
- "message.send",
15089
- "V2 session not initialized; encrypted message.send requires V2 (V1 E2EE removed)"
15090
- );
15091
- this._clientLog.debug("call route: message.send \u2192 V2 encrypted send");
15092
- return await this._sendV2(String(p.to ?? ""), p.payload ?? {}, {
15093
- messageId: String(p.message_id ?? "") || void 0,
15094
- timestamp: p.timestamp,
15095
- protectedHeaders: this._protectedHeadersFromParams(p),
15096
- context: isJsonObject(p.context) ? p.context : void 0
15097
- });
15098
- }
15099
- this._maybeAppendEchoTraceSend(p);
15100
- }
15101
- if (method === "group.send") {
15102
- const encrypt = p.encrypt !== void 0 ? p.encrypt : true;
15103
- delete p.encrypt;
15104
- if (encrypt) {
15105
- await this._ensureV2SessionReady(
15106
- "group.send",
15107
- "V2 session not initialized; encrypted group.send requires V2 (V1 E2EE removed)"
15108
- );
15109
- this._clientLog.debug("call route: group.send \u2192 V2 encrypted send");
15110
- return await this._sendGroupV2(String(p.group_id ?? ""), p.payload ?? {}, {
15111
- messageId: String(p.message_id ?? "") || void 0,
15112
- timestamp: p.timestamp,
15113
- protectedHeaders: this._protectedHeadersFromParams(p),
15114
- context: isJsonObject(p.context) ? p.context : void 0
15115
- });
15116
- }
15117
- this._maybeAppendEchoTraceSend(p);
15118
- }
15119
- if (method === "group.thought.put") {
15120
- const encrypt = p.encrypt !== void 0 ? p.encrypt : true;
15121
- delete p.encrypt;
15122
- if (encrypt) {
15123
- await this._ensureV2SessionReady(
15124
- "group.thought.put",
15125
- "V2 session not initialized; encrypted group.thought.put requires V2 (V1 E2EE removed)"
15126
- );
15127
- this._clientLog.debug("call route: group.thought.put \u2192 V2 encrypted put");
15128
- return this._putGroupThoughtEncryptedV2(p);
15129
- }
15130
- }
15131
- if (method === "message.thought.put") {
15132
- const encrypt = p.encrypt !== void 0 ? p.encrypt : true;
15133
- delete p.encrypt;
15134
- if (encrypt) {
15135
- await this._ensureV2SessionReady(
15136
- "message.thought.put",
15137
- "V2 session not initialized; encrypted message.thought.put requires V2 (V1 E2EE removed)"
15138
- );
15139
- this._clientLog.debug("call route: message.thought.put \u2192 V2 encrypted put");
15140
- return this._putMessageThoughtEncryptedV2(p);
15141
- }
15142
- }
15143
- const pullGateKey = this._pullGateKeyForCall(method, p);
15144
- if (pullGateKey) {
15145
- return await this._runPullSerialized(pullGateKey, async () => {
15146
- return await this._callImplInner(method, p);
15147
- });
15148
- }
15149
- return await this._callImplInner(method, p);
15150
- }
15151
- /**
15152
- * _callImpl 的内层:pull gate 之后的实际 RPC 分发逻辑。
15153
- * 拆分出来以便 pull gate 包裹整个操作。
15154
- */
15155
- async _callImplInner(method, p) {
15156
- if (method === "message.pull") {
15157
- await this._ensureV2SessionReady("message.pull");
15158
- this._clientLog.debug("call route: message.pull \u2192 V2 pull");
15159
- const messages = await this._pullV2(Number(p.after_seq ?? 0) || 0, Number(p.limit ?? 50) || 50, { force: p.force === true });
15160
- return { messages };
15161
- }
15162
- if (method === "message.ack") {
15163
- await this._ensureV2SessionReady("message.ack");
15164
- this._clientLog.debug("call route: message.ack \u2192 V2 ack");
15165
- return await this._ackV2(Number(p.seq ?? p.up_to_seq ?? 0) || void 0);
15166
- }
15167
- if (method === "group.pull" && p.group_id) {
15168
- await this._ensureV2SessionReady("group.pull");
15169
- this._clientLog.debug("call route: group.pull \u2192 V2 pull");
15170
- const hasExplicitAfterSeq = "after_seq" in p || "after_message_seq" in p;
15171
- const cursorParams = this._explicitGroupCursorParams(p);
15172
- const ownsCursor = Object.keys(cursorParams).length === 0 || this._groupCursorTargetsCurrentInstance(cursorParams);
15173
- const pullOpts = {};
15174
- if (hasExplicitAfterSeq) pullOpts.explicitAfterSeq = true;
15175
- if (Object.keys(cursorParams).length > 0) pullOpts.cursorParams = cursorParams;
15176
- if (!ownsCursor) pullOpts.ownsCursor = false;
15177
- const messages = await this._pullGroupV2(
15178
- String(p.group_id),
15179
- Number(p.after_seq ?? p.after_message_seq ?? 0) || 0,
15180
- Number(p.limit ?? 50) || 50,
15181
- Object.keys(pullOpts).length > 0 ? pullOpts : void 0
15182
- );
15183
- return { messages };
15184
- }
15185
- if (method === "group.ack_messages" && p.group_id) {
15186
- await this._ensureV2SessionReady("group.ack_messages");
15187
- this._clientLog.debug("call route: group.ack_messages \u2192 V2 ack");
15188
- const cursorParams = this._explicitGroupCursorParams(p);
15189
- const ownsCursor = Object.keys(cursorParams).length === 0 || this._groupCursorTargetsCurrentInstance(cursorParams);
15190
- if (!ownsCursor) {
15191
- return await this._rawGroupAckMessages(p);
15192
- }
15193
- return await this._ackGroupV2(
15194
- String(p.group_id),
15195
- Number(p.seq ?? p.msg_seq ?? p.up_to_seq ?? 0) || void 0
15196
- );
15197
- }
15198
- await this._rpcPipeline.applyClientSignature(method, p);
15199
- const callTimeout = NON_IDEMPOTENT_METHODS.has(method) ? NON_IDEMPOTENT_TIMEOUT : void 0;
15200
- let result = callTimeout ? await this._transport.call(method, p, callTimeout) : await this._transport.call(method, p);
15201
- result = await this._rpcPipeline.postprocessResult(method, p, result);
15202
- return result;
15432
+ return await this._rpcPipeline.call(method, params);
15203
15433
  }
15204
15434
  async _callRawV2Rpc(method, params) {
15205
15435
  const p = { ...params ?? {} };
@@ -15243,10 +15473,6 @@ var _AUNClient = class _AUNClient {
15243
15473
  _onRawMessageReceived(data) {
15244
15474
  this._delivery.onRawMessageReceived(data);
15245
15475
  }
15246
- /** 实际处理推送消息的异步任务(V2-only:明文消息直接透传,V2 加密消息走 _onV2PushNotification) */
15247
- async _processAndPublishMessage(data) {
15248
- return this._delivery.processAndPublishMessage(data);
15249
- }
15250
15476
  /** 处理群组消息推送:re-publish(V2 加密消息走 V2 push 路径) */
15251
15477
  _onRawGroupMessageCreated(data) {
15252
15478
  return this._delivery.onRawGroupMessageCreated(data);
@@ -15255,43 +15481,19 @@ var _AUNClient = class _AUNClient {
15255
15481
  async _onRawGroupV2MessageCreated(data) {
15256
15482
  return this._delivery.onRawGroupV2MessageCreated(data);
15257
15483
  }
15258
- /**
15259
- * 处理群组推送消息的异步任务(V2-only:明文消息直接透传)。
15260
- *
15261
- * 带 payload 的事件(消息推送):直接 re-publish。
15262
- * 不带 payload 的事件(通知):自动 pull 最新消息。
15263
- */
15264
- async _processAndPublishGroupMessage(data) {
15265
- return this._delivery.processAndPublishGroupMessage(data);
15266
- }
15267
15484
  async _publishEncryptedPushMessage(normalEvent, undecryptableEvent, ns, seq, msg, group) {
15268
15485
  return await this._v2E2EE.publishEncryptedPushMessage(normalEvent, undecryptableEvent, ns, seq, msg, group);
15269
15486
  }
15270
15487
  async _decryptV2PushMessage(data) {
15271
15488
  return await this._v2E2EE.decryptV2PushMessage(data);
15272
15489
  }
15273
- /** 后台补齐群消息空洞 */
15274
- async _fillGroupGap(groupId) {
15275
- return this._delivery.fillGroupGap(groupId);
15276
- }
15277
- /** 后台补齐群事件空洞 */
15278
- async _fillGroupEventGap(groupId) {
15279
- return this._delivery.fillGroupEventGap(groupId);
15280
- }
15281
15490
  /** 后台补齐 P2P 消息空洞 */
15282
15491
  async _fillP2pGap() {
15283
15492
  return this._delivery.fillP2pGap();
15284
15493
  }
15285
- /** 只按硬上限裁剪 published guard,不能按 contiguousSeq 清理。 */
15286
- _prunePushedSeqs(ns) {
15287
- this._delivery.prunePushedSeqs(ns);
15288
- }
15289
15494
  _markPublishedSeq(ns, seq) {
15290
15495
  this._delivery.markPublishedSeq(ns, seq);
15291
15496
  }
15292
- _attachCurrentInstanceContext(payload) {
15293
- return this._delivery.attachCurrentInstanceContext(payload);
15294
- }
15295
15497
  async _publishAppEvent(event, payload) {
15296
15498
  await this._delivery.publishAppEvent(event, payload);
15297
15499
  }
@@ -15328,9 +15530,6 @@ var _AUNClient = class _AUNClient {
15328
15530
  const trace = `${this._echoTimestamp()} [AUN-SDK.receive] aid=${this._aid ?? "-"} conn_uptime=${uptime}s`;
15329
15531
  msg.payload = { ...payload, text: payload.text + "\n" + trace };
15330
15532
  }
15331
- _messageTargetsCurrentInstance(message) {
15332
- return this._delivery.messageTargetsCurrentInstance(message);
15333
- }
15334
15533
  async _drainOrderedMessages(ns, beforeSeq) {
15335
15534
  await this._delivery.drainOrderedMessages(ns, beforeSeq);
15336
15535
  }
@@ -15474,59 +15673,6 @@ var _AUNClient = class _AUNClient {
15474
15673
  const digest = await crypto.subtle.digest("SHA-256", certBytes);
15475
15674
  return "sha256:" + Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
15476
15675
  }
15477
- /**
15478
- * 从 X.509 DER 证书中提取 SubjectPublicKeyInfo 并计算其 SHA-256 指纹。
15479
- * 返回 "sha256:<hex>",提取失败返回空串。
15480
- * 用于 H7 指纹校验(DER 证书指纹 OR SPKI 指纹任一匹配)。
15481
- */
15482
- async _spkiFingerprint(certPem) {
15483
- try {
15484
- const der = new Uint8Array(pemToArrayBuffer(certPem));
15485
- const readLen = (buf, pos) => {
15486
- const first = buf[pos];
15487
- if (first < 128) return { len: first, next: pos + 1 };
15488
- const n = first & 127;
15489
- let len = 0;
15490
- for (let i = 0; i < n; i++) len = len << 8 | buf[pos + 1 + i];
15491
- return { len, next: pos + 1 + n };
15492
- };
15493
- if (der[0] !== 48) return "";
15494
- const outer = readLen(der, 1);
15495
- const tbsStart = outer.next;
15496
- if (der[tbsStart] !== 48) return "";
15497
- const tbsLen = readLen(der, tbsStart + 1);
15498
- let p = tbsLen.next;
15499
- const tbsEnd = tbsLen.next + tbsLen.len;
15500
- if (der[p] === 160) {
15501
- const lv2 = readLen(der, p + 1);
15502
- p = lv2.next + lv2.len;
15503
- }
15504
- if (der[p] !== 2) return "";
15505
- let lv = readLen(der, p + 1);
15506
- p = lv.next + lv.len;
15507
- if (der[p] !== 48) return "";
15508
- lv = readLen(der, p + 1);
15509
- p = lv.next + lv.len;
15510
- if (der[p] !== 48) return "";
15511
- lv = readLen(der, p + 1);
15512
- p = lv.next + lv.len;
15513
- if (der[p] !== 48) return "";
15514
- lv = readLen(der, p + 1);
15515
- p = lv.next + lv.len;
15516
- if (der[p] !== 48) return "";
15517
- lv = readLen(der, p + 1);
15518
- p = lv.next + lv.len;
15519
- if (der[p] !== 48 || p >= tbsEnd) return "";
15520
- const spkiStart = p;
15521
- const spkiLV = readLen(der, p + 1);
15522
- const spkiEnd = spkiLV.next + spkiLV.len;
15523
- const spkiDer = der.subarray(spkiStart, spkiEnd);
15524
- const digest = await crypto.subtle.digest("SHA-256", spkiDer);
15525
- return "sha256:" + Array.from(new Uint8Array(digest)).map((b) => b.toString(16).padStart(2, "0")).join("");
15526
- } catch {
15527
- return "";
15528
- }
15529
- }
15530
15676
  async _decryptGroupThoughts(result) {
15531
15677
  return await this._v2E2EE.decryptGroupThoughts(result);
15532
15678
  }
@@ -16030,12 +16176,6 @@ var _AUNClient = class _AUNClient {
16030
16176
  _validateMessageRecipient(toAid) {
16031
16177
  this._rpcPipeline.validateMessageRecipient(toAid);
16032
16178
  }
16033
- _validateOutboundCall(method, params) {
16034
- this._rpcPipeline.validateOutboundCall(method, params);
16035
- }
16036
- _injectMessageCursorContext(method, params) {
16037
- this._rpcPipeline.injectMessageCursorContext(method, params);
16038
- }
16039
16179
  /** 处理服务端主动断开通知 event/gateway.disconnect
16040
16180
  *
16041
16181
  * 服务端可能附带结构化 detail 字段(如配额超限时含 aid/device_id/slot_id/quota_kind/evicted_by)。
@@ -16414,9 +16554,6 @@ var _AUNClient = class _AUNClient {
16414
16554
  _scheduleV2SenderIKPending(args) {
16415
16555
  return this._v2E2EE.scheduleSenderIKPending(args);
16416
16556
  }
16417
- async _resolveV2SenderIKPending(fromAid, senderDeviceId, groupId, fetchKey) {
16418
- return await this._v2E2EE.resolveSenderIKPending(fromAid, senderDeviceId, groupId, fetchKey);
16419
- }
16420
16557
  /**
16421
16558
  * V2 P2P 加密发送(推测性:用缓存 bootstrap 直接发,失败刷新重试一次)。
16422
16559
  *
@@ -16769,9 +16906,6 @@ var _AUNClient = class _AUNClient {
16769
16906
  return (!deviceId || deviceId === (this._deviceId ?? "")) && (!slotId || slotId === (this._slotId ?? ""));
16770
16907
  }
16771
16908
  // ── Pull Gate(序列化同一 key 的并发 pull)──────────────────
16772
- _pullGateKeyForCall(method, params) {
16773
- return this._rpcPipeline.pullGateKeyForCall(method, params);
16774
- }
16775
16909
  async _runPullSerialized(key, operation) {
16776
16910
  return await this._rpcPipeline.runPullSerialized(key, operation);
16777
16911
  }