@djangocfg/centrifugo 2.1.54 → 2.1.55
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 +168 -7
- package/package.json +5 -5
- package/src/core/client/CentrifugoRPCClient.ts +145 -561
- package/src/core/client/connection.ts +229 -0
- package/src/core/client/index.ts +47 -2
- package/src/core/client/rpc.ts +290 -0
- package/src/core/client/subscriptions.ts +176 -0
- package/src/core/client/types.ts +44 -0
- package/src/core/client/version.ts +92 -0
- package/src/events.ts +160 -47
package/README.md
CHANGED
|
@@ -30,10 +30,18 @@ Professional Centrifugo WebSocket client with React integration, composable UI c
|
|
|
30
30
|
src/
|
|
31
31
|
├── core/ # Platform-agnostic (no React dependencies)
|
|
32
32
|
│ ├── client/ # CentrifugoRPCClient - WebSocket client
|
|
33
|
+
│ │ ├── CentrifugoRPCClient.ts # Main facade (~165 lines)
|
|
34
|
+
│ │ ├── connection.ts # Connection lifecycle
|
|
35
|
+
│ │ ├── subscriptions.ts # Channel subscriptions
|
|
36
|
+
│ │ ├── rpc.ts # RPC methods (namedRPC, namedRPCNoWait)
|
|
37
|
+
│ │ ├── version.ts # API version checking
|
|
38
|
+
│ │ ├── types.ts # Type definitions
|
|
39
|
+
│ │ └── index.ts # Exports
|
|
33
40
|
│ ├── logger/ # Logging system with circular buffer
|
|
34
41
|
│ │ ├── createLogger.ts # Logger factory (supports string prefix)
|
|
35
42
|
│ │ └── LogsStore.ts # In-memory logs accumulation
|
|
36
43
|
│ └── types/ # TypeScript type definitions
|
|
44
|
+
├── events.ts # Unified event system (single 'centrifugo' event)
|
|
37
45
|
├── providers/ # React Context providers
|
|
38
46
|
│ ├── CentrifugoProvider/ # Main connection provider (no auto-FAB)
|
|
39
47
|
│ └── LogsProvider/ # Logs accumulation provider
|
|
@@ -486,6 +494,24 @@ async def terminal_input(conn, params: TerminalInputParams) -> SuccessResult:
|
|
|
486
494
|
return SuccessResult(success=True, message="Input queued")
|
|
487
495
|
```
|
|
488
496
|
|
|
497
|
+
**Retry Logic with Exponential Backoff:**
|
|
498
|
+
|
|
499
|
+
`namedRPCNoWait` includes automatic retry with exponential backoff:
|
|
500
|
+
|
|
501
|
+
```tsx
|
|
502
|
+
// Default: 3 retries, 100ms base delay, 2000ms max delay
|
|
503
|
+
client?.namedRPCNoWait('terminal.input', { session_id, data });
|
|
504
|
+
|
|
505
|
+
// Custom retry options
|
|
506
|
+
client?.namedRPCNoWait('terminal.input', { session_id, data }, {
|
|
507
|
+
maxRetries: 5, // Max retry attempts (default: 3)
|
|
508
|
+
baseDelayMs: 100, // Base delay for exponential backoff (default: 100)
|
|
509
|
+
maxDelayMs: 3000, // Max delay cap (default: 2000)
|
|
510
|
+
});
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
**Retry sequence:** 100ms → 200ms → 400ms → 800ms → 1600ms (capped at maxDelayMs)
|
|
514
|
+
|
|
489
515
|
**Performance comparison:**
|
|
490
516
|
|
|
491
517
|
| Method | Latency | Use Case |
|
|
@@ -493,6 +519,44 @@ async def terminal_input(conn, params: TerminalInputParams) -> SuccessResult:
|
|
|
493
519
|
| `namedRPC()` | ~800-1800ms | Commands that need response |
|
|
494
520
|
| `namedRPCNoWait()` | ~10-30ms | Real-time input, fire-and-forget |
|
|
495
521
|
|
|
522
|
+
### checkApiVersion() - API Contract Validation
|
|
523
|
+
|
|
524
|
+
Validates that the client API version matches the server. Useful for detecting when the frontend needs to refresh after a backend deployment.
|
|
525
|
+
|
|
526
|
+
```tsx
|
|
527
|
+
import { API_VERSION } from '@/_ws'; // Generated client exports version hash
|
|
528
|
+
|
|
529
|
+
// Check version after connect
|
|
530
|
+
const result = await client.checkApiVersion(API_VERSION);
|
|
531
|
+
|
|
532
|
+
if (!result.compatible) {
|
|
533
|
+
// Versions don't match - show refresh prompt
|
|
534
|
+
toast.warning('New version available. Please refresh the page.');
|
|
535
|
+
}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
**Unified Event Handling:**
|
|
539
|
+
|
|
540
|
+
Version mismatch automatically dispatches a `'centrifugo'` event:
|
|
541
|
+
|
|
542
|
+
```tsx
|
|
543
|
+
// Listen globally for version mismatch
|
|
544
|
+
window.addEventListener('centrifugo', (e: CustomEvent) => {
|
|
545
|
+
if (e.detail.type === 'version_mismatch') {
|
|
546
|
+
const { clientVersion, serverVersion, message } = e.detail.data;
|
|
547
|
+
toast.warning(message);
|
|
548
|
+
}
|
|
549
|
+
});
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
**How Version Hash Works:**
|
|
553
|
+
|
|
554
|
+
The version hash is computed from:
|
|
555
|
+
- All `@websocket_rpc` method signatures (name, params, return type)
|
|
556
|
+
- All Pydantic model schemas used in handlers
|
|
557
|
+
|
|
558
|
+
When any handler or model changes, the hash changes, triggering a version mismatch.
|
|
559
|
+
|
|
496
560
|
## Auto-Generated Type-Safe Clients
|
|
497
561
|
|
|
498
562
|
Django-CFG can auto-generate TypeScript clients from your `@websocket_rpc` handlers, providing full type safety and eliminating manual RPC calls.
|
|
@@ -857,16 +921,28 @@ function CustomLogsView() {
|
|
|
857
921
|
import { CentrifugoRPCClient, createLogger } from '@djangocfg/centrifugo';
|
|
858
922
|
|
|
859
923
|
const logger = createLogger('MyApp');
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
'
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
924
|
+
|
|
925
|
+
// Recommended: Options-based constructor
|
|
926
|
+
const client = new CentrifugoRPCClient({
|
|
927
|
+
url: 'ws://localhost:8000/ws',
|
|
928
|
+
token: 'your-token',
|
|
929
|
+
userId: 'user-id',
|
|
930
|
+
timeout: 30000,
|
|
931
|
+
logger,
|
|
932
|
+
// Auto-refresh token on expiration
|
|
933
|
+
getToken: async () => {
|
|
934
|
+
const response = await fetch('/api/auth/refresh-token');
|
|
935
|
+
const { token } = await response.json();
|
|
936
|
+
return token;
|
|
937
|
+
},
|
|
938
|
+
});
|
|
867
939
|
|
|
868
940
|
await client.connect();
|
|
869
941
|
|
|
942
|
+
// Check API version after connect
|
|
943
|
+
import { API_VERSION } from '@/_ws';
|
|
944
|
+
await client.checkApiVersion(API_VERSION);
|
|
945
|
+
|
|
870
946
|
// Subscribe to channel
|
|
871
947
|
const unsubscribe = client.subscribe('channel-name', (data) => {
|
|
872
948
|
console.log('Message:', data);
|
|
@@ -927,6 +1003,12 @@ The package is fully typed with comprehensive TypeScript definitions:
|
|
|
927
1003
|
|
|
928
1004
|
```typescript
|
|
929
1005
|
import type {
|
|
1006
|
+
// Client Options
|
|
1007
|
+
CentrifugoClientOptions,
|
|
1008
|
+
RPCOptions,
|
|
1009
|
+
RetryOptions,
|
|
1010
|
+
VersionCheckResult,
|
|
1011
|
+
|
|
930
1012
|
// Connection
|
|
931
1013
|
ConnectionState,
|
|
932
1014
|
CentrifugoToken,
|
|
@@ -959,6 +1041,85 @@ import type {
|
|
|
959
1041
|
} from '@djangocfg/centrifugo';
|
|
960
1042
|
```
|
|
961
1043
|
|
|
1044
|
+
## Unified Event System
|
|
1045
|
+
|
|
1046
|
+
All Centrifugo events use a single `'centrifugo'` CustomEvent with a type discriminator. This simplifies event handling and reduces the number of event listeners needed.
|
|
1047
|
+
|
|
1048
|
+
**Event Types:**
|
|
1049
|
+
|
|
1050
|
+
| Type | Description | Data |
|
|
1051
|
+
|------|-------------|------|
|
|
1052
|
+
| `error` | RPC call failed | `{ method, error, code, data }` |
|
|
1053
|
+
| `version_mismatch` | API version mismatch | `{ clientVersion, serverVersion, message }` |
|
|
1054
|
+
| `connected` | Successfully connected | `{ userId }` |
|
|
1055
|
+
| `disconnected` | Connection lost | `{ userId, reason }` |
|
|
1056
|
+
| `reconnecting` | Attempting to reconnect | `{ userId, attempt, reason }` |
|
|
1057
|
+
|
|
1058
|
+
**Listening to Events:**
|
|
1059
|
+
|
|
1060
|
+
```tsx
|
|
1061
|
+
// Listen to all Centrifugo events
|
|
1062
|
+
window.addEventListener('centrifugo', (e: CustomEvent) => {
|
|
1063
|
+
const { type, data, timestamp } = e.detail;
|
|
1064
|
+
|
|
1065
|
+
switch (type) {
|
|
1066
|
+
case 'error':
|
|
1067
|
+
console.error('RPC error:', data.method, data.error);
|
|
1068
|
+
break;
|
|
1069
|
+
case 'version_mismatch':
|
|
1070
|
+
toast.warning('Please refresh the page');
|
|
1071
|
+
break;
|
|
1072
|
+
case 'connected':
|
|
1073
|
+
console.log('Connected as', data.userId);
|
|
1074
|
+
break;
|
|
1075
|
+
case 'disconnected':
|
|
1076
|
+
console.log('Disconnected:', data.reason);
|
|
1077
|
+
break;
|
|
1078
|
+
case 'reconnecting':
|
|
1079
|
+
console.log('Reconnecting, attempt', data.attempt);
|
|
1080
|
+
break;
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
```
|
|
1084
|
+
|
|
1085
|
+
**Dispatching Events (for custom integrations):**
|
|
1086
|
+
|
|
1087
|
+
```tsx
|
|
1088
|
+
import {
|
|
1089
|
+
dispatchCentrifugoError,
|
|
1090
|
+
dispatchVersionMismatch,
|
|
1091
|
+
dispatchConnected,
|
|
1092
|
+
dispatchDisconnected,
|
|
1093
|
+
dispatchReconnecting,
|
|
1094
|
+
} from '@djangocfg/centrifugo';
|
|
1095
|
+
|
|
1096
|
+
// Dispatch error
|
|
1097
|
+
dispatchCentrifugoError({
|
|
1098
|
+
method: 'terminal.input',
|
|
1099
|
+
error: 'Connection timeout',
|
|
1100
|
+
code: 408,
|
|
1101
|
+
});
|
|
1102
|
+
|
|
1103
|
+
// Dispatch version mismatch
|
|
1104
|
+
dispatchVersionMismatch({
|
|
1105
|
+
clientVersion: 'abc123',
|
|
1106
|
+
serverVersion: 'def456',
|
|
1107
|
+
message: 'API version mismatch',
|
|
1108
|
+
});
|
|
1109
|
+
```
|
|
1110
|
+
|
|
1111
|
+
**Integration with ErrorsTracker:**
|
|
1112
|
+
|
|
1113
|
+
The `@djangocfg/layouts` package's `ErrorTrackingProvider` automatically listens to `'centrifugo'` events with `type: 'error'` and displays toast notifications.
|
|
1114
|
+
|
|
1115
|
+
```tsx
|
|
1116
|
+
import { ErrorTrackingProvider } from '@djangocfg/layouts';
|
|
1117
|
+
|
|
1118
|
+
<ErrorTrackingProvider centrifugo={{ enabled: true, showToast: true }}>
|
|
1119
|
+
<App />
|
|
1120
|
+
</ErrorTrackingProvider>
|
|
1121
|
+
```
|
|
1122
|
+
|
|
962
1123
|
**Proper Centrifuge Types:**
|
|
963
1124
|
|
|
964
1125
|
The package now uses proper types from the `centrifuge` library:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/centrifugo",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.55",
|
|
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.55",
|
|
55
|
+
"@djangocfg/ui-nextjs": "^2.1.55",
|
|
56
|
+
"@djangocfg/layouts": "^2.1.55",
|
|
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.55",
|
|
65
65
|
"@types/react": "^19.1.0",
|
|
66
66
|
"@types/react-dom": "^19.1.0",
|
|
67
67
|
"moment": "^2.30.1",
|