@djangocfg/centrifugo 2.1.26 → 2.1.28

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/README.md CHANGED
@@ -441,7 +441,141 @@ interface UseNamedRPCResult {
441
441
  }
442
442
  ```
443
443
 
444
- > **Note:** `useNamedRPC` uses native Centrifugo RPC which requires RPC proxy to be configured in Centrifugo server. See the [Setup Guide](https://djangocfg.com/features/integrations/centrifugo/setup) for configuration details.
444
+ > **Note:** `useNamedRPC` uses native Centrifugo RPC which requires RPC proxy to be configured in Centrifugo server. See the [Setup Guide](https://djangocfg.com/docs/features/integrations/centrifugo/client-generation/) for configuration details.
445
+
446
+ ## Auto-Generated Type-Safe Clients
447
+
448
+ Django-CFG can auto-generate TypeScript clients from your `@websocket_rpc` handlers, providing full type safety and eliminating manual RPC calls.
449
+
450
+ ### Generate Clients
451
+
452
+ ```bash
453
+ # Generate TypeScript client from @websocket_rpc handlers
454
+ python manage.py generate_centrifugo_clients --typescript --output ./clients
455
+
456
+ # Generate and auto-copy to Next.js app (recommended)
457
+ python manage.py generate_centrifugo_clients --typescript
458
+ # Outputs to: openapi/centrifuge/typescript/
459
+ ```
460
+
461
+ This generates type-safe clients with:
462
+
463
+ ```
464
+ typescript/
465
+ ├── client.ts # Type-safe API methods
466
+ ├── types.ts # Pydantic models → TypeScript interfaces
467
+ ├── index.ts # Exports
468
+ └── rpc-client.ts # Low-level RPC client (optional)
469
+ ```
470
+
471
+ **Copy to your app:**
472
+
473
+ ```bash
474
+ # Manual copy
475
+ cp -r openapi/centrifuge/typescript/* apps/web/app/_ws/
476
+
477
+ # Or create a custom command for your project
478
+ # See: core/management/commands/generate_centrifuge_web.py
479
+ ```
480
+
481
+ ### Using Generated Clients
482
+
483
+ **Before (manual RPC):**
484
+ ```tsx
485
+ // ❌ No type safety, easy to make mistakes
486
+ const result = await client.namedRPC('ai_chat.get_messages', {
487
+ session_id: sessionId,
488
+ limit: 50,
489
+ before_id: beforeId, // Could typo: beforeID
490
+ });
491
+ ```
492
+
493
+ **After (generated client):**
494
+ ```tsx
495
+ import { APIClient, type MessageListResult } from '@/_ws';
496
+
497
+ const apiClient = useMemo(() =>
498
+ client ? new APIClient(client) : null,
499
+ [client]
500
+ );
501
+
502
+ // ✅ Full type safety with autocomplete
503
+ const result: MessageListResult = await apiClient.aiChatGetMessages({
504
+ session_id: sessionId, // ✓ Autocomplete
505
+ limit: 50, // ✓ Type checking
506
+ before_id: beforeId, // ✓ Typo protection
507
+ });
508
+ ```
509
+
510
+ ### How It Works
511
+
512
+ 1. **Backend** - Define RPC handlers with Pydantic models:
513
+
514
+ ```python
515
+ from pydantic import BaseModel
516
+ from django_cfg.apps.integrations.centrifugo.decorators import websocket_rpc
517
+
518
+ class GetMessagesParams(BaseModel):
519
+ session_id: str
520
+ limit: int = 50
521
+ before_id: str | None = None
522
+
523
+ class MessageListResult(BaseModel):
524
+ messages: list[MessageData]
525
+ total: int
526
+ has_more: bool
527
+
528
+ @websocket_rpc("ai_chat.get_messages")
529
+ async def ai_chat_get_messages(conn, params: GetMessagesParams) -> MessageListResult:
530
+ # Implementation
531
+ return MessageListResult(...)
532
+ ```
533
+
534
+ 2. **Generate** - Run generation command:
535
+
536
+ ```bash
537
+ python manage.py generate_centrifuge_web
538
+ # Discovers all @websocket_rpc handlers
539
+ # Converts Pydantic models to TypeScript
540
+ # Generates type-safe APIClient
541
+ ```
542
+
543
+ 3. **Frontend** - Use generated client:
544
+
545
+ ```tsx
546
+ const apiClient = new APIClient(client);
547
+ const result = await apiClient.aiChatGetMessages({
548
+ session_id: "uuid",
549
+ limit: 50,
550
+ });
551
+ // TypeScript knows result is MessageListResult!
552
+ ```
553
+
554
+ ### Benefits
555
+
556
+ - ✅ **Type Safety** - Compile-time errors for invalid parameters
557
+ - ✅ **Autocomplete** - IDE suggests available methods and parameters
558
+ - ✅ **Sync with Backend** - Regenerate when backend changes
559
+ - ✅ **No Manual Updates** - Types automatically match Pydantic models
560
+ - ✅ **Documentation** - Docstrings from backend appear in IDE
561
+
562
+ ### Available Methods
563
+
564
+ All `@websocket_rpc` handlers are auto-generated:
565
+
566
+ ```tsx
567
+ // Terminal RPC
568
+ await apiClient.terminalInput({ session_id, data });
569
+ await apiClient.terminalResize({ session_id, cols, rows });
570
+ await apiClient.terminalClose({ session_id });
571
+
572
+ // AI Chat RPC
573
+ await apiClient.aiChatCreateSession({ workspace_id, title });
574
+ await apiClient.aiChatSendMessage({ session_id, message, stream });
575
+ await apiClient.aiChatGetMessages({ session_id, limit, before_id });
576
+ ```
577
+
578
+ > **Tip:** Run `python manage.py generate_centrifugo_clients --typescript` after adding new RPC handlers to update types.
445
579
 
446
580
  ## UI Components
447
581
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/centrifugo",
3
- "version": "2.1.26",
3
+ "version": "2.1.28",
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.28",
55
+ "@djangocfg/ui-nextjs": "^2.1.28",
56
+ "@djangocfg/layouts": "^2.1.28",
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.28",
65
65
  "@types/react": "^19.1.0",
66
66
  "@types/react-dom": "^19.1.0",
67
67
  "moment": "^2.30.1",
@@ -12,9 +12,9 @@ import { Badge } from '@djangocfg/ui-nextjs';
12
12
  import { Wifi, WifiOff, Radio, Clock } from 'lucide-react';
13
13
  import moment from 'moment';
14
14
  import { useCentrifugo } from '../../providers/CentrifugoProvider';
15
- import { createLogger } from '../../core/logger/createLogger';
15
+ import { getConsolaLogger } from '../../core/logger/consolaLogger';
16
16
 
17
- const logger = createLogger('ConnectionStatus');
17
+ const logger = getConsolaLogger('ConnectionStatus');
18
18
 
19
19
  // ─────────────────────────────────────────────────────────────────────────
20
20
  // Types
@@ -19,9 +19,9 @@ import {
19
19
  import { Radio, RefreshCw, Trash2 } from 'lucide-react';
20
20
  import { Subscription, SubscriptionState } from 'centrifuge';
21
21
  import { useCentrifugo } from '../../providers/CentrifugoProvider';
22
- import { createLogger } from '../../core/logger/createLogger';
22
+ import { getConsolaLogger } from '../../core/logger/consolaLogger';
23
23
 
24
- const logger = createLogger('SubscriptionsList');
24
+ const logger = getConsolaLogger('SubscriptionsList');
25
25
 
26
26
  // ─────────────────────────────────────────────────────────────────────────
27
27
  // Types
@@ -7,7 +7,20 @@
7
7
 
8
8
  import { Centrifuge } from 'centrifuge';
9
9
  import type { Logger } from '../logger';
10
- import { createLogger } from '../logger';
10
+ import { getConsolaLogger } from '../logger/consolaLogger';
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
+ }
11
24
 
12
25
  export class CentrifugoRPCClient {
13
26
  private centrifuge: Centrifuge;
@@ -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 || getConsolaLogger('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';
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Shared Consola Logger
3
+ *
4
+ * Single consola instance used across the package for development logging.
5
+ */
6
+
7
+ import { createConsola } from 'consola';
8
+ import type { Logger } from './createLogger';
9
+
10
+ const isDevelopment = process.env.NODE_ENV === 'development';
11
+
12
+ /**
13
+ * Shared consola logger for Centrifugo package
14
+ */
15
+ export const consolaLogger = createConsola({
16
+ level: isDevelopment ? 4 : 3,
17
+ formatOptions: {
18
+ colors: true,
19
+ date: false,
20
+ compact: !isDevelopment,
21
+ },
22
+ }).withTag('[Centrifugo]');
23
+
24
+ /**
25
+ * Get a consola logger with custom tag wrapped to match Logger interface
26
+ */
27
+ export function getConsolaLogger(tag: string): Logger {
28
+ const consola = consolaLogger.withTag(`[${tag}]`);
29
+
30
+ return {
31
+ debug: (message: string, data?: unknown) => consola.debug(message, data || ''),
32
+ info: (message: string, data?: unknown) => consola.info(message, data || ''),
33
+ success: (message: string, data?: unknown) => consola.success(message, data || ''),
34
+ warning: (message: string, data?: unknown) => consola.warn(message, data || ''),
35
+ error: (message: string, error?: Error | unknown) => consola.error(message, error || ''),
36
+ };
37
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Development Tips
3
+ *
4
+ * Shows helpful development tips once per session.
5
+ * Used across the package to educate developers about features.
6
+ */
7
+
8
+ import { getConsolaLogger } from '../logger/consolaLogger';
9
+
10
+ const logger = getConsolaLogger('DevTips');
11
+ const shownTips = new Set<string>();
12
+
13
+ /**
14
+ * Show a development tip once per session
15
+ *
16
+ * @param id - Unique identifier for the tip
17
+ * @param message - Tip message
18
+ * @param details - Optional details (shown as second parameter)
19
+ *
20
+ * @example
21
+ * showDevTip('codegen', 'Generate type-safe clients:', 'python manage.py ...');
22
+ */
23
+ export function showDevTip(id: string, message: string, details?: string): void {
24
+ if (process.env.NODE_ENV !== 'development') return;
25
+ if (shownTips.has(id)) return;
26
+
27
+ shownTips.add(id);
28
+ logger.info(message, details);
29
+ }
30
+
31
+ /**
32
+ * Predefined tips
33
+ */
34
+ export const DevTips = {
35
+ /**
36
+ * Show tip about generating type-safe clients
37
+ */
38
+ codegenClients: () => {
39
+ showDevTip(
40
+ 'codegen-clients',
41
+ '💡 Generate type-safe clients from @websocket_rpc handlers:',
42
+ '\n\npython manage.py generate_centrifugo_clients --typescript\n\nLearn more: https://djangocfg.com/centrifugo/codegen'
43
+ );
44
+ },
45
+
46
+ /**
47
+ * Show tip about using generated client instead of manual RPC
48
+ */
49
+ useGeneratedClient: () => {
50
+ showDevTip(
51
+ 'use-generated-client',
52
+ '💡 Consider using auto-generated APIClient for type safety:',
53
+ '\n\nimport { APIClient } from \'@/_ws\';\nconst apiClient = new APIClient(client);\nawait apiClient.aiChatGetMessages({ ... });\n\nGenerate: python manage.py generate_centrifugo_clients --typescript'
54
+ );
55
+ },
56
+
57
+ /**
58
+ * Show tip about RPC proxy configuration
59
+ */
60
+ rpcProxyConfig: () => {
61
+ showDevTip(
62
+ 'rpc-proxy-config',
63
+ '💡 Using namedRPC requires Centrifugo RPC proxy configuration:',
64
+ '\n\nSee: https://djangocfg.com/features/integrations/centrifugo/setup'
65
+ );
66
+ },
67
+ };
68
+
69
+ /**
70
+ * Reset shown tips (useful for testing)
71
+ */
72
+ export function resetDevTips(): void {
73
+ shownTips.clear();
74
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * useCodegenTip Hook
3
+ *
4
+ * Shows development tip about client generation once per session.
5
+ */
6
+
7
+ import { useEffect, useRef } from 'react';
8
+ import { consolaLogger } from '../core/logger/consolaLogger';
9
+
10
+ const isDevelopment = process.env.NODE_ENV === 'development';
11
+ let tipShown = false; // Global flag to show tip once across all components
12
+
13
+ /**
14
+ * Show dev tip about generating type-safe clients from @websocket_rpc handlers
15
+ *
16
+ * @example
17
+ * function MyComponent() {
18
+ * useCodegenTip();
19
+ * // ... rest of component
20
+ * }
21
+ */
22
+ export function useCodegenTip() {
23
+ const mountedRef = useRef(false);
24
+
25
+ useEffect(() => {
26
+ if (!isDevelopment) return;
27
+ if (tipShown) return;
28
+ if (mountedRef.current) return;
29
+
30
+ mountedRef.current = true;
31
+ tipShown = true;
32
+
33
+ consolaLogger.info(
34
+ '💡 Generate type-safe clients from @websocket_rpc handlers:\n\n' +
35
+ 'python manage.py generate_centrifugo_clients --typescript\n\n' +
36
+ 'Learn more: https://djangocfg.com/docs/features/integrations/centrifugo/client-generation/'
37
+ );
38
+ }, []);
39
+ }
@@ -27,7 +27,7 @@
27
27
 
28
28
  import { useState, useCallback, useRef } from 'react';
29
29
  import { useCentrifugo } from '../providers/CentrifugoProvider';
30
- import { createLogger } from '../core/logger';
30
+ import { getConsolaLogger } from '../core/logger/consolaLogger';
31
31
 
32
32
  export interface UseNamedRPCOptions {
33
33
  onError?: (error: Error) => void;
@@ -50,7 +50,7 @@ export function useNamedRPC(
50
50
  const [isLoading, setIsLoading] = useState(false);
51
51
  const [error, setError] = useState<Error | null>(null);
52
52
 
53
- const logger = useRef(createLogger('useNamedRPC')).current;
53
+ const logger = useRef(getConsolaLogger('useNamedRPC')).current;
54
54
 
55
55
  const reset = useCallback(() => {
56
56
  setIsLoading(false);
@@ -22,7 +22,7 @@
22
22
 
23
23
  import { useState, useCallback, useRef } from 'react';
24
24
  import { useCentrifugo } from '../providers/CentrifugoProvider';
25
- import { createLogger } from '../core/logger';
25
+ import { getConsolaLogger } from '../core/logger/consolaLogger';
26
26
 
27
27
  export interface UseRPCOptions {
28
28
  timeout?: number;
@@ -45,8 +45,8 @@ export function useRPC(defaultOptions: UseRPCOptions = {}): UseRPCResult {
45
45
  const { client, isConnected } = useCentrifugo();
46
46
  const [isLoading, setIsLoading] = useState(false);
47
47
  const [error, setError] = useState<Error | null>(null);
48
-
49
- const logger = useRef(createLogger('useRPC')).current;
48
+
49
+ const logger = useRef(getConsolaLogger('useRPC')).current;
50
50
  const abortControllerRef = useRef<AbortController | null>(null);
51
51
 
52
52
  const reset = useCallback(() => {
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { useEffect, useCallback, useRef, useState } from 'react';
16
16
  import { useCentrifugo } from '../providers/CentrifugoProvider';
17
- import { createLogger } from '../core/logger';
17
+ import { getConsolaLogger } from '../core/logger/consolaLogger';
18
18
  import { logChannelWarnings } from '../core/utils/channelValidator';
19
19
 
20
20
  export interface UseSubscriptionOptions<T = any> {
@@ -42,7 +42,7 @@ export function useSubscription<T = any>(
42
42
  const [isSubscribed, setIsSubscribed] = useState(false);
43
43
 
44
44
  const unsubscribeRef = useRef<(() => void) | null>(null);
45
- const logger = useRef(createLogger('useSubscription')).current;
45
+ const logger = useRef(getConsolaLogger('useSubscription')).current;
46
46
 
47
47
  // Store callbacks in refs to avoid re-subscriptions when they change
48
48
  const onPublicationRef = useRef(onPublication);
package/src/index.ts CHANGED
@@ -53,6 +53,7 @@ export type { Subscription } from 'centrifuge';
53
53
 
54
54
  export { CentrifugoRPCClient } from './core/client/CentrifugoRPCClient';
55
55
  export { createLogger, getGlobalLogsStore } from './core/logger';
56
+ export { consolaLogger, getConsolaLogger } from './core/logger/consolaLogger';
56
57
  export type {
57
58
  // Connection
58
59
  ConnectionState,
@@ -92,6 +93,8 @@ export type {
92
93
  UseSubscriptionResult,
93
94
  } from './hooks/useSubscription';
94
95
 
96
+ export { useCodegenTip } from './hooks/useCodegenTip';
97
+
95
98
  // ─────────────────────────────────────────────────────────────────────────
96
99
  // Configuration
97
100
  // ─────────────────────────────────────────────────────────────────────────
@@ -10,11 +10,12 @@
10
10
  import { createContext, useContext, useState, useEffect, useCallback, useRef, useMemo, type ReactNode } from 'react';
11
11
  import { useAuth } from '@djangocfg/api/auth';
12
12
  import { CentrifugoRPCClient } from '../../core/client';
13
- import { createLogger } from '../../core/logger';
13
+ import { getConsolaLogger } from '../../core/logger/consolaLogger';
14
14
  import type { ConnectionState, CentrifugoToken, ActiveSubscription } from '../../core/types';
15
15
  import { LogsProvider } from '../LogsProvider';
16
16
  import { isStaticBuild, isDevelopment, reconnectConfig } from '../../config';
17
17
  import { CentrifugoMonitorDialog } from '../../components/CentrifugoMonitor/CentrifugoMonitorDialog';
18
+ import { useCodegenTip } from '../../hooks/useCodegenTip';
18
19
 
19
20
  // ─────────────────────────────────────────────────────────────────────────
20
21
  // Context
@@ -56,6 +57,18 @@ export interface CentrifugoProviderProps {
56
57
  enabled?: boolean;
57
58
  url?: string;
58
59
  autoConnect?: boolean;
60
+ /**
61
+ * Callback to refresh the Centrifugo token when it expires.
62
+ * If provided, centrifuge-js will automatically call this when token expires.
63
+ * Should return a fresh JWT token string.
64
+ *
65
+ * @example
66
+ * onTokenRefresh={async () => {
67
+ * const response = await getCentrifugoAuthTokenRetrieve();
68
+ * return response.token;
69
+ * }}
70
+ */
71
+ onTokenRefresh?: () => Promise<string>;
59
72
  }
60
73
 
61
74
  // ─────────────────────────────────────────────────────────────────────────
@@ -67,6 +80,7 @@ function CentrifugoProviderInner({
67
80
  enabled = false,
68
81
  url,
69
82
  autoConnect: autoConnectProp = true,
83
+ onTokenRefresh,
70
84
  }: CentrifugoProviderProps) {
71
85
  const { isAuthenticated, isLoading, user } = useAuth();
72
86
 
@@ -79,15 +93,18 @@ function CentrifugoProviderInner({
79
93
  const [subscriptions, setSubscriptions] = useState<string[]>([]);
80
94
  const [activeSubscriptions, setActiveSubscriptions] = useState<ActiveSubscription[]>([]);
81
95
 
82
- const logger = useMemo(() => createLogger({ source: 'provider' }), []);
96
+ const logger = useMemo(() => getConsolaLogger('provider'), []);
97
+
98
+ // Show dev tip about client generation
99
+ useCodegenTip();
83
100
 
84
101
  const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
85
102
  const hasConnectedRef = useRef(false);
86
103
  const isConnectingRef = useRef(false);
87
104
  const isMountedRef = useRef(true);
88
105
  const reconnectAttemptRef = useRef(0);
89
- const devWarningShownRef = useRef(false);
90
106
  const reconnectStoppedRef = useRef(false); // Track if we should stop reconnecting
107
+ const devWarningShownRef = useRef(false); // Track if server unavailable warning was shown
91
108
  const connectRef = useRef<(() => Promise<void>) | null>(null);
92
109
  const disconnectRef = useRef<(() => void) | null>(null);
93
110
 
@@ -215,7 +232,14 @@ function CentrifugoProviderInner({
215
232
  }
216
233
  }
217
234
 
218
- const rpcClient = new CentrifugoRPCClient(wsUrl, token, userId, 30000, logger);
235
+ const rpcClient = new CentrifugoRPCClient({
236
+ url: wsUrl,
237
+ token,
238
+ userId,
239
+ timeout: 30000,
240
+ logger,
241
+ getToken: onTokenRefresh,
242
+ });
219
243
  await rpcClient.connect();
220
244
 
221
245
  if (!isMountedRef.current) {
@@ -304,7 +328,7 @@ function CentrifugoProviderInner({
304
328
  } finally {
305
329
  setIsConnecting(false);
306
330
  }
307
- }, [wsUrl, centrifugoToken, user, logger, isConnecting, isConnected, getReconnectDelay]);
331
+ }, [wsUrl, centrifugoToken, user, logger, isConnecting, isConnected, getReconnectDelay, onTokenRefresh]);
308
332
 
309
333
  // Disconnect function
310
334
  const disconnect = useCallback(() => {