decane-connect-kit 2.25.0 → 2.26.1

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/dist/index.cjs CHANGED
@@ -2276,6 +2276,17 @@ var ApiClient = class {
2276
2276
  headers: { Authorization: `Bearer ${jwt}` }
2277
2277
  });
2278
2278
  }
2279
+ /**
2280
+ * Trade a still-valid token for a fresh one, so an active user is never
2281
+ * signed out mid-use. The backend caps the whole chain by session age, so
2282
+ * this cannot renew forever; a 401 means "sign in again", not "retry".
2283
+ */
2284
+ async refreshAccessToken(jwt) {
2285
+ return this._request("/auth/refresh", {
2286
+ method: "POST",
2287
+ headers: { Authorization: `Bearer ${jwt}` }
2288
+ });
2289
+ }
2279
2290
  /** Redeem a decrypted unlock token for a session. */
2280
2291
  async exchangeUnlockToken(token) {
2281
2292
  return this._request("/auth/unlock-token/exchange", {
@@ -4030,6 +4041,7 @@ async function createDecaneConnect(config) {
4030
4041
  clearSessionPersist(SESSION_KEY);
4031
4042
  }
4032
4043
  let accessTokenExpiry = null;
4044
+ let tokenRefresh = null;
4033
4045
  function scheduleAccessTokenExpiry(jwt, expiresAt) {
4034
4046
  if (accessTokenExpiry) clearTimeout(accessTokenExpiry);
4035
4047
  accessTokenExpiry = setTimeout(() => {
@@ -4039,6 +4051,40 @@ async function createDecaneConnect(config) {
4039
4051
  dropLiveSession();
4040
4052
  events.emit("session-expired");
4041
4053
  }, Math.max(0, expiresAt - Date.now()));
4054
+ scheduleTokenRefresh(jwt, expiresAt);
4055
+ }
4056
+ function scheduleTokenRefresh(jwt, expiresAt) {
4057
+ if (tokenRefresh) clearTimeout(tokenRefresh);
4058
+ const lifetime = expiresAt - Date.now();
4059
+ if (lifetime <= 0) return;
4060
+ const at = Math.max(5e3, lifetime * 0.75);
4061
+ tokenRefresh = setTimeout(() => {
4062
+ tokenRefresh = null;
4063
+ void renewAccessToken(jwt);
4064
+ }, at);
4065
+ }
4066
+ async function renewAccessToken(expected) {
4067
+ if (auth?.jwt !== expected) return;
4068
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
4069
+ const onVisible = () => {
4070
+ document.removeEventListener("visibilitychange", onVisible);
4071
+ if (auth?.jwt === expected) void renewAccessToken(expected);
4072
+ };
4073
+ document.addEventListener("visibilitychange", onVisible);
4074
+ return;
4075
+ }
4076
+ try {
4077
+ const { jwt: fresh } = await api.refreshAccessToken(expected);
4078
+ if (auth?.jwt !== expected) return;
4079
+ auth = { ...auth, jwt: fresh };
4080
+ const freshExpiry = decodeAccessToken(fresh)?.expiresAt;
4081
+ if (freshExpiry) {
4082
+ scheduleAccessTokenExpiry(fresh, freshExpiry);
4083
+ persistLiveSession();
4084
+ }
4085
+ events.emit("session-renewed");
4086
+ } catch {
4087
+ }
4042
4088
  }
4043
4089
  if (persistSession) {
4044
4090
  const saved = loadSession(SESSION_KEY) ?? (LEGACY_SESSION_KEY !== SESSION_KEY ? loadSession(LEGACY_SESSION_KEY) : null);
@@ -7621,6 +7667,7 @@ function DecaneKit({ config, children }) {
7621
7667
  rpcUrls,
7622
7668
  requireAssertionPerSignature,
7623
7669
  deferDeviceProtection,
7670
+ resumeSessionAcrossTabs,
7624
7671
  // Recovery callbacks. Forwarding these is what makes recovery
7625
7672
  // configurable at all from <DecaneKit>; before, they were silently
7626
7673
  // dropped and the headless client was constructed without them.
@@ -7642,6 +7689,7 @@ function DecaneKit({ config, children }) {
7642
7689
  promptUnlockPassword,
7643
7690
  requireAssertionPerSignature,
7644
7691
  deferDeviceProtection,
7692
+ resumeSessionAcrossTabs,
7645
7693
  offerRecoveryAtSignup,
7646
7694
  onRecoveryShareOffer,
7647
7695
  onRecoveryFileReady,