@djangocfg/centrifugo 2.1.17 → 2.1.19

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.17",
3
+ "version": "2.1.19",
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.17",
55
- "@djangocfg/ui-nextjs": "^2.1.17",
56
- "@djangocfg/layouts": "^2.1.17",
54
+ "@djangocfg/api": "^2.1.19",
55
+ "@djangocfg/ui-nextjs": "^2.1.19",
56
+ "@djangocfg/layouts": "^2.1.19",
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.17",
64
+ "@djangocfg/typescript-config": "^2.1.19",
65
65
  "@types/react": "^19.1.0",
66
66
  "@types/react-dom": "^19.1.0",
67
67
  "moment": "^2.30.1",
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Channel name validation utilities for Centrifugo
3
+ *
4
+ * Helps detect common mistakes with channel naming that can lead to permission errors.
5
+ */
6
+
7
+ export interface ChannelValidationResult {
8
+ valid: boolean;
9
+ warning?: string;
10
+ suggestion?: string;
11
+ }
12
+
13
+ /**
14
+ * Validates Centrifugo channel name and detects potential issues.
15
+ *
16
+ * Common issues:
17
+ * - Using `#` for namespace separator (should use `:`)
18
+ * - User-limited channels without proper JWT token setup
19
+ *
20
+ * @param channel - Channel name to validate
21
+ * @returns Validation result with warnings and suggestions
22
+ *
23
+ * @example
24
+ * ```ts
25
+ * // ❌ Bad: might be interpreted as user-limited channel
26
+ * validateChannelName('terminal#session#abc123')
27
+ * // Returns: { valid: false, warning: "...", suggestion: "Use terminal:session:abc123" }
28
+ *
29
+ * // ✅ Good: proper namespace separator
30
+ * validateChannelName('terminal:session:abc123')
31
+ * // Returns: { valid: true }
32
+ * ```
33
+ */
34
+ export function validateChannelName(channel: string): ChannelValidationResult {
35
+ // Check for multiple # symbols (potential user-limited channel misuse)
36
+ const hashCount = (channel.match(/#/g) || []).length;
37
+
38
+ if (hashCount >= 2) {
39
+ // Pattern: namespace#something#something
40
+ // This might be interpreted as user-limited channel: namespace#user_id#channel
41
+ const parts = channel.split('#');
42
+ const [namespace, possibleUserId, ...rest] = parts;
43
+
44
+ // Check if second part looks like a user ID (numeric)
45
+ const isNumericUserId = /^\d+$/.test(possibleUserId);
46
+
47
+ if (!isNumericUserId && possibleUserId) {
48
+ // Non-numeric second part after # - likely a mistake
49
+ const suggestion = channel.replace(/#/g, ':');
50
+
51
+ return {
52
+ valid: false,
53
+ warning: `Channel "${channel}" uses '#' separator which Centrifugo interprets as user-limited channel boundary. ` +
54
+ `The part "${possibleUserId}" will be treated as user_id, which may cause permission errors if not in JWT token.`,
55
+ suggestion: `Use ':' for namespace separation: "${suggestion}"`
56
+ };
57
+ }
58
+
59
+ if (isNumericUserId) {
60
+ return {
61
+ valid: true,
62
+ warning: `Channel "${channel}" appears to be a user-limited channel (user_id: ${possibleUserId}). ` +
63
+ `Make sure your JWT token's "sub" field matches "${possibleUserId}".`,
64
+ };
65
+ }
66
+ }
67
+
68
+ // Single # is okay for user-limited channels like "user#123"
69
+ if (hashCount === 1) {
70
+ const [namespace, userId] = channel.split('#');
71
+ if (userId && !/^\d+$/.test(userId) && userId !== '*') {
72
+ // Non-numeric user_id (not a wildcard) - might be a mistake
73
+ const suggestion = channel.replace('#', ':');
74
+ return {
75
+ valid: false,
76
+ warning: `Channel "${channel}" uses '#' but "${userId}" doesn't look like a user_id. ` +
77
+ `This might cause permission issues.`,
78
+ suggestion: `Consider using ':' instead: "${suggestion}"`
79
+ };
80
+ }
81
+ }
82
+
83
+ return { valid: true };
84
+ }
85
+
86
+ /**
87
+ * Log channel validation warnings to console (development only).
88
+ *
89
+ * @param channel - Channel name to validate
90
+ * @param logger - Logger instance (optional)
91
+ */
92
+ export function logChannelWarnings(
93
+ channel: string,
94
+ logger?: { warning: (msg: string) => void }
95
+ ): void {
96
+ if (process.env.NODE_ENV === 'production') {
97
+ return; // Skip in production
98
+ }
99
+
100
+ const result = validateChannelName(channel);
101
+
102
+ if (!result.valid && result.warning) {
103
+ const message = `[Centrifugo Channel Warning]\n${result.warning}${
104
+ result.suggestion ? `\n💡 Suggestion: ${result.suggestion}` : ''
105
+ }`;
106
+
107
+ if (logger?.warning) {
108
+ logger.warning(message);
109
+ } else {
110
+ console.warn(message);
111
+ }
112
+ } else if (result.warning) {
113
+ // Valid but has informational warning
114
+ if (logger?.warning) {
115
+ logger.warning(`[Centrifugo] ${result.warning}`);
116
+ }
117
+ }
118
+ }
@@ -15,6 +15,7 @@
15
15
  import { useEffect, useCallback, useRef, useState } from 'react';
16
16
  import { useCentrifugo } from '../providers/CentrifugoProvider';
17
17
  import { createLogger } from '../core/logger';
18
+ import { logChannelWarnings } from '../core/utils/channelValidator';
18
19
 
19
20
  export interface UseSubscriptionOptions<T = any> {
20
21
  channel: string;
@@ -76,6 +77,9 @@ export function useSubscription<T = any>(
76
77
  return;
77
78
  }
78
79
 
80
+ // Validate channel name and log warnings (dev only)
81
+ logChannelWarnings(channel, logger);
82
+
79
83
  logger.info(`Subscribing to channel: ${channel}`);
80
84
 
81
85
  try {