@23blocks/sdk 13.4.0 → 13.5.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/CHANGELOG.md +15 -0
- package/dist/index.esm.js +302 -4
- package/dist/src/lib/client.d.ts +27 -0
- package/dist/src/lib/client.d.ts.map +1 -1
- package/dist/src/lib/sdk.d.ts +2 -1
- package/dist/src/lib/sdk.d.ts.map +1 -1
- package/dist/src/lib/token-lifecycle.d.ts +95 -0
- package/dist/src/lib/token-lifecycle.d.ts.map +1 -0
- package/dist/src/lib/token-manager.d.ts +6 -0
- package/dist/src/lib/token-manager.d.ts.map +1 -1
- package/llms.txt +7 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
## 13.5.0 (2026-03-14)
|
|
2
|
+
|
|
3
|
+
### 🚀 Features
|
|
4
|
+
|
|
5
|
+
- **@23blocks/sdk:** add token lifecycle management with auto-refresh and 401 retry ([5426358](https://github.com/23blocks-OS/frontend-sdk/commit/5426358))
|
|
6
|
+
|
|
7
|
+
### 📖 Documentation
|
|
8
|
+
|
|
9
|
+
- add token lifecycle to llms.txt ([f42d730](https://github.com/23blocks-OS/frontend-sdk/commit/f42d730))
|
|
10
|
+
|
|
11
|
+
### ❤️ Thank You
|
|
12
|
+
|
|
13
|
+
- Claude Opus 4.6
|
|
14
|
+
- Juan Pelaez
|
|
15
|
+
|
|
1
16
|
## 13.4.0 (2026-03-09)
|
|
2
17
|
|
|
3
18
|
### 🚀 Features
|
package/dist/index.esm.js
CHANGED
|
@@ -55,6 +55,7 @@ export { blockOnboarding as onboarding };
|
|
|
55
55
|
import { createUniversityBlock } from '@23blocks/block-university';
|
|
56
56
|
import * as blockUniversity from '@23blocks/block-university';
|
|
57
57
|
export { blockUniversity as university };
|
|
58
|
+
import { BlockErrorException } from '@23blocks/contracts';
|
|
58
59
|
export * from '@23blocks/contracts';
|
|
59
60
|
export * from '@23blocks/jsonapi-codec';
|
|
60
61
|
import * as blockRag from '@23blocks/block-rag';
|
|
@@ -204,6 +205,252 @@ export { blockRag as rag };
|
|
|
204
205
|
};
|
|
205
206
|
}
|
|
206
207
|
|
|
208
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
209
|
+
// JWT Decode Utility
|
|
210
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
211
|
+
/**
|
|
212
|
+
* Decode the `exp` claim from a JWT without external dependencies.
|
|
213
|
+
* Returns the expiry as a Unix timestamp (seconds), or null if unavailable.
|
|
214
|
+
*/ function decodeJwtExp(token) {
|
|
215
|
+
try {
|
|
216
|
+
const parts = token.split('.');
|
|
217
|
+
if (parts.length !== 3) return null;
|
|
218
|
+
// Base64url → Base64
|
|
219
|
+
let payload = parts[1];
|
|
220
|
+
payload = payload.replace(/-/g, '+').replace(/_/g, '/');
|
|
221
|
+
// Pad to multiple of 4
|
|
222
|
+
const pad = payload.length % 4;
|
|
223
|
+
if (pad) {
|
|
224
|
+
payload += '='.repeat(4 - pad);
|
|
225
|
+
}
|
|
226
|
+
// Decode — works in browser (atob) and Node 16+ (Buffer)
|
|
227
|
+
let decoded;
|
|
228
|
+
if (typeof atob === 'function') {
|
|
229
|
+
decoded = atob(payload);
|
|
230
|
+
} else if (typeof Buffer !== 'undefined') {
|
|
231
|
+
decoded = Buffer.from(payload, 'base64').toString('utf-8');
|
|
232
|
+
} else {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
const parsed = JSON.parse(decoded);
|
|
236
|
+
if (typeof parsed.exp === 'number') {
|
|
237
|
+
return parsed.exp;
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
} catch (e) {
|
|
241
|
+
return null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
245
|
+
// Lifecycle Manager Factory
|
|
246
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
247
|
+
/**
|
|
248
|
+
* Create a token lifecycle manager that automatically refreshes tokens,
|
|
249
|
+
* handles tab visibility, and notifies listeners of auth state changes.
|
|
250
|
+
*
|
|
251
|
+
* @param tokenManager - Token storage (read/write tokens)
|
|
252
|
+
* @param refreshFn - Function to call the backend refresh endpoint
|
|
253
|
+
* @param config - Lifecycle configuration
|
|
254
|
+
*/ function createTokenLifecycleManager(tokenManager, refreshFn, config = {}) {
|
|
255
|
+
const { refreshBufferSeconds = 120, enableVisibilityRefresh = true, enableProactiveRefresh = true } = config;
|
|
256
|
+
const listeners = new Set();
|
|
257
|
+
let refreshTimer = null;
|
|
258
|
+
let refreshPromise = null;
|
|
259
|
+
let visibilityHandler = null;
|
|
260
|
+
let destroyed = false;
|
|
261
|
+
let running = false;
|
|
262
|
+
function notify(event) {
|
|
263
|
+
listeners.forEach((listener)=>{
|
|
264
|
+
try {
|
|
265
|
+
listener(event);
|
|
266
|
+
} catch (e) {
|
|
267
|
+
// Listener errors should not break the lifecycle
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function clearTimer() {
|
|
272
|
+
if (refreshTimer !== null) {
|
|
273
|
+
clearTimeout(refreshTimer);
|
|
274
|
+
refreshTimer = null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function scheduleRefresh() {
|
|
278
|
+
if (!enableProactiveRefresh || destroyed || !running) return;
|
|
279
|
+
clearTimer();
|
|
280
|
+
const accessToken = tokenManager.getAccessToken();
|
|
281
|
+
if (!accessToken) return;
|
|
282
|
+
const exp = decodeJwtExp(accessToken);
|
|
283
|
+
if (!exp) return;
|
|
284
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
285
|
+
const secondsUntilExpiry = exp - nowSeconds;
|
|
286
|
+
const refreshInSeconds = secondsUntilExpiry - refreshBufferSeconds;
|
|
287
|
+
if (refreshInSeconds <= 0) {
|
|
288
|
+
// Token is already expired or within buffer — refresh immediately
|
|
289
|
+
refreshNow().catch(()=>{
|
|
290
|
+
// Error handled inside refreshNow
|
|
291
|
+
});
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
refreshTimer = setTimeout(()=>{
|
|
295
|
+
if (!destroyed && running) {
|
|
296
|
+
refreshNow().catch(()=>{
|
|
297
|
+
// Error handled inside refreshNow
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}, refreshInSeconds * 1000);
|
|
301
|
+
}
|
|
302
|
+
function handleVisibilityChange() {
|
|
303
|
+
if (destroyed || !running) return;
|
|
304
|
+
if (typeof document === 'undefined') return;
|
|
305
|
+
if (document.visibilityState === 'visible') {
|
|
306
|
+
const accessToken = tokenManager.getAccessToken();
|
|
307
|
+
if (!accessToken) return;
|
|
308
|
+
const exp = decodeJwtExp(accessToken);
|
|
309
|
+
if (!exp) {
|
|
310
|
+
// No exp claim — refresh to be safe
|
|
311
|
+
refreshNow().catch(()=>{});
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
315
|
+
const secondsUntilExpiry = exp - nowSeconds;
|
|
316
|
+
// Refresh if expired or within buffer
|
|
317
|
+
if (secondsUntilExpiry <= refreshBufferSeconds) {
|
|
318
|
+
refreshNow().catch(()=>{});
|
|
319
|
+
} else {
|
|
320
|
+
// Reschedule proactive refresh (timer may have drifted during sleep)
|
|
321
|
+
scheduleRefresh();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
function registerVisibilityListener() {
|
|
326
|
+
if (!enableVisibilityRefresh || !isBrowser$1() || visibilityHandler) return;
|
|
327
|
+
if (typeof document !== 'undefined' && typeof document.addEventListener === 'function') {
|
|
328
|
+
visibilityHandler = handleVisibilityChange;
|
|
329
|
+
document.addEventListener('visibilitychange', visibilityHandler);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
function removeVisibilityListener() {
|
|
333
|
+
if (visibilityHandler && typeof document !== 'undefined') {
|
|
334
|
+
document.removeEventListener('visibilitychange', visibilityHandler);
|
|
335
|
+
visibilityHandler = null;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
async function refreshNow() {
|
|
339
|
+
if (destroyed) {
|
|
340
|
+
throw new Error('[23blocks] Token lifecycle manager has been destroyed');
|
|
341
|
+
}
|
|
342
|
+
// Concurrency lock — all callers share the same in-flight promise
|
|
343
|
+
if (refreshPromise) {
|
|
344
|
+
return refreshPromise;
|
|
345
|
+
}
|
|
346
|
+
refreshPromise = (async ()=>{
|
|
347
|
+
try {
|
|
348
|
+
const refreshToken = tokenManager.getRefreshToken();
|
|
349
|
+
if (!refreshToken) {
|
|
350
|
+
throw new Error('No refresh token available');
|
|
351
|
+
}
|
|
352
|
+
const result = await refreshFn(refreshToken);
|
|
353
|
+
// Store new tokens
|
|
354
|
+
tokenManager.setTokens(result.accessToken, result.refreshToken);
|
|
355
|
+
// Reschedule proactive refresh
|
|
356
|
+
scheduleRefresh();
|
|
357
|
+
notify('TOKEN_REFRESHED');
|
|
358
|
+
return result.accessToken;
|
|
359
|
+
} catch (error) {
|
|
360
|
+
// Refresh failed — session is dead
|
|
361
|
+
clearTimer();
|
|
362
|
+
tokenManager.clearTokens();
|
|
363
|
+
running = false;
|
|
364
|
+
notify('SESSION_EXPIRED');
|
|
365
|
+
throw error;
|
|
366
|
+
} finally{
|
|
367
|
+
refreshPromise = null;
|
|
368
|
+
}
|
|
369
|
+
})();
|
|
370
|
+
return refreshPromise;
|
|
371
|
+
}
|
|
372
|
+
function start() {
|
|
373
|
+
if (destroyed) return;
|
|
374
|
+
running = true;
|
|
375
|
+
scheduleRefresh();
|
|
376
|
+
registerVisibilityListener();
|
|
377
|
+
}
|
|
378
|
+
function stop() {
|
|
379
|
+
running = false;
|
|
380
|
+
clearTimer();
|
|
381
|
+
refreshPromise = null;
|
|
382
|
+
}
|
|
383
|
+
function destroy() {
|
|
384
|
+
destroyed = true;
|
|
385
|
+
stop();
|
|
386
|
+
removeVisibilityListener();
|
|
387
|
+
listeners.clear();
|
|
388
|
+
}
|
|
389
|
+
function onAuthStateChanged(listener) {
|
|
390
|
+
listeners.add(listener);
|
|
391
|
+
return ()=>{
|
|
392
|
+
listeners.delete(listener);
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
return {
|
|
396
|
+
start,
|
|
397
|
+
stop,
|
|
398
|
+
onAuthStateChanged,
|
|
399
|
+
refreshNow,
|
|
400
|
+
destroy
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
404
|
+
// Retrying Transport Wrapper
|
|
405
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
406
|
+
/**
|
|
407
|
+
* Wrap a transport with automatic 401 retry via token refresh.
|
|
408
|
+
*
|
|
409
|
+
* On a 401 BlockErrorException, the wrapper calls `getLifecycle().refreshNow()`
|
|
410
|
+
* to obtain a fresh token, then retries the request once.
|
|
411
|
+
*
|
|
412
|
+
* @param baseTransport - The underlying HTTP transport
|
|
413
|
+
* @param getLifecycle - Lazy getter for the lifecycle manager (supports React refs and late init)
|
|
414
|
+
*/ function createRetryingTransport(baseTransport, getLifecycle) {
|
|
415
|
+
async function withRetry(fn) {
|
|
416
|
+
try {
|
|
417
|
+
return await fn();
|
|
418
|
+
} catch (error) {
|
|
419
|
+
if (error instanceof BlockErrorException && error.status === 401) {
|
|
420
|
+
const lifecycle = getLifecycle();
|
|
421
|
+
if (lifecycle) {
|
|
422
|
+
try {
|
|
423
|
+
await lifecycle.refreshNow();
|
|
424
|
+
// Retry once — transport reads fresh token from tokenManager on next call
|
|
425
|
+
return await fn();
|
|
426
|
+
} catch (e) {
|
|
427
|
+
// Refresh failed — throw original 401
|
|
428
|
+
throw error;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
throw error;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
return {
|
|
436
|
+
get (path, options) {
|
|
437
|
+
return withRetry(()=>baseTransport.get(path, options));
|
|
438
|
+
},
|
|
439
|
+
post (path, body, options) {
|
|
440
|
+
return withRetry(()=>baseTransport.post(path, body, options));
|
|
441
|
+
},
|
|
442
|
+
patch (path, body, options) {
|
|
443
|
+
return withRetry(()=>baseTransport.patch(path, body, options));
|
|
444
|
+
},
|
|
445
|
+
put (path, body, options) {
|
|
446
|
+
return withRetry(()=>baseTransport.put(path, body, options));
|
|
447
|
+
},
|
|
448
|
+
delete (path, options) {
|
|
449
|
+
return withRetry(()=>baseTransport.delete(path, options));
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
|
|
207
454
|
/**
|
|
208
455
|
* Detect browser environment.
|
|
209
456
|
* Uses try/catch to handle edge runtimes (Lambda@Edge, Cloudflare Workers)
|
|
@@ -269,7 +516,7 @@ export { blockRag as rag };
|
|
|
269
516
|
* });
|
|
270
517
|
* ```
|
|
271
518
|
*/ function create23BlocksClient(config) {
|
|
272
|
-
const { urls, apiKey, tenantId, authMode = 'token', storage = isBrowser() ? 'localStorage' : 'memory', headers: staticHeaders = {}, timeout } = config;
|
|
519
|
+
const { urls, apiKey, tenantId, authMode = 'token', storage = isBrowser() ? 'localStorage' : 'memory', headers: staticHeaders = {}, timeout, tokenLifecycle: lifecycleConfig = {} } = config;
|
|
273
520
|
// Create token manager for token mode
|
|
274
521
|
let tokenManager = null;
|
|
275
522
|
if (authMode === 'token') {
|
|
@@ -279,8 +526,11 @@ export { blockRag as rag };
|
|
|
279
526
|
storage
|
|
280
527
|
});
|
|
281
528
|
}
|
|
282
|
-
//
|
|
283
|
-
|
|
529
|
+
// Token lifecycle manager (created lazily after auth block exists)
|
|
530
|
+
let lifecycle = null;
|
|
531
|
+
const lifecycleEnabled = authMode === 'token' && lifecycleConfig !== false;
|
|
532
|
+
// Factory to create base transport for a specific service URL
|
|
533
|
+
function createBaseTransport(baseUrl) {
|
|
284
534
|
return createHttpTransport({
|
|
285
535
|
baseUrl,
|
|
286
536
|
timeout,
|
|
@@ -303,6 +553,14 @@ export { blockRag as rag };
|
|
|
303
553
|
}
|
|
304
554
|
});
|
|
305
555
|
}
|
|
556
|
+
// Factory to create transport with optional 401 retry
|
|
557
|
+
function createServiceTransport(baseUrl) {
|
|
558
|
+
const base = createBaseTransport(baseUrl);
|
|
559
|
+
if (lifecycleEnabled) {
|
|
560
|
+
return createRetryingTransport(base, ()=>lifecycle);
|
|
561
|
+
}
|
|
562
|
+
return base;
|
|
563
|
+
}
|
|
306
564
|
// Helper to create a proxy that throws when accessing unconfigured service
|
|
307
565
|
function createUnconfiguredServiceProxy(serviceName, urlKey) {
|
|
308
566
|
return new Proxy({}, {
|
|
@@ -335,12 +593,31 @@ export { blockRag as rag };
|
|
|
335
593
|
const jarvisBlock = urls.jarvis ? createJarvisBlock(createServiceTransport(urls.jarvis), blockConfig) : null;
|
|
336
594
|
const onboardingBlock = urls.onboarding ? createOnboardingBlock(createServiceTransport(urls.onboarding), blockConfig) : null;
|
|
337
595
|
const universityBlock = urls.university ? createUniversityBlock(createServiceTransport(urls.university), blockConfig) : null;
|
|
596
|
+
// Create lifecycle manager if enabled and auth block is available
|
|
597
|
+
if (lifecycleEnabled && tokenManager && authenticationBlock) {
|
|
598
|
+
const lifecycleRefreshFn = async (refreshToken)=>{
|
|
599
|
+
const response = await authenticationBlock.auth.refreshToken({
|
|
600
|
+
refreshToken
|
|
601
|
+
});
|
|
602
|
+
return {
|
|
603
|
+
accessToken: response.accessToken,
|
|
604
|
+
refreshToken: response.refreshToken,
|
|
605
|
+
expiresIn: response.expiresIn
|
|
606
|
+
};
|
|
607
|
+
};
|
|
608
|
+
lifecycle = createTokenLifecycleManager(tokenManager, lifecycleRefreshFn, typeof lifecycleConfig === 'object' ? lifecycleConfig : {});
|
|
609
|
+
// Auto-start if tokens already exist (page reload scenario)
|
|
610
|
+
if (tokenManager.getAccessToken() && tokenManager.getRefreshToken()) {
|
|
611
|
+
lifecycle.start();
|
|
612
|
+
}
|
|
613
|
+
}
|
|
338
614
|
// Create managed auth service with automatic token handling (only if auth URL configured)
|
|
339
615
|
const managedAuth = authenticationBlock ? {
|
|
340
616
|
async signIn (request) {
|
|
341
617
|
const response = await authenticationBlock.auth.signIn(request);
|
|
342
618
|
if (authMode === 'token' && tokenManager && response.accessToken) {
|
|
343
619
|
tokenManager.setTokens(response.accessToken, response.refreshToken);
|
|
620
|
+
lifecycle == null ? void 0 : lifecycle.start();
|
|
344
621
|
}
|
|
345
622
|
return response;
|
|
346
623
|
},
|
|
@@ -352,6 +629,7 @@ export { blockRag as rag };
|
|
|
352
629
|
return response;
|
|
353
630
|
},
|
|
354
631
|
async signOut () {
|
|
632
|
+
lifecycle == null ? void 0 : lifecycle.stop();
|
|
355
633
|
await authenticationBlock.auth.signOut();
|
|
356
634
|
if (authMode === 'token' && tokenManager) {
|
|
357
635
|
tokenManager.clearTokens();
|
|
@@ -361,6 +639,7 @@ export { blockRag as rag };
|
|
|
361
639
|
const response = await authenticationBlock.auth.verifyMagicLink(request);
|
|
362
640
|
if (authMode === 'token' && tokenManager && response.accessToken) {
|
|
363
641
|
tokenManager.setTokens(response.accessToken, response.refreshToken);
|
|
642
|
+
lifecycle == null ? void 0 : lifecycle.start();
|
|
364
643
|
}
|
|
365
644
|
return response;
|
|
366
645
|
},
|
|
@@ -368,6 +647,7 @@ export { blockRag as rag };
|
|
|
368
647
|
const response = await authenticationBlock.auth.acceptInvitation(request);
|
|
369
648
|
if (authMode === 'token' && tokenManager && response.accessToken) {
|
|
370
649
|
tokenManager.setTokens(response.accessToken, response.refreshToken);
|
|
650
|
+
lifecycle == null ? void 0 : lifecycle.start();
|
|
371
651
|
}
|
|
372
652
|
return response;
|
|
373
653
|
},
|
|
@@ -375,6 +655,7 @@ export { blockRag as rag };
|
|
|
375
655
|
const response = await authenticationBlock.auth.verifyPasswordOtp(request);
|
|
376
656
|
if (authMode === 'token' && tokenManager && response.accessToken) {
|
|
377
657
|
tokenManager.setTokens(response.accessToken, response.refreshToken);
|
|
658
|
+
lifecycle == null ? void 0 : lifecycle.start();
|
|
378
659
|
}
|
|
379
660
|
return response;
|
|
380
661
|
},
|
|
@@ -445,8 +726,25 @@ export { blockRag as rag };
|
|
|
445
726
|
return null;
|
|
446
727
|
}
|
|
447
728
|
return tokenManager ? !!tokenManager.getAccessToken() : false;
|
|
729
|
+
},
|
|
730
|
+
onAuthStateChanged (listener) {
|
|
731
|
+
if (lifecycle) {
|
|
732
|
+
return lifecycle.onAuthStateChanged(listener);
|
|
733
|
+
}
|
|
734
|
+
// No lifecycle — return no-op unsubscribe
|
|
735
|
+
return ()=>{};
|
|
736
|
+
},
|
|
737
|
+
async refreshSession () {
|
|
738
|
+
if (!lifecycle) {
|
|
739
|
+
throw new Error('[23blocks] Token lifecycle is not available. ' + 'Ensure authMode is "token" and tokenLifecycle is not disabled.');
|
|
740
|
+
}
|
|
741
|
+
return lifecycle.refreshNow();
|
|
742
|
+
},
|
|
743
|
+
destroy () {
|
|
744
|
+
lifecycle == null ? void 0 : lifecycle.destroy();
|
|
745
|
+
lifecycle = null;
|
|
448
746
|
}
|
|
449
747
|
};
|
|
450
748
|
}
|
|
451
749
|
|
|
452
|
-
export { create23BlocksClient, createTokenManager };
|
|
750
|
+
export { create23BlocksClient, createRetryingTransport, createTokenLifecycleManager, createTokenManager, isBrowser$1 as isBrowser };
|
package/dist/src/lib/client.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { type JarvisBlock } from '@23blocks/block-jarvis';
|
|
|
17
17
|
import { type OnboardingBlock } from '@23blocks/block-onboarding';
|
|
18
18
|
import { type UniversityBlock } from '@23blocks/block-university';
|
|
19
19
|
import { type StorageType } from './token-manager.js';
|
|
20
|
+
import { type TokenLifecycleConfig, type AuthStateListener } from './token-lifecycle.js';
|
|
20
21
|
/**
|
|
21
22
|
* Authentication mode
|
|
22
23
|
* - 'token': Store tokens in browser storage, attach Authorization header
|
|
@@ -114,6 +115,16 @@ export interface ClientConfig {
|
|
|
114
115
|
* @default 30000
|
|
115
116
|
*/
|
|
116
117
|
timeout?: number;
|
|
118
|
+
/**
|
|
119
|
+
* Token lifecycle configuration for automatic refresh and 401 retry.
|
|
120
|
+
* - Pass an object to customize (e.g., `{ refreshBufferSeconds: 60 }`)
|
|
121
|
+
* - Pass `false` to disable entirely
|
|
122
|
+
* - Omit or pass `{}` to use defaults (enabled with 120s buffer)
|
|
123
|
+
*
|
|
124
|
+
* Only applies in token mode. Ignored in cookie mode.
|
|
125
|
+
* @default {} (enabled with defaults)
|
|
126
|
+
*/
|
|
127
|
+
tokenLifecycle?: TokenLifecycleConfig | false;
|
|
117
128
|
}
|
|
118
129
|
/**
|
|
119
130
|
* Auth service wrapper with automatic token management
|
|
@@ -286,6 +297,22 @@ export interface Blocks23Client {
|
|
|
286
297
|
* In cookie mode: always returns null (check with validateToken instead)
|
|
287
298
|
*/
|
|
288
299
|
isAuthenticated(): boolean | null;
|
|
300
|
+
/**
|
|
301
|
+
* Subscribe to auth state changes (token refreshed, session expired, etc.).
|
|
302
|
+
* Returns an unsubscribe function.
|
|
303
|
+
* Only active when tokenLifecycle is enabled (token mode).
|
|
304
|
+
*/
|
|
305
|
+
onAuthStateChanged(listener: AuthStateListener): () => void;
|
|
306
|
+
/**
|
|
307
|
+
* Force an immediate token refresh.
|
|
308
|
+
* Returns the new access token. Throws if no lifecycle or refresh fails.
|
|
309
|
+
*/
|
|
310
|
+
refreshSession(): Promise<string>;
|
|
311
|
+
/**
|
|
312
|
+
* Destroy the client — stops lifecycle timers and cleans up listeners.
|
|
313
|
+
* Call when the client is no longer needed (e.g., component unmount).
|
|
314
|
+
*/
|
|
315
|
+
destroy(): void;
|
|
289
316
|
}
|
|
290
317
|
/**
|
|
291
318
|
* Create a 23blocks client instance
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/client.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC9B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAuB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACnF,OAAO,EAAkB,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAA0B,KAAK,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC5F,OAAO,EAA4B,KAAK,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAClG,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACtF,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAsB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC7E,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACzF,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAEzF,OAAO,EAAsB,KAAK,WAAW,EAAqB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/lib/client.ts"],"names":[],"mappings":"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"}
|
package/dist/src/lib/sdk.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { create23BlocksClient, type AuthMode, type ClientConfig, type ServiceUrls, type Blocks23Client, type ManagedAuthService, type StorageType, type TokenManager, } from './client.js';
|
|
2
|
-
export { createTokenManager } from './token-manager.js';
|
|
2
|
+
export { createTokenManager, isBrowser } from './token-manager.js';
|
|
3
|
+
export { createTokenLifecycleManager, createRetryingTransport, type AuthStateEvent, type AuthStateListener, type RefreshTokenFn, type TokenLifecycleConfig, type TokenLifecycleManager, } from './token-lifecycle.js';
|
|
3
4
|
export * from '@23blocks/contracts';
|
|
4
5
|
export * from '@23blocks/jsonapi-codec';
|
|
5
6
|
export * from '@23blocks/transport-http';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../../src/lib/sdk.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,oBAAoB,EACpB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"sdk.d.ts","sourceRoot":"","sources":["../../../src/lib/sdk.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,oBAAoB,EACpB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAEnE,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,GAC3B,MAAM,sBAAsB,CAAC;AAM9B,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AAqBzC,OAAO,KAAK,cAAc,MAAM,gCAAgC,CAAC;AACjE,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,QAAQ,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAC3C,OAAO,KAAK,OAAO,MAAM,yBAAyB,CAAC;AACnD,OAAO,KAAK,WAAW,MAAM,6BAA6B,CAAC;AAC3D,OAAO,KAAK,aAAa,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,KAAK,MAAM,uBAAuB,CAAC;AAC/C,OAAO,KAAK,KAAK,MAAM,uBAAuB,CAAC;AAC/C,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,SAAS,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,OAAO,MAAM,yBAAyB,CAAC;AACnD,OAAO,KAAK,OAAO,MAAM,yBAAyB,CAAC;AACnD,OAAO,KAAK,KAAK,MAAM,uBAAuB,CAAC;AAC/C,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,MAAM,MAAM,wBAAwB,CAAC;AACjD,OAAO,KAAK,UAAU,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,UAAU,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,GAAG,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { Transport } from '@23blocks/contracts';
|
|
2
|
+
import type { TokenManager } from './token-manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* Auth state change events emitted by the lifecycle manager.
|
|
5
|
+
*
|
|
6
|
+
* - `SIGNED_IN` — User signed in and lifecycle started
|
|
7
|
+
* - `SIGNED_OUT` — User signed out and lifecycle stopped
|
|
8
|
+
* - `TOKEN_REFRESHED` — Access token was silently refreshed
|
|
9
|
+
* - `SESSION_EXPIRED` — Refresh failed, tokens cleared, user must re-authenticate
|
|
10
|
+
*/
|
|
11
|
+
export type AuthStateEvent = 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'SESSION_EXPIRED';
|
|
12
|
+
/**
|
|
13
|
+
* Callback invoked when auth state changes
|
|
14
|
+
*/
|
|
15
|
+
export type AuthStateListener = (event: AuthStateEvent) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Function that performs the actual token refresh against the backend.
|
|
18
|
+
* Accepts the current refresh token and returns new credentials.
|
|
19
|
+
*/
|
|
20
|
+
export type RefreshTokenFn = (refreshToken: string) => Promise<{
|
|
21
|
+
accessToken: string;
|
|
22
|
+
refreshToken?: string;
|
|
23
|
+
expiresIn?: number;
|
|
24
|
+
}>;
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for automatic token lifecycle management
|
|
27
|
+
*/
|
|
28
|
+
export interface TokenLifecycleConfig {
|
|
29
|
+
/**
|
|
30
|
+
* Seconds before token expiry to trigger a proactive refresh.
|
|
31
|
+
* @default 120
|
|
32
|
+
*/
|
|
33
|
+
refreshBufferSeconds?: number;
|
|
34
|
+
/**
|
|
35
|
+
* Refresh token when tab becomes visible (handles laptop sleep/wake).
|
|
36
|
+
* @default true
|
|
37
|
+
*/
|
|
38
|
+
enableVisibilityRefresh?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Schedule proactive refresh based on JWT `exp` claim.
|
|
41
|
+
* @default true
|
|
42
|
+
*/
|
|
43
|
+
enableProactiveRefresh?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Token lifecycle manager interface.
|
|
47
|
+
* Manages automatic token refresh, tab visibility sync, and auth state notifications.
|
|
48
|
+
*/
|
|
49
|
+
export interface TokenLifecycleManager {
|
|
50
|
+
/**
|
|
51
|
+
* Start the lifecycle — schedule proactive refresh and register visibility listener.
|
|
52
|
+
* Call after successful sign-in.
|
|
53
|
+
*/
|
|
54
|
+
start(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Stop the lifecycle — clear timers and listeners but keep the manager reusable.
|
|
57
|
+
* Call on sign-out.
|
|
58
|
+
*/
|
|
59
|
+
stop(): void;
|
|
60
|
+
/**
|
|
61
|
+
* Subscribe to auth state changes.
|
|
62
|
+
* Returns an unsubscribe function.
|
|
63
|
+
*/
|
|
64
|
+
onAuthStateChanged(listener: AuthStateListener): () => void;
|
|
65
|
+
/**
|
|
66
|
+
* Force an immediate token refresh.
|
|
67
|
+
* Multiple concurrent calls share the same in-flight promise (concurrency lock).
|
|
68
|
+
* Returns the new access token on success.
|
|
69
|
+
*/
|
|
70
|
+
refreshNow(): Promise<string>;
|
|
71
|
+
/**
|
|
72
|
+
* Permanently destroy the manager — clears everything and prevents reuse.
|
|
73
|
+
*/
|
|
74
|
+
destroy(): void;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a token lifecycle manager that automatically refreshes tokens,
|
|
78
|
+
* handles tab visibility, and notifies listeners of auth state changes.
|
|
79
|
+
*
|
|
80
|
+
* @param tokenManager - Token storage (read/write tokens)
|
|
81
|
+
* @param refreshFn - Function to call the backend refresh endpoint
|
|
82
|
+
* @param config - Lifecycle configuration
|
|
83
|
+
*/
|
|
84
|
+
export declare function createTokenLifecycleManager(tokenManager: TokenManager, refreshFn: RefreshTokenFn, config?: TokenLifecycleConfig): TokenLifecycleManager;
|
|
85
|
+
/**
|
|
86
|
+
* Wrap a transport with automatic 401 retry via token refresh.
|
|
87
|
+
*
|
|
88
|
+
* On a 401 BlockErrorException, the wrapper calls `getLifecycle().refreshNow()`
|
|
89
|
+
* to obtain a fresh token, then retries the request once.
|
|
90
|
+
*
|
|
91
|
+
* @param baseTransport - The underlying HTTP transport
|
|
92
|
+
* @param getLifecycle - Lazy getter for the lifecycle manager (supports React refs and late init)
|
|
93
|
+
*/
|
|
94
|
+
export declare function createRetryingTransport(baseTransport: Transport, getLifecycle: () => TokenLifecycleManager | null): Transport;
|
|
95
|
+
//# sourceMappingURL=token-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-lifecycle.d.ts","sourceRoot":"","sources":["../../../src/lib/token-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAkB,MAAM,qBAAqB,CAAC;AAErE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAOvD;;;;;;;GAOG;AACH,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,YAAY,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;AAEhG;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;AAEhE;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;IAC7D,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;;OAGG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAElC;;;OAGG;IACH,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,KAAK,IAAI,IAAI,CAAC;IAEd;;;OAGG;IACH,IAAI,IAAI,IAAI,CAAC;IAEb;;;OAGG;IACH,kBAAkB,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,IAAI,CAAC;IAE5D;;;;OAIG;IACH,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE9B;;OAEG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAiDD;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,cAAc,EACzB,MAAM,GAAE,oBAAyB,GAChC,qBAAqB,CAuLvB;AAMD;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,aAAa,EAAE,SAAS,EACxB,YAAY,EAAE,MAAM,qBAAqB,GAAG,IAAI,GAC/C,SAAS,CAuCX"}
|
|
@@ -47,6 +47,12 @@ export interface TokenManager {
|
|
|
47
47
|
*/
|
|
48
48
|
onStorageChange(callback: () => void): () => void;
|
|
49
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Detect if we're running in a browser environment.
|
|
52
|
+
* Uses try/catch to handle edge runtimes (Lambda@Edge, Cloudflare Workers)
|
|
53
|
+
* that may throw on property access.
|
|
54
|
+
*/
|
|
55
|
+
export declare function isBrowser(): boolean;
|
|
50
56
|
/**
|
|
51
57
|
* Create a token manager instance
|
|
52
58
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../../../src/lib/token-manager.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,gBAAgB,GAAG,QAAQ,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,cAAc,IAAI,MAAM,GAAG,IAAI,CAAC;IAEhC;;OAEG;IACH,eAAe,IAAI,MAAM,GAAG,IAAI,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5D;;OAEG;IACH,WAAW,IAAI,IAAI,CAAC;IAEpB;;;OAGG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CACnD;
|
|
1
|
+
{"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../../../src/lib/token-manager.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,cAAc,GAAG,gBAAgB,GAAG,QAAQ,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,cAAc,IAAI,MAAM,GAAG,IAAI,CAAC;IAEhC;;OAEG;IACH,eAAe,IAAI,MAAM,GAAG,IAAI,CAAC;IAEjC;;OAEG;IACH,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE5D;;OAEG;IACH,WAAW,IAAI,IAAI,CAAC;IAEpB;;;OAGG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CACnD;AA6BD;;;;GAIG;AACH,wBAAgB,SAAS,IAAI,OAAO,CASnC;AAuBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAwE3E"}
|
package/llms.txt
CHANGED
|
@@ -294,6 +294,13 @@ The client provides:
|
|
|
294
294
|
- `client.authentication` - Full authentication block
|
|
295
295
|
- `client.{blockName}` - All other blocks
|
|
296
296
|
- `client.getAccessToken()`, `client.setTokens()`, `client.clearSession()` - Token utilities
|
|
297
|
+
- `client.onAuthStateChanged(listener)` - Subscribe to auth state changes (TOKEN_REFRESHED, SESSION_EXPIRED)
|
|
298
|
+
- `client.refreshSession()` - Force immediate token refresh
|
|
299
|
+
- `client.destroy()` - Cleanup lifecycle timers and listeners
|
|
300
|
+
|
|
301
|
+
### Token Lifecycle (Auto-Refresh & 401 Retry)
|
|
302
|
+
|
|
303
|
+
Enabled by default in token mode. Handles proactive refresh (JWT exp decode, 120s buffer), 401 retry (refresh + retry once), tab visibility refresh (sleep/wake), concurrency lock, and session expiry (clears tokens, emits SESSION_EXPIRED). Disable with `tokenLifecycle: false`. Customize with `tokenLifecycle: { refreshBufferSeconds: 60 }`.
|
|
297
304
|
|
|
298
305
|
## Health Check
|
|
299
306
|
|