playkit-sdk 1.2.8-beta.2 → 1.2.8-beta.4
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/playkit-sdk.cjs.js +236 -4
- package/dist/playkit-sdk.cjs.js.map +1 -1
- package/dist/playkit-sdk.d.ts +106 -1
- package/dist/playkit-sdk.esm.js +236 -4
- package/dist/playkit-sdk.esm.js.map +1 -1
- package/dist/playkit-sdk.umd.js +236 -4
- package/dist/playkit-sdk.umd.js.map +1 -1
- package/package.json +1 -1
package/dist/playkit-sdk.cjs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* playkit-sdk v1.2.8-beta.
|
|
2
|
+
* playkit-sdk v1.2.8-beta.4
|
|
3
3
|
* PlayKit SDK for JavaScript
|
|
4
4
|
* @license SEE LICENSE IN LICENSE
|
|
5
5
|
*/
|
|
@@ -2394,6 +2394,7 @@ class DeviceAuthFlowManager extends EventEmitter {
|
|
|
2394
2394
|
// @ts-ignore - replaced at build time
|
|
2395
2395
|
const DEFAULT_BASE_URL$5 = "https://api.playkit.ai";
|
|
2396
2396
|
const JWT_EXCHANGE_ENDPOINT = '/api/external/exchange-jwt';
|
|
2397
|
+
const TOKEN_REFRESH_ENDPOINT = '/api/auth/refresh';
|
|
2397
2398
|
class AuthManager extends EventEmitter {
|
|
2398
2399
|
constructor(config) {
|
|
2399
2400
|
super();
|
|
@@ -2445,6 +2446,21 @@ class AuthManager extends EventEmitter {
|
|
|
2445
2446
|
this.emit('authenticated', this.authState);
|
|
2446
2447
|
return;
|
|
2447
2448
|
}
|
|
2449
|
+
// Token expired, but check if we can refresh (browser mode only)
|
|
2450
|
+
if (this.config.mode !== 'server' &&
|
|
2451
|
+
savedState.refreshToken &&
|
|
2452
|
+
(!savedState.refreshExpiresAt || Date.now() < savedState.refreshExpiresAt)) {
|
|
2453
|
+
this.logger.debug('Access token expired, attempting refresh');
|
|
2454
|
+
this.authState = savedState; // Load state with refresh token
|
|
2455
|
+
try {
|
|
2456
|
+
await this.refreshToken();
|
|
2457
|
+
return; // Successfully refreshed
|
|
2458
|
+
}
|
|
2459
|
+
catch (error) {
|
|
2460
|
+
this.logger.warn('Token refresh failed during initialization', error);
|
|
2461
|
+
// Continue to re-authentication
|
|
2462
|
+
}
|
|
2463
|
+
}
|
|
2448
2464
|
}
|
|
2449
2465
|
// Check if player JWT was provided
|
|
2450
2466
|
if (this.config.playerJWT) {
|
|
@@ -2494,12 +2510,14 @@ class AuthManager extends EventEmitter {
|
|
|
2494
2510
|
const result = await this.deviceAuthFlowManager.startFlow({
|
|
2495
2511
|
scope: 'player:play',
|
|
2496
2512
|
});
|
|
2497
|
-
// Update auth state with the player token
|
|
2513
|
+
// Update auth state with the player token and refresh token
|
|
2498
2514
|
this.authState = {
|
|
2499
2515
|
isAuthenticated: true,
|
|
2500
2516
|
token: result.access_token,
|
|
2501
2517
|
tokenType: 'player',
|
|
2502
2518
|
expiresAt: Date.now() + result.expires_in * 1000,
|
|
2519
|
+
refreshToken: result.refresh_token,
|
|
2520
|
+
refreshExpiresAt: Date.now() + result.refresh_expires_in * 1000,
|
|
2503
2521
|
};
|
|
2504
2522
|
// Save to storage
|
|
2505
2523
|
await this.storage.saveAuthState(this.config.gameId, this.authState);
|
|
@@ -2598,6 +2616,58 @@ class AuthManager extends EventEmitter {
|
|
|
2598
2616
|
return false;
|
|
2599
2617
|
return Date.now() >= this.authState.expiresAt;
|
|
2600
2618
|
}
|
|
2619
|
+
/**
|
|
2620
|
+
* Check if token is about to expire (within threshold)
|
|
2621
|
+
* @param thresholdMs - Threshold in milliseconds (default: 5 minutes)
|
|
2622
|
+
*/
|
|
2623
|
+
isTokenExpiringSoon(thresholdMs = 5 * 60 * 1000) {
|
|
2624
|
+
if (!this.authState.expiresAt)
|
|
2625
|
+
return false;
|
|
2626
|
+
return Date.now() >= this.authState.expiresAt - thresholdMs;
|
|
2627
|
+
}
|
|
2628
|
+
/**
|
|
2629
|
+
* Ensure the access token is valid, refreshing if needed (browser mode only).
|
|
2630
|
+
* Call this before making API requests to automatically handle token refresh.
|
|
2631
|
+
*
|
|
2632
|
+
* In server mode, this method does nothing (tokens should be managed externally).
|
|
2633
|
+
*
|
|
2634
|
+
* @param thresholdMs - Refresh if token expires within this time (default: 5 minutes)
|
|
2635
|
+
* @returns Promise that resolves when token is valid
|
|
2636
|
+
* @throws PlayKitError if token cannot be refreshed and is expired
|
|
2637
|
+
*/
|
|
2638
|
+
async ensureValidToken(thresholdMs = 5 * 60 * 1000) {
|
|
2639
|
+
// Skip auto-refresh in server mode
|
|
2640
|
+
if (this.config.mode === 'server') {
|
|
2641
|
+
return;
|
|
2642
|
+
}
|
|
2643
|
+
// Skip if not authenticated
|
|
2644
|
+
if (!this.authState.isAuthenticated || !this.authState.token) {
|
|
2645
|
+
return;
|
|
2646
|
+
}
|
|
2647
|
+
// Check if token needs refresh
|
|
2648
|
+
if (!this.isTokenExpiringSoon(thresholdMs)) {
|
|
2649
|
+
return; // Token is still valid
|
|
2650
|
+
}
|
|
2651
|
+
// Try to refresh if possible
|
|
2652
|
+
if (this.canRefresh()) {
|
|
2653
|
+
this.logger.debug('Token expiring soon, refreshing automatically');
|
|
2654
|
+
try {
|
|
2655
|
+
await this.refreshToken();
|
|
2656
|
+
}
|
|
2657
|
+
catch (error) {
|
|
2658
|
+
this.logger.warn('Auto-refresh failed', error);
|
|
2659
|
+
// If refresh fails and token is already expired, throw
|
|
2660
|
+
if (this.isTokenExpired()) {
|
|
2661
|
+
throw error;
|
|
2662
|
+
}
|
|
2663
|
+
// Otherwise, continue with potentially expired token
|
|
2664
|
+
}
|
|
2665
|
+
}
|
|
2666
|
+
else if (this.isTokenExpired()) {
|
|
2667
|
+
// Token expired and cannot refresh
|
|
2668
|
+
throw new PlayKitError('Access token has expired and no refresh token is available', 'TOKEN_EXPIRED');
|
|
2669
|
+
}
|
|
2670
|
+
}
|
|
2601
2671
|
/**
|
|
2602
2672
|
* Logout and clear authentication
|
|
2603
2673
|
*/
|
|
@@ -2639,12 +2709,14 @@ class AuthManager extends EventEmitter {
|
|
|
2639
2709
|
try {
|
|
2640
2710
|
this.deviceAuthFlowManager = new DeviceAuthFlowManager(this.baseURL, this.config.gameId);
|
|
2641
2711
|
const result = await this.deviceAuthFlowManager.startFlow(options);
|
|
2642
|
-
// Update auth state with the token
|
|
2712
|
+
// Update auth state with the token and refresh token
|
|
2643
2713
|
this.authState = {
|
|
2644
2714
|
isAuthenticated: true,
|
|
2645
2715
|
token: result.access_token,
|
|
2646
2716
|
tokenType: result.scope === 'developer:full' ? 'developer' : 'player',
|
|
2647
2717
|
expiresAt: Date.now() + result.expires_in * 1000,
|
|
2718
|
+
refreshToken: result.refresh_token,
|
|
2719
|
+
refreshExpiresAt: Date.now() + result.refresh_expires_in * 1000,
|
|
2648
2720
|
};
|
|
2649
2721
|
// Save to storage
|
|
2650
2722
|
await this.storage.saveAuthState(this.config.gameId, this.authState);
|
|
@@ -2707,12 +2779,14 @@ class AuthManager extends EventEmitter {
|
|
|
2707
2779
|
}
|
|
2708
2780
|
try {
|
|
2709
2781
|
const result = await this.deviceAuthFlowManager.pollForToken(sessionId, codeVerifier, options);
|
|
2710
|
-
// Update auth state with the token
|
|
2782
|
+
// Update auth state with the token and refresh token
|
|
2711
2783
|
this.authState = {
|
|
2712
2784
|
isAuthenticated: true,
|
|
2713
2785
|
token: result.access_token,
|
|
2714
2786
|
tokenType: result.scope === 'developer:full' ? 'developer' : 'player',
|
|
2715
2787
|
expiresAt: Date.now() + result.expires_in * 1000,
|
|
2788
|
+
refreshToken: result.refresh_token,
|
|
2789
|
+
refreshExpiresAt: Date.now() + result.refresh_expires_in * 1000,
|
|
2716
2790
|
};
|
|
2717
2791
|
// Save to storage
|
|
2718
2792
|
await this.storage.saveAuthState(this.config.gameId, this.authState);
|
|
@@ -2725,6 +2799,91 @@ class AuthManager extends EventEmitter {
|
|
|
2725
2799
|
this.deviceAuthFlowManager = null;
|
|
2726
2800
|
}
|
|
2727
2801
|
}
|
|
2802
|
+
/**
|
|
2803
|
+
* Check if the current session can be refreshed
|
|
2804
|
+
* @returns true if a valid refresh token exists and has not expired
|
|
2805
|
+
*/
|
|
2806
|
+
canRefresh() {
|
|
2807
|
+
if (!this.authState.refreshToken)
|
|
2808
|
+
return false;
|
|
2809
|
+
if (!this.authState.refreshExpiresAt)
|
|
2810
|
+
return true; // No expiry info, assume valid
|
|
2811
|
+
return Date.now() < this.authState.refreshExpiresAt;
|
|
2812
|
+
}
|
|
2813
|
+
/**
|
|
2814
|
+
* Refresh the access token using the stored refresh token
|
|
2815
|
+
*
|
|
2816
|
+
* @returns Promise resolving to TokenRefreshResult with new tokens
|
|
2817
|
+
* @throws PlayKitError if no refresh token is available or refresh fails
|
|
2818
|
+
*
|
|
2819
|
+
* @example
|
|
2820
|
+
* ```ts
|
|
2821
|
+
* if (sdk.isTokenExpired() && authManager.canRefresh()) {
|
|
2822
|
+
* const result = await authManager.refreshToken();
|
|
2823
|
+
* console.log('New token expires in:', result.expiresIn, 'seconds');
|
|
2824
|
+
* }
|
|
2825
|
+
* ```
|
|
2826
|
+
*/
|
|
2827
|
+
async refreshToken() {
|
|
2828
|
+
if (!this.authState.refreshToken) {
|
|
2829
|
+
throw new PlayKitError('No refresh token available. Please re-authenticate.', 'NO_REFRESH_TOKEN');
|
|
2830
|
+
}
|
|
2831
|
+
if (this.authState.refreshExpiresAt && Date.now() >= this.authState.refreshExpiresAt) {
|
|
2832
|
+
throw new PlayKitError('Refresh token has expired. Please re-authenticate.', 'REFRESH_TOKEN_EXPIRED');
|
|
2833
|
+
}
|
|
2834
|
+
try {
|
|
2835
|
+
this.logger.debug('Refreshing access token');
|
|
2836
|
+
const response = await fetch(`${this.baseURL}${TOKEN_REFRESH_ENDPOINT}`, {
|
|
2837
|
+
method: 'POST',
|
|
2838
|
+
headers: {
|
|
2839
|
+
'Content-Type': 'application/json',
|
|
2840
|
+
},
|
|
2841
|
+
body: JSON.stringify({
|
|
2842
|
+
refresh_token: this.authState.refreshToken,
|
|
2843
|
+
}),
|
|
2844
|
+
});
|
|
2845
|
+
if (!response.ok) {
|
|
2846
|
+
const error = await response.json().catch(() => ({ error_description: 'Token refresh failed' }));
|
|
2847
|
+
// If refresh token is invalid, clear auth state
|
|
2848
|
+
if (response.status === 401) {
|
|
2849
|
+
this.logger.warn('Refresh token invalid, clearing auth state');
|
|
2850
|
+
await this.logout();
|
|
2851
|
+
throw new PlayKitError(error.error_description || 'Refresh token is invalid or expired', 'REFRESH_TOKEN_INVALID', response.status);
|
|
2852
|
+
}
|
|
2853
|
+
throw new PlayKitError(error.error_description || 'Token refresh failed', error.error || 'REFRESH_FAILED', response.status);
|
|
2854
|
+
}
|
|
2855
|
+
const data = await response.json();
|
|
2856
|
+
// Update auth state with new tokens
|
|
2857
|
+
this.authState = {
|
|
2858
|
+
isAuthenticated: true,
|
|
2859
|
+
token: data.access_token,
|
|
2860
|
+
tokenType: data.scope === 'developer:full' ? 'developer' : 'player',
|
|
2861
|
+
expiresAt: Date.now() + data.expires_in * 1000,
|
|
2862
|
+
refreshToken: data.refresh_token,
|
|
2863
|
+
refreshExpiresAt: Date.now() + data.refresh_expires_in * 1000,
|
|
2864
|
+
};
|
|
2865
|
+
// Save to storage
|
|
2866
|
+
await this.storage.saveAuthState(this.config.gameId, this.authState);
|
|
2867
|
+
this.logger.info('Access token refreshed successfully');
|
|
2868
|
+
this.emit('authenticated', this.authState);
|
|
2869
|
+
this.emit('token_refreshed', this.authState);
|
|
2870
|
+
return {
|
|
2871
|
+
accessToken: data.access_token,
|
|
2872
|
+
tokenType: data.token_type,
|
|
2873
|
+
expiresIn: data.expires_in,
|
|
2874
|
+
refreshToken: data.refresh_token,
|
|
2875
|
+
refreshExpiresIn: data.refresh_expires_in,
|
|
2876
|
+
scope: data.scope,
|
|
2877
|
+
};
|
|
2878
|
+
}
|
|
2879
|
+
catch (error) {
|
|
2880
|
+
if (error instanceof PlayKitError) {
|
|
2881
|
+
throw error;
|
|
2882
|
+
}
|
|
2883
|
+
this.logger.error('Token refresh failed', error);
|
|
2884
|
+
throw new PlayKitError('Token refresh failed due to network error', 'REFRESH_NETWORK_ERROR');
|
|
2885
|
+
}
|
|
2886
|
+
}
|
|
2728
2887
|
}
|
|
2729
2888
|
|
|
2730
2889
|
/**
|
|
@@ -3578,6 +3737,8 @@ class ChatProvider {
|
|
|
3578
3737
|
*/
|
|
3579
3738
|
async chatCompletion(chatConfig) {
|
|
3580
3739
|
var _a;
|
|
3740
|
+
// Ensure token is valid, auto-refresh if needed (browser mode only)
|
|
3741
|
+
await this.authManager.ensureValidToken();
|
|
3581
3742
|
const token = this.authManager.getToken();
|
|
3582
3743
|
if (!token) {
|
|
3583
3744
|
throw new PlayKitError('Not authenticated', 'NOT_AUTHENTICATED');
|
|
@@ -3635,6 +3796,8 @@ class ChatProvider {
|
|
|
3635
3796
|
*/
|
|
3636
3797
|
async chatCompletionStream(chatConfig) {
|
|
3637
3798
|
var _a;
|
|
3799
|
+
// Ensure token is valid, auto-refresh if needed (browser mode only)
|
|
3800
|
+
await this.authManager.ensureValidToken();
|
|
3638
3801
|
const token = this.authManager.getToken();
|
|
3639
3802
|
if (!token) {
|
|
3640
3803
|
throw new PlayKitError('Not authenticated', 'NOT_AUTHENTICATED');
|
|
@@ -3823,6 +3986,8 @@ class ChatProvider {
|
|
|
3823
3986
|
*/
|
|
3824
3987
|
async generateStructured(schemaName, prompt, model, temperature, schema, schemaDescription) {
|
|
3825
3988
|
var _a;
|
|
3989
|
+
// Ensure token is valid, auto-refresh if needed (browser mode only)
|
|
3990
|
+
await this.authManager.ensureValidToken();
|
|
3826
3991
|
const token = this.authManager.getToken();
|
|
3827
3992
|
if (!token) {
|
|
3828
3993
|
throw new PlayKitError('Not authenticated', 'NOT_AUTHENTICATED');
|
|
@@ -3912,6 +4077,8 @@ class ImageProvider {
|
|
|
3912
4077
|
* Generate one or more images
|
|
3913
4078
|
*/
|
|
3914
4079
|
async generateImages(imageConfig) {
|
|
4080
|
+
// Ensure token is valid, auto-refresh if needed (browser mode only)
|
|
4081
|
+
await this.authManager.ensureValidToken();
|
|
3915
4082
|
const token = this.authManager.getToken();
|
|
3916
4083
|
if (!token) {
|
|
3917
4084
|
throw new PlayKitError('Not authenticated', 'NOT_AUTHENTICATED');
|
|
@@ -4023,6 +4190,8 @@ class TranscriptionProvider {
|
|
|
4023
4190
|
* Transcribe audio to text
|
|
4024
4191
|
*/
|
|
4025
4192
|
async transcribe(transcriptionConfig) {
|
|
4193
|
+
// Ensure token is valid, auto-refresh if needed (browser mode only)
|
|
4194
|
+
await this.authManager.ensureValidToken();
|
|
4026
4195
|
const token = this.authManager.getToken();
|
|
4027
4196
|
if (!token) {
|
|
4028
4197
|
throw new PlayKitError('Not authenticated', 'NOT_AUTHENTICATED');
|
|
@@ -5970,6 +6139,10 @@ class PlayKitSDK extends EventEmitter {
|
|
|
5970
6139
|
this.emit('error', error);
|
|
5971
6140
|
this.logger.error('Auth error', error);
|
|
5972
6141
|
});
|
|
6142
|
+
this.authManager.on('token_refreshed', (authState) => {
|
|
6143
|
+
this.emit('token_refreshed', authState);
|
|
6144
|
+
this.logger.debug('Token refreshed', authState);
|
|
6145
|
+
});
|
|
5973
6146
|
// Forward recharge events
|
|
5974
6147
|
this.playerClient.on('recharge_opened', () => this.emit('recharge_opened'));
|
|
5975
6148
|
this.playerClient.on('recharge_modal_shown', () => this.emit('recharge_modal_shown'));
|
|
@@ -5979,6 +6152,7 @@ class PlayKitSDK extends EventEmitter {
|
|
|
5979
6152
|
this.playerClient.on('balance_updated', (credits) => this.emit('balance_updated', credits));
|
|
5980
6153
|
this.playerClient.on('player_info_updated', (info) => this.emit('player_info_updated', info));
|
|
5981
6154
|
this.playerClient.on('daily_credits_refreshed', (result) => this.emit('daily_credits_refreshed', result));
|
|
6155
|
+
this.playerClient.on('nickname_changed', (nickname) => this.emit('nickname_changed', nickname));
|
|
5982
6156
|
}
|
|
5983
6157
|
/**
|
|
5984
6158
|
* Initialize the SDK
|
|
@@ -6270,6 +6444,25 @@ class PlayKitSDK extends EventEmitter {
|
|
|
6270
6444
|
return playerInfo.credits;
|
|
6271
6445
|
}
|
|
6272
6446
|
// ============================================================
|
|
6447
|
+
// Player Profile Methods
|
|
6448
|
+
// ============================================================
|
|
6449
|
+
/**
|
|
6450
|
+
* Get player's nickname (cached)
|
|
6451
|
+
* @returns Nickname or null if not set
|
|
6452
|
+
*/
|
|
6453
|
+
getNickname() {
|
|
6454
|
+
return this.playerClient.getNickname();
|
|
6455
|
+
}
|
|
6456
|
+
/**
|
|
6457
|
+
* Set player's nickname for the current game
|
|
6458
|
+
* @param nickname - 1-16 characters (letters, numbers, Chinese, underscores, spaces)
|
|
6459
|
+
* @returns SetNicknameResponse with success status and gameId
|
|
6460
|
+
* @throws PlayKitError if validation fails or token type is invalid
|
|
6461
|
+
*/
|
|
6462
|
+
async setNickname(nickname) {
|
|
6463
|
+
return await this.playerClient.setNickname(nickname);
|
|
6464
|
+
}
|
|
6465
|
+
// ============================================================
|
|
6273
6466
|
// Headless Device Auth Methods (for terminal/CLI environments)
|
|
6274
6467
|
// ============================================================
|
|
6275
6468
|
/**
|
|
@@ -6317,6 +6510,45 @@ class PlayKitSDK extends EventEmitter {
|
|
|
6317
6510
|
cancelLogin() {
|
|
6318
6511
|
this.authManager.cancelDeviceAuthFlow();
|
|
6319
6512
|
}
|
|
6513
|
+
// ============================================================
|
|
6514
|
+
// Token Refresh Methods
|
|
6515
|
+
// ============================================================
|
|
6516
|
+
/**
|
|
6517
|
+
* Check if the current token is expired
|
|
6518
|
+
*/
|
|
6519
|
+
isTokenExpired() {
|
|
6520
|
+
return this.authManager.isTokenExpired();
|
|
6521
|
+
}
|
|
6522
|
+
/**
|
|
6523
|
+
* Check if the access token can be refreshed
|
|
6524
|
+
* @returns true if a valid refresh token exists
|
|
6525
|
+
*/
|
|
6526
|
+
canRefreshToken() {
|
|
6527
|
+
return this.authManager.canRefresh();
|
|
6528
|
+
}
|
|
6529
|
+
/**
|
|
6530
|
+
* Manually refresh the access token using the stored refresh token.
|
|
6531
|
+
*
|
|
6532
|
+
* Note: In browser mode, token refresh is handled automatically before API calls.
|
|
6533
|
+
* This method is useful for:
|
|
6534
|
+
* - Proactively refreshing tokens before they expire
|
|
6535
|
+
* - Server-side applications managing their own token lifecycle
|
|
6536
|
+
*
|
|
6537
|
+
* @returns Promise resolving to TokenRefreshResult with new tokens
|
|
6538
|
+
* @throws PlayKitError if no refresh token is available or refresh fails
|
|
6539
|
+
*
|
|
6540
|
+
* @example
|
|
6541
|
+
* ```ts
|
|
6542
|
+
* // Manual refresh before a long operation
|
|
6543
|
+
* if (sdk.isTokenExpired() && sdk.canRefreshToken()) {
|
|
6544
|
+
* const result = await sdk.refreshToken();
|
|
6545
|
+
* console.log('Token refreshed, expires in:', result.expiresIn, 'seconds');
|
|
6546
|
+
* }
|
|
6547
|
+
* ```
|
|
6548
|
+
*/
|
|
6549
|
+
async refreshToken() {
|
|
6550
|
+
return this.authManager.refreshToken();
|
|
6551
|
+
}
|
|
6320
6552
|
}
|
|
6321
6553
|
|
|
6322
6554
|
/**
|