@calimero-network/mero-js 25.0.0 → 25.0.2

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.mjs CHANGED
@@ -12051,6 +12051,13 @@ var MeroJs = class {
12051
12051
  constructor(config) {
12052
12052
  this.tokenData = null;
12053
12053
  this.refreshPromise = null;
12054
+ // True while the store is known to hold our tokens; a store that cannot persist never sets it.
12055
+ this.storeHoldsTokens = false;
12056
+ // Bumped when local tokens are dropped, so a refresh already in flight cannot restore them.
12057
+ this.tokenGeneration = 0;
12058
+ this.onStorage = () => {
12059
+ if (this.signedOutElsewhere()) this.dropLocalTokens();
12060
+ };
12054
12061
  this.rpcClient = null;
12055
12062
  this.sseClient = null;
12056
12063
  this.wsClient = null;
@@ -12062,6 +12069,8 @@ var MeroJs = class {
12062
12069
  this.tokenStore = config.tokenStore ?? null;
12063
12070
  if (this.tokenStore) {
12064
12071
  this.tokenData = this.tokenStore.getTokens();
12072
+ this.storeHoldsTokens = this.tokenData !== null;
12073
+ if (typeof window !== "undefined") window.addEventListener("storage", this.onStorage);
12065
12074
  }
12066
12075
  const isTauri = typeof window !== "undefined" && "__TAURI_INTERNALS__" in window;
12067
12076
  const getProof = resolveProofSigner(this.config.requestProof);
@@ -12080,7 +12089,7 @@ var MeroJs = class {
12080
12089
  onTokenRefresh: async (newToken) => {
12081
12090
  if (this.tokenData) {
12082
12091
  this.tokenData.access_token = newToken;
12083
- this.tokenStore?.setTokens(this.tokenData);
12092
+ this.persistTokens(this.tokenData);
12084
12093
  }
12085
12094
  },
12086
12095
  onAuthRevoked: async () => {
@@ -12226,7 +12235,7 @@ var MeroJs = class {
12226
12235
  refresh_token: response.data.refresh_token,
12227
12236
  expires_at: expiresAtFromJwt(accessToken, Date.now() + 36e5)
12228
12237
  };
12229
- this.tokenStore?.setTokens(this.tokenData);
12238
+ this.persistTokens(this.tokenData);
12230
12239
  return this.tokenData;
12231
12240
  } catch (error) {
12232
12241
  throw new Error(
@@ -12234,6 +12243,21 @@ var MeroJs = class {
12234
12243
  );
12235
12244
  }
12236
12245
  }
12246
+ persistTokens(data) {
12247
+ if (!this.tokenStore) return;
12248
+ this.tokenStore.setTokens(data);
12249
+ this.storeHoldsTokens = this.tokenStore.getTokens() !== null;
12250
+ }
12251
+ // Raw store read: an empty store is a logout, another node's bundle is not.
12252
+ signedOutElsewhere() {
12253
+ return this.storeHoldsTokens && !this.tokenStore?.getTokens();
12254
+ }
12255
+ dropLocalTokens() {
12256
+ this.tokenGeneration++;
12257
+ this.refreshPromise = null;
12258
+ this.tokenData = null;
12259
+ this.storeHoldsTokens = false;
12260
+ }
12237
12261
  /**
12238
12262
  * Get a valid token. Returns the current token as-is.
12239
12263
  * The server rejects refresh attempts while the access token is still valid,
@@ -12287,8 +12311,13 @@ var MeroJs = class {
12287
12311
  * Perform the actual token refresh. Runs with the refresh lock held.
12288
12312
  */
12289
12313
  async performTokenRefreshLocked(triggeringAccessToken) {
12314
+ if (this.signedOutElsewhere()) {
12315
+ this.dropLocalTokens();
12316
+ throw new Error("Signed out in another tab");
12317
+ }
12290
12318
  const stored = this.tokenStore?.getTokens() ?? null;
12291
12319
  if (stored?.refresh_token) {
12320
+ this.storeHoldsTokens = true;
12292
12321
  this.tokenData = stored;
12293
12322
  if (triggeringAccessToken && stored.access_token !== triggeringAccessToken) {
12294
12323
  return stored;
@@ -12297,11 +12326,20 @@ var MeroJs = class {
12297
12326
  if (!this.tokenData?.refresh_token) {
12298
12327
  throw new Error("No refresh token available");
12299
12328
  }
12329
+ const generation = this.tokenGeneration;
12330
+ const presented = this.tokenData.access_token;
12331
+ const storeHeldPresented = this.tokenStore?.getTokens()?.access_token === presented;
12300
12332
  try {
12301
12333
  const response = await this.authClient.refreshToken({
12302
12334
  access_token: this.tokenData.access_token,
12303
12335
  refresh_token: this.tokenData.refresh_token
12304
12336
  });
12337
+ if (generation !== this.tokenGeneration) throw new Error("Signed out during token refresh");
12338
+ if (storeHeldPresented && this.tokenStore?.getTokens()?.access_token !== presented) {
12339
+ this.tokenData = this.tokenStore?.getTokens() ?? null;
12340
+ this.storeHoldsTokens = this.tokenData !== null;
12341
+ throw new Error("Signed out during token refresh");
12342
+ }
12305
12343
  const accessToken = response.data.access_token;
12306
12344
  this.tokenData = {
12307
12345
  access_token: accessToken,
@@ -12310,7 +12348,7 @@ var MeroJs = class {
12310
12348
  refresh_token: response.data.refresh_token,
12311
12349
  expires_at: expiresAtFromJwt(accessToken, Date.now() + 36e5)
12312
12350
  };
12313
- this.tokenStore?.setTokens(this.tokenData);
12351
+ this.persistTokens(this.tokenData);
12314
12352
  return this.tokenData;
12315
12353
  } catch (error) {
12316
12354
  throw new Error(
@@ -12322,8 +12360,7 @@ var MeroJs = class {
12322
12360
  * Clear the current token
12323
12361
  */
12324
12362
  clearToken() {
12325
- this.refreshPromise = null;
12326
- this.tokenData = null;
12363
+ this.dropLocalTokens();
12327
12364
  this.tokenStore?.clear();
12328
12365
  }
12329
12366
  /**
@@ -12340,7 +12377,7 @@ var MeroJs = class {
12340
12377
  setTokenData(data) {
12341
12378
  const expiresAt = data.expires_at || expiresAtFromJwt(data.access_token, Date.now() + 36e5);
12342
12379
  this.tokenData = { ...data, expires_at: expiresAt };
12343
- this.tokenStore?.setTokens(this.tokenData);
12380
+ this.persistTokens(this.tokenData);
12344
12381
  }
12345
12382
  /**
12346
12383
  * Get the current token data (for debugging)
@@ -12352,6 +12389,7 @@ var MeroJs = class {
12352
12389
  * Close all event connections and clean up resources
12353
12390
  */
12354
12391
  close() {
12392
+ if (typeof window !== "undefined") window.removeEventListener("storage", this.onStorage);
12355
12393
  this.sseClient?.close();
12356
12394
  this.wsClient?.close();
12357
12395
  }