@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 +13 -1
- package/dist/index.d.ts +13 -1
- package/dist/index.js +46 -5
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +46 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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.
|
|
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(
|
|
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
|
-
|
|
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
|
},
|