@calimero-network/mero-js 25.0.6 → 25.0.7

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/mero-js.d.ts CHANGED
@@ -86,6 +86,8 @@ export interface TokenData {
86
86
  access_token: string;
87
87
  refresh_token: string;
88
88
  expires_at: number;
89
+ /** Origin and path prefix of the issuing node; a store entry for another node is ignored. */
90
+ node_origin?: string;
89
91
  }
90
92
  /**
91
93
  * Main MeroJs SDK class that manages all API clients and authentication
@@ -101,6 +103,7 @@ export declare class MeroJs {
101
103
  private storeHoldsTokens;
102
104
  private tokenGeneration;
103
105
  private readonly onStorage;
106
+ private readonly nodeKey;
104
107
  private rpcClient;
105
108
  private sseClient;
106
109
  private wsClient;
@@ -156,6 +159,8 @@ export declare class MeroJs {
156
159
  password: string;
157
160
  }): Promise<TokenData>;
158
161
  private persistTokens;
162
+ /** The stored bundle, only if this node issued it; a legacy or foreign entry reads as empty. */
163
+ private readStoredTokens;
159
164
  private signedOutElsewhere;
160
165
  private dropLocalTokens;
161
166
  /**
package/dist/mero-js.js CHANGED
@@ -28,6 +28,16 @@ function getLockManager() {
28
28
  const locks = nav?.locks;
29
29
  return locks && typeof locks.request === 'function' ? locks : null;
30
30
  }
31
+ /** Key a token bundle to its node: origin plus path prefix, so nodes behind one host stay apart. */
32
+ function nodeKeyOf(baseUrl) {
33
+ try {
34
+ const { origin, pathname } = new URL(baseUrl);
35
+ return origin === 'null' ? baseUrl : origin + pathname.replace(/\/+$/, '');
36
+ }
37
+ catch {
38
+ return baseUrl;
39
+ }
40
+ }
31
41
  /** Try to extract `exp` (seconds) from a JWT, return ms timestamp or fallback. */
32
42
  function expiresAtFromJwt(token, fallbackMs) {
33
43
  try {
@@ -72,9 +82,10 @@ export class MeroJs {
72
82
  ...config,
73
83
  };
74
84
  this.tokenStore = config.tokenStore ?? null;
85
+ this.nodeKey = nodeKeyOf(config.baseUrl);
75
86
  // Restore tokens from store if available
87
+ this.tokenData = this.readStoredTokens();
76
88
  if (this.tokenStore) {
77
- this.tokenData = this.tokenStore.getTokens();
78
89
  this.storeHoldsTokens = this.tokenData !== null;
79
90
  if (typeof window !== 'undefined')
80
91
  window.addEventListener('storage', this.onStorage);
@@ -101,7 +112,7 @@ export class MeroJs {
101
112
  onTokenRefresh: async (newToken) => {
102
113
  if (this.tokenData) {
103
114
  this.tokenData.access_token = newToken;
104
- this.persistTokens(this.tokenData);
115
+ this.persistTokens();
105
116
  }
106
117
  },
107
118
  onAuthRevoked: async () => {
@@ -253,19 +264,30 @@ export class MeroJs {
253
264
  refresh_token: response.data.refresh_token,
254
265
  expires_at: expiresAtFromJwt(accessToken, Date.now() + 3600000),
255
266
  };
256
- this.persistTokens(this.tokenData);
267
+ this.persistTokens();
257
268
  return this.tokenData;
258
269
  }
259
270
  catch (error) {
260
271
  throw new Error(`Authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
261
272
  }
262
273
  }
263
- persistTokens(data) {
264
- if (!this.tokenStore)
274
+ persistTokens() {
275
+ if (!this.tokenStore || !this.tokenData)
265
276
  return;
266
- this.tokenStore.setTokens(data);
277
+ this.tokenStore.setTokens({ ...this.tokenData, node_origin: this.nodeKey });
267
278
  this.storeHoldsTokens = this.tokenStore.getTokens() !== null;
268
279
  }
280
+ /** The stored bundle, only if this node issued it; a legacy or foreign entry reads as empty. */
281
+ readStoredTokens() {
282
+ const stored = this.tokenStore?.getTokens();
283
+ if (!stored || stored.node_origin !== this.nodeKey)
284
+ return null;
285
+ return {
286
+ access_token: stored.access_token,
287
+ refresh_token: stored.refresh_token,
288
+ expires_at: stored.expires_at,
289
+ };
290
+ }
269
291
  // Raw store read: an empty store is a logout, another node's bundle is not.
270
292
  signedOutElsewhere() {
271
293
  return this.storeHoldsTokens && !this.tokenStore?.getTokens();
@@ -338,7 +360,7 @@ export class MeroJs {
338
360
  this.dropLocalTokens();
339
361
  throw new Error('Signed out in another tab');
340
362
  }
341
- const stored = this.tokenStore?.getTokens() ?? null;
363
+ const stored = this.readStoredTokens();
342
364
  if (stored?.refresh_token) {
343
365
  this.storeHoldsTokens = true;
344
366
  this.tokenData = stored;
@@ -377,7 +399,7 @@ export class MeroJs {
377
399
  refresh_token: response.data.refresh_token,
378
400
  expires_at: expiresAtFromJwt(accessToken, Date.now() + 3600000),
379
401
  };
380
- this.persistTokens(this.tokenData);
402
+ this.persistTokens();
381
403
  return this.tokenData;
382
404
  }
383
405
  catch (error) {
@@ -409,7 +431,7 @@ export class MeroJs {
409
431
  setTokenData(data) {
410
432
  const expiresAt = data.expires_at || expiresAtFromJwt(data.access_token, Date.now() + 3600000);
411
433
  this.tokenData = { ...data, expires_at: expiresAt };
412
- this.persistTokens(this.tokenData);
434
+ this.persistTokens();
413
435
  }
414
436
  /**
415
437
  * Get the current token data (for debugging)
@@ -30,6 +30,7 @@ export class LocalStorageTokenStore {
30
30
  access_token: parsed.access_token,
31
31
  refresh_token: parsed.refresh_token,
32
32
  expires_at: typeof parsed.expires_at === 'number' ? parsed.expires_at : Date.now() + 3600000,
33
+ ...(typeof parsed.node_origin === 'string' && { node_origin: parsed.node_origin }),
33
34
  };
34
35
  }
35
36
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calimero-network/mero-js",
3
- "version": "25.0.6",
3
+ "version": "25.0.7",
4
4
  "description": "Pure JavaScript SDK for Calimero",
5
5
  "type": "module",
6
6
  "exports": {