@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.26",
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.26",
55
- "@djangocfg/ui-nextjs": "^2.1.26",
56
- "@djangocfg/layouts": "^2.1.26",
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.26",
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
- this.userId = userId;
30
- this.replyChannel = `user#${userId}`;
31
- this.timeout = timeout;
32
- this.logger = logger || createLogger({ source: 'client' });
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.centrifuge = new Centrifuge(url, {
35
- token,
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
@@ -3,3 +3,4 @@
3
3
  */
4
4
 
5
5
  export { CentrifugoRPCClient } from './CentrifugoRPCClient';
6
+ export type { CentrifugoClientOptions } from './CentrifugoRPCClient';
@@ -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(wsUrl, token, userId, 30000, logger);
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(() => {