@djangocfg/centrifugo 2.1.72 → 2.1.73

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
@@ -34,10 +34,13 @@ src/
34
34
  │ │ ├── CentrifugoRPCClient.ts # Main facade (~165 lines)
35
35
  │ │ ├── connection.ts # Connection lifecycle
36
36
  │ │ ├── subscriptions.ts # Channel subscriptions
37
- │ │ ├── rpc.ts # RPC methods (namedRPC, namedRPCNoWait)
37
+ │ │ ├── rpc.ts # RPC methods (namedRPC, namedRPCWithRetry, namedRPCNoWait)
38
38
  │ │ ├── version.ts # API version checking
39
39
  │ │ ├── types.ts # Type definitions
40
40
  │ │ └── index.ts # Exports
41
+ │ ├── errors/ # Error handling with retry logic
42
+ │ │ ├── RPCError.ts # Typed RPC errors (isRetryable, userMessage)
43
+ │ │ └── RPCRetryHandler.ts # Exponential backoff retry
41
44
  │ ├── logger/ # Logging system with circular buffer
42
45
  │ │ ├── createLogger.ts # Logger factory (supports string prefix)
43
46
  │ │ └── LogsStore.ts # In-memory logs accumulation
@@ -598,6 +601,98 @@ client?.namedRPCNoWait('terminal.input', { session_id, data }, {
598
601
  | `namedRPC()` | ~800-1800ms | Commands that need response |
599
602
  | `namedRPCNoWait()` | ~10-30ms | Real-time input, fire-and-forget |
600
603
 
604
+ ### namedRPCWithRetry() - RPC with Timeout and Retry
605
+
606
+ For operations that need both timeout protection and automatic retry on transient failures.
607
+
608
+ ```tsx
609
+ import { useCentrifugo } from '@djangocfg/centrifugo';
610
+
611
+ function FileList() {
612
+ const { client } = useCentrifugo();
613
+
614
+ const loadFiles = async () => {
615
+ // Automatically retries on timeout/network errors
616
+ const files = await client?.namedRPCWithRetry('files.list',
617
+ { path: '/home' },
618
+ {
619
+ timeout: 5000, // 5 second timeout per attempt
620
+ maxRetries: 3, // Up to 3 retries
621
+ baseDelayMs: 1000, // Start with 1s delay
622
+ maxDelayMs: 10000, // Cap at 10s
623
+ onRetry: (attempt, error, delay) => {
624
+ console.log(`Retry ${attempt}: ${error.userMessage}, waiting ${delay}ms`);
625
+ }
626
+ }
627
+ );
628
+ return files;
629
+ };
630
+ }
631
+ ```
632
+
633
+ ### RPCError - Typed Error Handling
634
+
635
+ All RPC methods now throw `RPCError` with classification for better error handling:
636
+
637
+ ```tsx
638
+ import { RPCError } from '@djangocfg/centrifugo';
639
+
640
+ try {
641
+ await client.namedRPC('files.list', { path: '/' });
642
+ } catch (error) {
643
+ if (error instanceof RPCError) {
644
+ // Error classification
645
+ console.log(error.code); // 'timeout' | 'network_error' | 'server_error' | ...
646
+ console.log(error.isRetryable); // true for transient errors
647
+ console.log(error.userMessage); // User-friendly message
648
+ console.log(error.suggestedRetryDelay); // Recommended delay in ms
649
+
650
+ // Show user-friendly message
651
+ toast.error(error.userMessage);
652
+
653
+ // Decide if retry makes sense
654
+ if (error.isRetryable) {
655
+ // Schedule retry
656
+ }
657
+ }
658
+ }
659
+ ```
660
+
661
+ **Error Codes:**
662
+
663
+ | Code | Retryable | Description |
664
+ |------|-----------|-------------|
665
+ | `timeout` | ✅ | Request timed out |
666
+ | `network_error` | ✅ | Network connectivity issue |
667
+ | `connection_failed` | ✅ | WebSocket connection failed |
668
+ | `websocket_error` | ✅ | WebSocket protocol error |
669
+ | `server_error` | ✅ (5xx only) | Server returned error |
670
+ | `not_connected` | ❌ | Client not connected |
671
+ | `encoding_error` | ❌ | Failed to encode request |
672
+ | `decoding_error` | ❌ | Failed to decode response |
673
+ | `cancelled` | ❌ | Request was cancelled |
674
+
675
+ ### withRetry() - Generic Retry Utility
676
+
677
+ For custom retry logic outside of RPC:
678
+
679
+ ```tsx
680
+ import { withRetry, RPCError } from '@djangocfg/centrifugo';
681
+
682
+ const result = await withRetry(
683
+ () => fetchSomething(),
684
+ {
685
+ maxRetries: 3,
686
+ baseDelayMs: 1000,
687
+ maxDelayMs: 10000,
688
+ jitterFactor: 0.2, // ±20% randomization
689
+ },
690
+ (state, delay) => {
691
+ console.log(`Retry ${state.attempt}, waiting ${delay}ms`);
692
+ }
693
+ );
694
+ ```
695
+
601
696
  ### checkApiVersion() - API Contract Validation
602
697
 
603
698
  Validates that the client API version matches the server. Useful for detecting when the frontend needs to refresh after a backend deployment.
@@ -1087,6 +1182,12 @@ import type {
1087
1182
  RPCOptions,
1088
1183
  RetryOptions,
1089
1184
  VersionCheckResult,
1185
+ NamedRPCWithRetryOptions,
1186
+
1187
+ // Errors
1188
+ RPCErrorCode,
1189
+ RetryConfig,
1190
+ RetryState,
1090
1191
 
1091
1192
  // Connection
1092
1193
  ConnectionState,
@@ -1121,6 +1222,9 @@ import type {
1121
1222
  SubscriptionsListProps,
1122
1223
  CentrifugoMonitorProps,
1123
1224
  } from '@djangocfg/centrifugo';
1225
+
1226
+ // Error classes
1227
+ import { RPCError, withRetry, createRetryHandler } from '@djangocfg/centrifugo';
1124
1228
  ```
1125
1229
 
1126
1230
  ## Unified Event System
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/centrifugo",
3
- "version": "2.1.72",
3
+ "version": "2.1.73",
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.72",
55
- "@djangocfg/ui-nextjs": "^2.1.72",
56
- "@djangocfg/layouts": "^2.1.72",
54
+ "@djangocfg/api": "^2.1.73",
55
+ "@djangocfg/ui-nextjs": "^2.1.73",
56
+ "@djangocfg/layouts": "^2.1.73",
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.72",
64
+ "@djangocfg/typescript-config": "^2.1.73",
65
65
  "@types/react": "^19.1.0",
66
66
  "@types/react-dom": "^19.1.0",
67
67
  "moment": "^2.30.1",
@@ -47,7 +47,9 @@ import {
47
47
  rpc as legacyRpc,
48
48
  namedRPC as nativeNamedRPC,
49
49
  namedRPCNoWait as nativeNamedRPCNoWait,
50
+ namedRPCWithRetry as nativeNamedRPCWithRetry,
50
51
  type RPCManager,
52
+ type NamedRPCWithRetryOptions,
51
53
  } from './rpc';
52
54
 
53
55
  import { checkApiVersion as checkVersion } from './version';
@@ -158,6 +160,14 @@ export class CentrifugoRPCClient {
158
160
  nativeNamedRPCNoWait(this.rpcManager, method, data, options);
159
161
  }
160
162
 
163
+ async namedRPCWithRetry<TRequest = any, TResponse = any>(
164
+ method: string,
165
+ data: TRequest,
166
+ options?: NamedRPCWithRetryOptions
167
+ ): Promise<TResponse> {
168
+ return nativeNamedRPCWithRetry(this.rpcManager, method, data, options);
169
+ }
170
+
161
171
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
162
172
  // API Version Checking
163
173
  // ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━