@spacelr/sdk 0.9.0 → 0.9.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.d.mts CHANGED
@@ -372,12 +372,13 @@ type AuthLostListener = (reason: AuthLostReason) => void;
372
372
  declare class TokenManager {
373
373
  private storage;
374
374
  private refreshBufferSeconds;
375
+ private refreshLockName;
375
376
  private refreshCallback;
376
377
  private refreshPromise;
377
378
  private tokenRefreshedListeners;
378
379
  private authLostListeners;
379
380
  private authLostEmitted;
380
- constructor(storage?: TokenStorage, refreshBufferSeconds?: number);
381
+ constructor(storage?: TokenStorage, refreshBufferSeconds?: number, refreshLockName?: string);
381
382
  setRefreshCallback(callback: RefreshCallback): void;
382
383
  getAccessToken(): Promise<string | null>;
383
384
  setTokens(tokens: StoredTokens): Promise<void>;
@@ -398,6 +399,9 @@ declare class TokenManager {
398
399
  private shouldRefresh;
399
400
  private tryRefresh;
400
401
  private executeRefresh;
402
+ private doRefresh;
403
+ private safeGetTokens;
404
+ private withCrossTabLock;
401
405
  private emitTokenRefreshed;
402
406
  }
403
407
 
@@ -1702,6 +1706,14 @@ interface SpacelrClient {
1702
1706
  setTokens(tokens: StoredTokens): Promise<void>;
1703
1707
  /** Clear stored tokens and reset auth-loss state. */
1704
1708
  clearTokens(): Promise<void>;
1709
+ /**
1710
+ * Whether a session (a stored token set) currently exists in the configured
1711
+ * TokenStorage. Use this to distinguish "genuinely logged out" from
1712
+ * "maybe-restorable" without reading the storage backend directly — so it
1713
+ * works with any TokenStorage (memory, IndexedDB, …), not just the default
1714
+ * localStorage one.
1715
+ */
1716
+ hasStoredSession(): Promise<boolean>;
1705
1717
  /** Disconnect realtime WebSocket (if connected) */
1706
1718
  disconnect(): void;
1707
1719
  /**
package/dist/index.d.ts CHANGED
@@ -372,12 +372,13 @@ type AuthLostListener = (reason: AuthLostReason) => void;
372
372
  declare class TokenManager {
373
373
  private storage;
374
374
  private refreshBufferSeconds;
375
+ private refreshLockName;
375
376
  private refreshCallback;
376
377
  private refreshPromise;
377
378
  private tokenRefreshedListeners;
378
379
  private authLostListeners;
379
380
  private authLostEmitted;
380
- constructor(storage?: TokenStorage, refreshBufferSeconds?: number);
381
+ constructor(storage?: TokenStorage, refreshBufferSeconds?: number, refreshLockName?: string);
381
382
  setRefreshCallback(callback: RefreshCallback): void;
382
383
  getAccessToken(): Promise<string | null>;
383
384
  setTokens(tokens: StoredTokens): Promise<void>;
@@ -398,6 +399,9 @@ declare class TokenManager {
398
399
  private shouldRefresh;
399
400
  private tryRefresh;
400
401
  private executeRefresh;
402
+ private doRefresh;
403
+ private safeGetTokens;
404
+ private withCrossTabLock;
401
405
  private emitTokenRefreshed;
402
406
  }
403
407
 
@@ -1702,6 +1706,14 @@ interface SpacelrClient {
1702
1706
  setTokens(tokens: StoredTokens): Promise<void>;
1703
1707
  /** Clear stored tokens and reset auth-loss state. */
1704
1708
  clearTokens(): Promise<void>;
1709
+ /**
1710
+ * Whether a session (a stored token set) currently exists in the configured
1711
+ * TokenStorage. Use this to distinguish "genuinely logged out" from
1712
+ * "maybe-restorable" without reading the storage backend directly — so it
1713
+ * works with any TokenStorage (memory, IndexedDB, …), not just the default
1714
+ * localStorage one.
1715
+ */
1716
+ hasStoredSession(): Promise<boolean>;
1705
1717
  /** Disconnect realtime WebSocket (if connected) */
1706
1718
  disconnect(): void;
1707
1719
  /**
package/dist/index.js CHANGED
@@ -428,7 +428,7 @@ var BrowserTokenStorage = class {
428
428
 
429
429
  // libs/sdk/src/core/token-manager.ts
430
430
  var TokenManager = class {
431
- constructor(storage, refreshBufferSeconds = 60) {
431
+ constructor(storage, refreshBufferSeconds = 60, refreshLockName = "spacelr_token_refresh") {
432
432
  this.refreshCallback = null;
433
433
  this.refreshPromise = null;
434
434
  this.tokenRefreshedListeners = /* @__PURE__ */ new Set();
@@ -439,6 +439,7 @@ var TokenManager = class {
439
439
  this.authLostEmitted = false;
440
440
  this.storage = storage ?? new MemoryTokenStorage();
441
441
  this.refreshBufferSeconds = refreshBufferSeconds;
442
+ this.refreshLockName = refreshLockName;
442
443
  }
443
444
  setRefreshCallback(callback) {
444
445
  this.refreshCallback = callback;
@@ -468,7 +469,7 @@ var TokenManager = class {
468
469
  this.authLostEmitted = false;
469
470
  }
470
471
  async getStoredTokens() {
471
- return this.storage.getTokens();
472
+ return this.safeGetTokens();
472
473
  }
473
474
  /**
474
475
  * Force a refresh using the current stored refresh token.
@@ -535,9 +536,18 @@ var TokenManager = class {
535
536
  }
536
537
  }
537
538
  async executeRefresh(refreshToken) {
539
+ return this.withCrossTabLock(() => this.doRefresh(refreshToken));
540
+ }
541
+ async doRefresh(refreshToken) {
538
542
  const callback = this.refreshCallback;
543
+ const current = await this.safeGetTokens();
544
+ if (current && current.refreshToken && current.refreshToken !== refreshToken && !this.isTokenExpired(current)) {
545
+ this.emitTokenRefreshed(current);
546
+ return current;
547
+ }
548
+ const activeRefreshToken = current?.refreshToken || refreshToken;
539
549
  try {
540
- const newTokens = await callback(refreshToken);
550
+ const newTokens = await callback(activeRefreshToken);
541
551
  await this.storage.setTokens(newTokens);
542
552
  this.emitTokenRefreshed(newTokens);
543
553
  return newTokens;
@@ -546,6 +556,22 @@ var TokenManager = class {
546
556
  throw error;
547
557
  }
548
558
  }
559
+ async safeGetTokens() {
560
+ try {
561
+ return await this.storage.getTokens();
562
+ } catch {
563
+ return null;
564
+ }
565
+ }
566
+ withCrossTabLock(fn) {
567
+ const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
568
+ if (!locks) return fn();
569
+ try {
570
+ return locks.request(this.refreshLockName, fn);
571
+ } catch {
572
+ return fn();
573
+ }
574
+ }
549
575
  emitTokenRefreshed(tokens) {
550
576
  for (const listener of this.tokenRefreshedListeners) {
551
577
  try {
@@ -1304,6 +1330,16 @@ function localStorageCursorStorage(prefix = "spacelr:cursor:") {
1304
1330
  }
1305
1331
 
1306
1332
  // libs/sdk/src/modules/auth.module.ts
1333
+ function mapProfile(u) {
1334
+ const name = u.name ?? ([u.firstName, u.lastName].filter(Boolean).join(" ") || void 0) ?? u.displayName;
1335
+ return {
1336
+ id: u.id ?? u._id ?? u.userId ?? u.sub ?? "",
1337
+ email: u.email ?? "",
1338
+ username: u.username,
1339
+ name,
1340
+ roles: u.roles ?? []
1341
+ };
1342
+ }
1307
1343
  var AuthModule = class {
1308
1344
  constructor(http, tokenManager, config) {
1309
1345
  this.stateListeners = /* @__PURE__ */ new Set();
@@ -1427,11 +1463,12 @@ var AuthModule = class {
1427
1463
  });
1428
1464
  }
1429
1465
  async getProfile() {
1430
- return this.http.request({
1466
+ const data = await this.http.request({
1431
1467
  method: "GET",
1432
1468
  path: "/auth/me",
1433
1469
  authenticated: true
1434
1470
  });
1471
+ return mapProfile(data.user ?? data);
1435
1472
  }
1436
1473
  async logout() {
1437
1474
  try {
@@ -3063,7 +3100,8 @@ function createClient(config) {
3063
3100
  const tokenStorage = config.tokenStorage ?? (typeof window !== "undefined" && typeof window.localStorage !== "undefined" ? new BrowserTokenStorage() : new MemoryTokenStorage());
3064
3101
  const tokenManager = new TokenManager(
3065
3102
  tokenStorage,
3066
- config.refreshBufferSeconds ?? 60
3103
+ config.refreshBufferSeconds ?? 60,
3104
+ `spacelr_token_refresh:${config.projectId}`
3067
3105
  );
3068
3106
  const httpClient = new HttpClient(config, tokenManager);
3069
3107
  const realtime = new RealtimeClient({
@@ -3090,6 +3128,9 @@ function createClient(config) {
3090
3128
  clearTokens() {
3091
3129
  return tokenManager.clearTokens();
3092
3130
  },
3131
+ hasStoredSession() {
3132
+ return tokenManager.getStoredTokens().then((tokens) => !!tokens);
3133
+ },
3093
3134
  disconnect() {
3094
3135
  realtime.disconnect();
3095
3136
  },