@djangocfg/centrifugo 2.1.26 → 2.1.27
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/centrifugo",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.27",
|
|
4
4
|
"description": "Production-ready Centrifugo WebSocket client for React with real-time subscriptions, RPC patterns, and connection state management",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"centrifugo",
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"centrifuge": "^5.2.2"
|
|
52
52
|
},
|
|
53
53
|
"peerDependencies": {
|
|
54
|
-
"@djangocfg/api": "^2.1.
|
|
55
|
-
"@djangocfg/ui-nextjs": "^2.1.
|
|
56
|
-
"@djangocfg/layouts": "^2.1.
|
|
54
|
+
"@djangocfg/api": "^2.1.27",
|
|
55
|
+
"@djangocfg/ui-nextjs": "^2.1.27",
|
|
56
|
+
"@djangocfg/layouts": "^2.1.27",
|
|
57
57
|
"consola": "^3.4.2",
|
|
58
58
|
"lucide-react": "^0.545.0",
|
|
59
59
|
"moment": "^2.30.1",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"react-dom": "^19.1.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
64
|
+
"@djangocfg/typescript-config": "^2.1.27",
|
|
65
65
|
"@types/react": "^19.1.0",
|
|
66
66
|
"@types/react-dom": "^19.1.0",
|
|
67
67
|
"moment": "^2.30.1",
|
|
@@ -9,6 +9,19 @@ import { Centrifuge } from 'centrifuge';
|
|
|
9
9
|
import type { Logger } from '../logger';
|
|
10
10
|
import { createLogger } from '../logger';
|
|
11
11
|
|
|
12
|
+
export interface CentrifugoClientOptions {
|
|
13
|
+
url: string;
|
|
14
|
+
token: string;
|
|
15
|
+
userId: string;
|
|
16
|
+
timeout?: number;
|
|
17
|
+
logger?: Logger;
|
|
18
|
+
/**
|
|
19
|
+
* Callback to get fresh token when current token expires.
|
|
20
|
+
* Centrifuge-js calls this automatically on token expiration.
|
|
21
|
+
*/
|
|
22
|
+
getToken?: () => Promise<string>;
|
|
23
|
+
}
|
|
24
|
+
|
|
12
25
|
export class CentrifugoRPCClient {
|
|
13
26
|
private centrifuge: Centrifuge;
|
|
14
27
|
private subscription: any;
|
|
@@ -19,21 +32,73 @@ export class CentrifugoRPCClient {
|
|
|
19
32
|
private readonly timeout: number;
|
|
20
33
|
private readonly logger: Logger;
|
|
21
34
|
|
|
35
|
+
constructor(options: CentrifugoClientOptions);
|
|
36
|
+
/** @deprecated Use options object instead */
|
|
22
37
|
constructor(
|
|
23
38
|
url: string,
|
|
24
39
|
token: string,
|
|
25
40
|
userId: string,
|
|
41
|
+
timeout?: number,
|
|
42
|
+
logger?: Logger
|
|
43
|
+
);
|
|
44
|
+
constructor(
|
|
45
|
+
urlOrOptions: string | CentrifugoClientOptions,
|
|
46
|
+
token?: string,
|
|
47
|
+
userId?: string,
|
|
26
48
|
timeout: number = 30000,
|
|
27
49
|
logger?: Logger
|
|
28
50
|
) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
51
|
+
// Handle both old and new API
|
|
52
|
+
let url: string;
|
|
53
|
+
let actualToken: string;
|
|
54
|
+
let actualUserId: string;
|
|
55
|
+
let actualTimeout: number;
|
|
56
|
+
let actualLogger: Logger | undefined;
|
|
57
|
+
let getToken: (() => Promise<string>) | undefined;
|
|
58
|
+
|
|
59
|
+
if (typeof urlOrOptions === 'object') {
|
|
60
|
+
// New options-based API
|
|
61
|
+
url = urlOrOptions.url;
|
|
62
|
+
actualToken = urlOrOptions.token;
|
|
63
|
+
actualUserId = urlOrOptions.userId;
|
|
64
|
+
actualTimeout = urlOrOptions.timeout ?? 30000;
|
|
65
|
+
actualLogger = urlOrOptions.logger;
|
|
66
|
+
getToken = urlOrOptions.getToken;
|
|
67
|
+
} else {
|
|
68
|
+
// Legacy positional arguments API
|
|
69
|
+
url = urlOrOptions;
|
|
70
|
+
actualToken = token!;
|
|
71
|
+
actualUserId = userId!;
|
|
72
|
+
actualTimeout = timeout;
|
|
73
|
+
actualLogger = logger;
|
|
74
|
+
}
|
|
33
75
|
|
|
34
|
-
this.
|
|
35
|
-
|
|
36
|
-
|
|
76
|
+
this.userId = actualUserId;
|
|
77
|
+
this.replyChannel = `user#${actualUserId}`;
|
|
78
|
+
this.timeout = actualTimeout;
|
|
79
|
+
this.logger = actualLogger || createLogger({ source: 'client' });
|
|
80
|
+
|
|
81
|
+
// Build Centrifuge options
|
|
82
|
+
const centrifugeOptions: any = {
|
|
83
|
+
token: actualToken,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// Add getToken callback for automatic token refresh
|
|
87
|
+
if (getToken) {
|
|
88
|
+
centrifugeOptions.getToken = async () => {
|
|
89
|
+
this.logger.info('Token expired, refreshing...');
|
|
90
|
+
try {
|
|
91
|
+
const newToken = await getToken();
|
|
92
|
+
this.logger.success('Token refreshed successfully');
|
|
93
|
+
return newToken;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
this.logger.error('Failed to refresh token', error);
|
|
96
|
+
throw error;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this.centrifuge = new Centrifuge(url, centrifugeOptions);
|
|
37
102
|
|
|
38
103
|
this.centrifuge.on('disconnected', (ctx) => {
|
|
39
104
|
// Reject all pending requests
|
package/src/core/client/index.ts
CHANGED
|
@@ -56,6 +56,18 @@ export interface CentrifugoProviderProps {
|
|
|
56
56
|
enabled?: boolean;
|
|
57
57
|
url?: string;
|
|
58
58
|
autoConnect?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Callback to refresh the Centrifugo token when it expires.
|
|
61
|
+
* If provided, centrifuge-js will automatically call this when token expires.
|
|
62
|
+
* Should return a fresh JWT token string.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* onTokenRefresh={async () => {
|
|
66
|
+
* const response = await getCentrifugoAuthTokenRetrieve();
|
|
67
|
+
* return response.token;
|
|
68
|
+
* }}
|
|
69
|
+
*/
|
|
70
|
+
onTokenRefresh?: () => Promise<string>;
|
|
59
71
|
}
|
|
60
72
|
|
|
61
73
|
// ─────────────────────────────────────────────────────────────────────────
|
|
@@ -67,6 +79,7 @@ function CentrifugoProviderInner({
|
|
|
67
79
|
enabled = false,
|
|
68
80
|
url,
|
|
69
81
|
autoConnect: autoConnectProp = true,
|
|
82
|
+
onTokenRefresh,
|
|
70
83
|
}: CentrifugoProviderProps) {
|
|
71
84
|
const { isAuthenticated, isLoading, user } = useAuth();
|
|
72
85
|
|
|
@@ -215,7 +228,14 @@ function CentrifugoProviderInner({
|
|
|
215
228
|
}
|
|
216
229
|
}
|
|
217
230
|
|
|
218
|
-
const rpcClient = new CentrifugoRPCClient(
|
|
231
|
+
const rpcClient = new CentrifugoRPCClient({
|
|
232
|
+
url: wsUrl,
|
|
233
|
+
token,
|
|
234
|
+
userId,
|
|
235
|
+
timeout: 30000,
|
|
236
|
+
logger,
|
|
237
|
+
getToken: onTokenRefresh,
|
|
238
|
+
});
|
|
219
239
|
await rpcClient.connect();
|
|
220
240
|
|
|
221
241
|
if (!isMountedRef.current) {
|
|
@@ -304,7 +324,7 @@ function CentrifugoProviderInner({
|
|
|
304
324
|
} finally {
|
|
305
325
|
setIsConnecting(false);
|
|
306
326
|
}
|
|
307
|
-
}, [wsUrl, centrifugoToken, user, logger, isConnecting, isConnected, getReconnectDelay]);
|
|
327
|
+
}, [wsUrl, centrifugoToken, user, logger, isConnecting, isConnected, getReconnectDelay, onTokenRefresh]);
|
|
308
328
|
|
|
309
329
|
// Disconnect function
|
|
310
330
|
const disconnect = useCallback(() => {
|