decane-connect-kit 2.25.0 → 2.26.0

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.d.cts CHANGED
@@ -308,7 +308,13 @@ interface SignTypedDataRequest {
308
308
  message: Record<string, unknown>;
309
309
  primaryType?: string;
310
310
  }
311
- type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started' | 'wallet-creating' | 'wallet-unlocking'
311
+ type SdkEvent = 'connected' | 'disconnected' | 'session-expired'
312
+ /**
313
+ * The access token was renewed while the user was active, so the session
314
+ * continued instead of ending. Informational: apps that cache the token need
315
+ * to re-read it, most do not.
316
+ */
317
+ | 'session-renewed' | 'session-started' | 'wallet-creating' | 'wallet-unlocking'
312
318
  /**
313
319
  * Emitted when this wallet could not be enrolled for server-assisted
314
320
  * recovery. Not fatal — the wallet works — but until it is enrolled the user
@@ -737,6 +743,16 @@ declare class ApiClient {
737
743
  token: string;
738
744
  expiresInDays: number;
739
745
  }>;
746
+ /**
747
+ * Trade a still-valid token for a fresh one, so an active user is never
748
+ * signed out mid-use. The backend caps the whole chain by session age, so
749
+ * this cannot renew forever; a 401 means "sign in again", not "retry".
750
+ */
751
+ refreshAccessToken(jwt: string): Promise<{
752
+ jwt: string;
753
+ expiresIn: number;
754
+ sessionExpiresIn: number;
755
+ }>;
740
756
  /** Redeem a decrypted unlock token for a session. */
741
757
  exchangeUnlockToken(token: string): Promise<{
742
758
  jwt: string;
package/dist/index.d.ts CHANGED
@@ -308,7 +308,13 @@ interface SignTypedDataRequest {
308
308
  message: Record<string, unknown>;
309
309
  primaryType?: string;
310
310
  }
311
- type SdkEvent = 'connected' | 'disconnected' | 'session-expired' | 'session-started' | 'wallet-creating' | 'wallet-unlocking'
311
+ type SdkEvent = 'connected' | 'disconnected' | 'session-expired'
312
+ /**
313
+ * The access token was renewed while the user was active, so the session
314
+ * continued instead of ending. Informational: apps that cache the token need
315
+ * to re-read it, most do not.
316
+ */
317
+ | 'session-renewed' | 'session-started' | 'wallet-creating' | 'wallet-unlocking'
312
318
  /**
313
319
  * Emitted when this wallet could not be enrolled for server-assisted
314
320
  * recovery. Not fatal — the wallet works — but until it is enrolled the user
@@ -737,6 +743,16 @@ declare class ApiClient {
737
743
  token: string;
738
744
  expiresInDays: number;
739
745
  }>;
746
+ /**
747
+ * Trade a still-valid token for a fresh one, so an active user is never
748
+ * signed out mid-use. The backend caps the whole chain by session age, so
749
+ * this cannot renew forever; a 401 means "sign in again", not "retry".
750
+ */
751
+ refreshAccessToken(jwt: string): Promise<{
752
+ jwt: string;
753
+ expiresIn: number;
754
+ sessionExpiresIn: number;
755
+ }>;
740
756
  /** Redeem a decrypted unlock token for a session. */
741
757
  exchangeUnlockToken(token: string): Promise<{
742
758
  jwt: string;
package/dist/index.js CHANGED
@@ -2193,6 +2193,17 @@ var ApiClient = class {
2193
2193
  headers: { Authorization: `Bearer ${jwt}` }
2194
2194
  });
2195
2195
  }
2196
+ /**
2197
+ * Trade a still-valid token for a fresh one, so an active user is never
2198
+ * signed out mid-use. The backend caps the whole chain by session age, so
2199
+ * this cannot renew forever; a 401 means "sign in again", not "retry".
2200
+ */
2201
+ async refreshAccessToken(jwt) {
2202
+ return this._request("/auth/refresh", {
2203
+ method: "POST",
2204
+ headers: { Authorization: `Bearer ${jwt}` }
2205
+ });
2206
+ }
2196
2207
  /** Redeem a decrypted unlock token for a session. */
2197
2208
  async exchangeUnlockToken(token) {
2198
2209
  return this._request("/auth/unlock-token/exchange", {
@@ -3952,6 +3963,7 @@ async function createDecaneConnect(config) {
3952
3963
  clearSessionPersist(SESSION_KEY);
3953
3964
  }
3954
3965
  let accessTokenExpiry = null;
3966
+ let tokenRefresh = null;
3955
3967
  function scheduleAccessTokenExpiry(jwt, expiresAt) {
3956
3968
  if (accessTokenExpiry) clearTimeout(accessTokenExpiry);
3957
3969
  accessTokenExpiry = setTimeout(() => {
@@ -3961,6 +3973,40 @@ async function createDecaneConnect(config) {
3961
3973
  dropLiveSession();
3962
3974
  events.emit("session-expired");
3963
3975
  }, Math.max(0, expiresAt - Date.now()));
3976
+ scheduleTokenRefresh(jwt, expiresAt);
3977
+ }
3978
+ function scheduleTokenRefresh(jwt, expiresAt) {
3979
+ if (tokenRefresh) clearTimeout(tokenRefresh);
3980
+ const lifetime = expiresAt - Date.now();
3981
+ if (lifetime <= 0) return;
3982
+ const at = Math.max(5e3, lifetime * 0.75);
3983
+ tokenRefresh = setTimeout(() => {
3984
+ tokenRefresh = null;
3985
+ void renewAccessToken(jwt);
3986
+ }, at);
3987
+ }
3988
+ async function renewAccessToken(expected) {
3989
+ if (auth?.jwt !== expected) return;
3990
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
3991
+ const onVisible = () => {
3992
+ document.removeEventListener("visibilitychange", onVisible);
3993
+ if (auth?.jwt === expected) void renewAccessToken(expected);
3994
+ };
3995
+ document.addEventListener("visibilitychange", onVisible);
3996
+ return;
3997
+ }
3998
+ try {
3999
+ const { jwt: fresh } = await api.refreshAccessToken(expected);
4000
+ if (auth?.jwt !== expected) return;
4001
+ auth = { ...auth, jwt: fresh };
4002
+ const freshExpiry = decodeAccessToken(fresh)?.expiresAt;
4003
+ if (freshExpiry) {
4004
+ scheduleAccessTokenExpiry(fresh, freshExpiry);
4005
+ persistLiveSession();
4006
+ }
4007
+ events.emit("session-renewed");
4008
+ } catch {
4009
+ }
3964
4010
  }
3965
4011
  if (persistSession) {
3966
4012
  const saved = loadSession(SESSION_KEY) ?? (LEGACY_SESSION_KEY !== SESSION_KEY ? loadSession(LEGACY_SESSION_KEY) : null);