@djangocfg/centrifugo 1.0.1
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/API_GENERATOR.md +253 -0
- package/README.md +84 -0
- package/package.json +35 -0
- package/src/components/CentrifugoDebug.tsx +182 -0
- package/src/components/index.ts +5 -0
- package/src/config.ts +16 -0
- package/src/context/CentrifugoProvider.tsx +228 -0
- package/src/context/index.ts +5 -0
- package/src/hooks/index.ts +9 -0
- package/src/hooks/useLogger.ts +69 -0
- package/src/hooks/useSubscription.ts +86 -0
- package/src/index.ts +27 -0
- package/src/types/index.ts +45 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tsbuildinfo +1 -0
package/API_GENERATOR.md
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Centrifugo Client Generator
|
|
2
|
+
|
|
3
|
+
Documentation for generating Centrifugo WebSocket RPC clients from gRPC services and Django configuration.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The Centrifugo client generator creates typed WebSocket RPC clients from:
|
|
8
|
+
- **gRPC Services**: Protocol buffer definitions and service implementations
|
|
9
|
+
- **Django Config**: Configuration schema from `config.py`
|
|
10
|
+
|
|
11
|
+
This enables full-stack type safety and auto-completion for WebSocket RPC communication.
|
|
12
|
+
|
|
13
|
+
## Paths Reference
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
$PACKAGES = ../packages (monorepo packages root)
|
|
17
|
+
$ADMIN = ../apps/admin (admin app root)
|
|
18
|
+
$DJANGO = ../../../../solution/projects/django (django project root)
|
|
19
|
+
$DJANGO_CFG = ../../../../solution/projects/django_cfg (django_cfg package)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# From admin app root
|
|
26
|
+
cd $ADMIN && make centrifugo
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This will:
|
|
30
|
+
1. Generate clients (TypeScript, Python, Go) in `$DJANGO/openapi/centrifugo/`
|
|
31
|
+
2. Copy TypeScript client to `$ADMIN/src/centrifugo/generated/`
|
|
32
|
+
|
|
33
|
+
## Generator Architecture
|
|
34
|
+
|
|
35
|
+
### 1. Management Command
|
|
36
|
+
|
|
37
|
+
**Location**: `$DJANGO/core/management/commands/generate_centrifugo.py`
|
|
38
|
+
|
|
39
|
+
This is the main entry point that orchestrates the generation process.
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
# Usage
|
|
43
|
+
python manage.py generate_centrifugo
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. Codegen Logic
|
|
47
|
+
|
|
48
|
+
**Location**: `$DJANGO_CFG/apps/integrations/centrifugo/codegen/`
|
|
49
|
+
|
|
50
|
+
The generator has modular architecture:
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
codegen/
|
|
54
|
+
├── management/commands/
|
|
55
|
+
│ └── generate_centrifugo_clients.py # Core generator command
|
|
56
|
+
├── generators/
|
|
57
|
+
│ ├── typescript_thin/ # TypeScript client generator
|
|
58
|
+
│ │ ├── generator.py
|
|
59
|
+
│ │ └── templates/ # Jinja2 templates
|
|
60
|
+
│ ├── python/ # Python client generator
|
|
61
|
+
│ └── go/ # Go client generator
|
|
62
|
+
└── utils/ # Shared utilities
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 3. Templates
|
|
66
|
+
|
|
67
|
+
Client generation uses Jinja2 templates located in:
|
|
68
|
+
- `$DJANGO_CFG/apps/integrations/centrifugo/codegen/generators/typescript_thin/templates/`
|
|
69
|
+
|
|
70
|
+
Key templates:
|
|
71
|
+
- `client.ts.j2` - RPC method wrappers
|
|
72
|
+
- `rpc-client.ts.j2` - Base Centrifugo client with connection logic
|
|
73
|
+
- `types.ts.j2` - TypeScript type definitions
|
|
74
|
+
|
|
75
|
+
## Source Data
|
|
76
|
+
|
|
77
|
+
### gRPC Services
|
|
78
|
+
|
|
79
|
+
**Location**: `$DJANGO/apps/*/grpc_services`
|
|
80
|
+
|
|
81
|
+
The generator scans all Django apps for gRPC service definitions:
|
|
82
|
+
- `.proto` files
|
|
83
|
+
- Service implementations
|
|
84
|
+
- Method signatures
|
|
85
|
+
|
|
86
|
+
### Django Config
|
|
87
|
+
|
|
88
|
+
**Location**: `$DJANGO/api/config.py`
|
|
89
|
+
|
|
90
|
+
Configuration schema is extracted from:
|
|
91
|
+
```python
|
|
92
|
+
class ConfigView(APIView):
|
|
93
|
+
@action(detail=False, methods=['get'])
|
|
94
|
+
def config(self, request):
|
|
95
|
+
return Response({
|
|
96
|
+
'centrifugo': {
|
|
97
|
+
'enabled': settings.CENTRIFUGO_ENABLED,
|
|
98
|
+
'url': settings.CENTRIFUGO_URL,
|
|
99
|
+
# ...
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Generated Outputs
|
|
105
|
+
|
|
106
|
+
### TypeScript Client
|
|
107
|
+
|
|
108
|
+
**Output**: `$DJANGO/openapi/centrifugo/typescript/`
|
|
109
|
+
|
|
110
|
+
Generated files:
|
|
111
|
+
- `client.ts` - High-level RPC method wrappers
|
|
112
|
+
- `rpc-client.ts` - Low-level Centrifugo client
|
|
113
|
+
- `types.ts` - TypeScript interfaces
|
|
114
|
+
|
|
115
|
+
**Copied to**: `$ADMIN/src/centrifugo/generated/`
|
|
116
|
+
|
|
117
|
+
### Python Client
|
|
118
|
+
|
|
119
|
+
**Output**: `$DJANGO/openapi/centrifugo/python/`
|
|
120
|
+
|
|
121
|
+
### Go Client
|
|
122
|
+
|
|
123
|
+
**Output**: `$DJANGO/openapi/centrifugo/go/`
|
|
124
|
+
|
|
125
|
+
## Modifying the Generator
|
|
126
|
+
|
|
127
|
+
The codegen logic is in `$DJANGO_CFG` which is auto-linked to Django. Changes are immediately available:
|
|
128
|
+
|
|
129
|
+
1. Edit generator code or templates in `$DJANGO_CFG/apps/integrations/centrifugo/codegen/`
|
|
130
|
+
2. Re-run generation: `cd $ADMIN && make centrifugo`
|
|
131
|
+
3. Verify generated output
|
|
132
|
+
|
|
133
|
+
### Example: Modify TypeScript Template
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# Edit base client template
|
|
137
|
+
vim $DJANGO_CFG/apps/integrations/centrifugo/codegen/generators/typescript_thin/templates/rpc-client.ts.j2
|
|
138
|
+
|
|
139
|
+
# Regenerate
|
|
140
|
+
cd $ADMIN && make centrifugo
|
|
141
|
+
|
|
142
|
+
# Check output
|
|
143
|
+
cat $ADMIN/src/centrifugo/generated/rpc-client.ts
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Integration with Admin App
|
|
147
|
+
|
|
148
|
+
The admin app wraps the generated client with app-specific providers:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
$ADMIN/src/centrifugo/
|
|
152
|
+
├── generated/ # Auto-generated (DO NOT EDIT)
|
|
153
|
+
│ ├── client.ts
|
|
154
|
+
│ ├── rpc-client.ts
|
|
155
|
+
│ └── types.ts
|
|
156
|
+
├── AppCentrifugoProvider.tsx # App-specific wrapper
|
|
157
|
+
└── index.ts # Re-exports from @djangocfg/centrifugo
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Universal components (context, hooks, debug) are in `@djangocfg/centrifugo` package:
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
$PACKAGES/centrifugo/src/
|
|
164
|
+
├── context/ # React context provider
|
|
165
|
+
├── hooks/ # Custom hooks
|
|
166
|
+
├── components/ # Debug panel, etc.
|
|
167
|
+
├── config.ts # Environment config
|
|
168
|
+
└── types.ts # Shared types
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Development Workflow
|
|
172
|
+
|
|
173
|
+
### 1. Add New gRPC Service
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# 1. Define .proto file
|
|
177
|
+
vim $DJANGO/apps/myapp/grpc_services/myservice.proto
|
|
178
|
+
|
|
179
|
+
# 2. Implement service
|
|
180
|
+
vim $DJANGO/apps/myapp/grpc_services/myservice.py
|
|
181
|
+
|
|
182
|
+
# 3. Regenerate client
|
|
183
|
+
cd $ADMIN && make centrifugo
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 2. Use in Frontend
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
import { useCentrifugo } from '@/centrifugo';
|
|
190
|
+
|
|
191
|
+
function MyComponent() {
|
|
192
|
+
const { client, isConnected } = useCentrifugo();
|
|
193
|
+
|
|
194
|
+
const handleCall = async () => {
|
|
195
|
+
// Auto-generated typed method
|
|
196
|
+
const result = await client?.myMethod({ param: 'value' });
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Troubleshooting
|
|
202
|
+
|
|
203
|
+
### Client Generation Fails
|
|
204
|
+
|
|
205
|
+
1. Check Django apps are properly configured
|
|
206
|
+
2. Verify gRPC services are valid
|
|
207
|
+
3. Check template syntax in `$DJANGO_CFG/apps/integrations/centrifugo/codegen/generators/typescript_thin/templates/`
|
|
208
|
+
|
|
209
|
+
### TypeScript Errors After Generation
|
|
210
|
+
|
|
211
|
+
1. Check generated types in `src/centrifugo/generated/types.ts`
|
|
212
|
+
2. Verify imports match package structure
|
|
213
|
+
3. Run type check: `pnpm tsc --noEmit`
|
|
214
|
+
|
|
215
|
+
### Connection Issues
|
|
216
|
+
|
|
217
|
+
Use the debug panel (FAB button in bottom-left corner) to inspect:
|
|
218
|
+
- Connection state
|
|
219
|
+
- Authentication status
|
|
220
|
+
- WebSocket URL
|
|
221
|
+
- Channel subscriptions
|
|
222
|
+
- Error messages
|
|
223
|
+
|
|
224
|
+
## Advanced Configuration
|
|
225
|
+
|
|
226
|
+
### Custom Templates
|
|
227
|
+
|
|
228
|
+
You can customize the generated output by modifying templates:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Location
|
|
232
|
+
$DJANGO_CFG/apps/integrations/centrifugo/codegen/generators/typescript_thin/templates/
|
|
233
|
+
|
|
234
|
+
# Files
|
|
235
|
+
client.ts.j2 # Method wrappers
|
|
236
|
+
rpc-client.ts.j2 # Base client
|
|
237
|
+
types.ts.j2 # Type definitions
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Generator Options
|
|
241
|
+
|
|
242
|
+
The `generate_centrifugo_clients` command supports options:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
python manage.py generate_centrifugo_clients --help
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## References
|
|
249
|
+
|
|
250
|
+
- **Centrifugo Docs**: https://centrifugal.dev/
|
|
251
|
+
- **Centrifuge-JS**: https://github.com/centrifugal/centrifuge-js
|
|
252
|
+
- **gRPC**: https://grpc.io/
|
|
253
|
+
- **Jinja2**: https://jinja.palletsprojects.com/
|
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# @djangocfg/centrifugo
|
|
2
|
+
|
|
3
|
+
WebSocket client package for Django-CFG + Centrifugo integration.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @djangocfg/centrifugo
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### 1. Wrap your app with CentrifugoProvider
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { CentrifugoProvider } from '@djangocfg/centrifugo';
|
|
17
|
+
|
|
18
|
+
function App() {
|
|
19
|
+
return (
|
|
20
|
+
<CentrifugoProvider auth={authContext} config={centrifugoConfig}>
|
|
21
|
+
<YourApp />
|
|
22
|
+
</CentrifugoProvider>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### 2. Use hooks to interact with Centrifugo
|
|
28
|
+
|
|
29
|
+
```tsx
|
|
30
|
+
import { useCentrifugo, useSubscription } from '@djangocfg/centrifugo';
|
|
31
|
+
|
|
32
|
+
function MyComponent() {
|
|
33
|
+
const { isConnected, baseClient } = useCentrifugo();
|
|
34
|
+
|
|
35
|
+
const { data, isLoading, error } = useSubscription({
|
|
36
|
+
channel: 'notifications',
|
|
37
|
+
onPublication: (data) => console.log('New message:', data),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return <div>Connected: {isConnected ? 'Yes' : 'No'}</div>;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 3. Debug connection (development only)
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
import { CentrifugoDebug } from '@djangocfg/centrifugo/components';
|
|
48
|
+
|
|
49
|
+
function DevTools() {
|
|
50
|
+
return <CentrifugoDebug auth={authContext} config={centrifugoConfig} />;
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Exports
|
|
55
|
+
|
|
56
|
+
### Context & Provider
|
|
57
|
+
- `CentrifugoProvider` - WebSocket connection provider
|
|
58
|
+
- `useCentrifugo` - Access connection state and client
|
|
59
|
+
|
|
60
|
+
### Hooks
|
|
61
|
+
- `useCentrifugoLogger` - Consola logger for debugging
|
|
62
|
+
- `useSubscription` - Subscribe to Centrifugo channels
|
|
63
|
+
|
|
64
|
+
### Components
|
|
65
|
+
- `CentrifugoDebug` - Development debug panel
|
|
66
|
+
|
|
67
|
+
### Types
|
|
68
|
+
- `CentrifugoConfig` - Configuration interface
|
|
69
|
+
- `CentrifugoToken` - JWT token structure
|
|
70
|
+
- `User` - User type with Centrifugo data
|
|
71
|
+
- `AuthContext` - Authentication context interface
|
|
72
|
+
- `CentrifugoProviderProps` - Provider props
|
|
73
|
+
- `CentrifugoContextValue` - Context value type
|
|
74
|
+
|
|
75
|
+
## Architecture
|
|
76
|
+
|
|
77
|
+
This package provides universal WebSocket logic that can be used across multiple Django-CFG admin applications. The generated RPC client is imported separately from your app's generated code.
|
|
78
|
+
|
|
79
|
+
## Requirements
|
|
80
|
+
|
|
81
|
+
- React 19+
|
|
82
|
+
- `@djangocfg/ui` - UI components
|
|
83
|
+
- `@djangocfg/layouts` - Layout components
|
|
84
|
+
- `centrifuge` - WebSocket client library
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@djangocfg/centrifugo",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "WebSocket RPC client for Django-CFG + Centrifugo integration",
|
|
5
|
+
"main": "./src/index.ts",
|
|
6
|
+
"types": "./src/index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./components": "./src/components/index.ts",
|
|
10
|
+
"./hooks": "./src/hooks/index.ts",
|
|
11
|
+
"./context": "./src/context/index.ts",
|
|
12
|
+
"./types": "./src/types/index.ts"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"check": "tsc --noEmit",
|
|
16
|
+
"lint": "eslint . --max-warnings 0"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"centrifuge": "^5.2.2"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"@djangocfg/ui": "^1.2.26",
|
|
23
|
+
"@djangocfg/layouts": "^1.2.26",
|
|
24
|
+
"consola": "^3.4.2",
|
|
25
|
+
"lucide-react": "^0.468.0",
|
|
26
|
+
"react": "^19.0.0",
|
|
27
|
+
"react-dom": "^19.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@djangocfg/typescript-config": "^1.2.26",
|
|
31
|
+
"@types/react": "^19.0.6",
|
|
32
|
+
"@types/react-dom": "^19.0.2",
|
|
33
|
+
"typescript": "^5.7.3"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centrifugo Debug Component
|
|
3
|
+
*
|
|
4
|
+
* Displays detailed Centrifugo connection status and diagnostics
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use client';
|
|
8
|
+
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import { useCentrifugo } from '../context';
|
|
11
|
+
import { useAuth } from '@djangocfg/layouts';
|
|
12
|
+
import { Card, CardHeader, CardTitle, CardContent, Badge, Separator, Button, useCopy } from '@djangocfg/ui';
|
|
13
|
+
import { PrettyCode } from '@djangocfg/ui/tools';
|
|
14
|
+
import { Wifi, WifiOff, Radio, X, Copy, RefreshCw } from 'lucide-react';
|
|
15
|
+
import { centrifugoConfig } from '../config';
|
|
16
|
+
|
|
17
|
+
export function CentrifugoDebug() {
|
|
18
|
+
const { isConnected, isConnecting, error, connectionState, baseClient, enabled, reconnect } = useCentrifugo();
|
|
19
|
+
const { isAuthenticated, isLoading: authLoading, user } = useAuth();
|
|
20
|
+
const [isVisible, setIsVisible] = React.useState(true);
|
|
21
|
+
const [isReconnecting, setIsReconnecting] = React.useState(false);
|
|
22
|
+
const { copyToClipboard } = useCopy();
|
|
23
|
+
|
|
24
|
+
const centrifugoToken = user?.centrifugo;
|
|
25
|
+
const hasCentrifugoToken = !!centrifugoToken?.token;
|
|
26
|
+
|
|
27
|
+
const shouldConnect = isAuthenticated && !authLoading && enabled && hasCentrifugoToken;
|
|
28
|
+
|
|
29
|
+
const debugInfo = React.useMemo(() => ({
|
|
30
|
+
connection: {
|
|
31
|
+
status: connectionState,
|
|
32
|
+
isConnected,
|
|
33
|
+
isConnecting,
|
|
34
|
+
error: error?.message || null,
|
|
35
|
+
},
|
|
36
|
+
auth: {
|
|
37
|
+
isAuthenticated,
|
|
38
|
+
authLoading,
|
|
39
|
+
userId: user?.id,
|
|
40
|
+
},
|
|
41
|
+
config: {
|
|
42
|
+
centrifugoEnabled: enabled,
|
|
43
|
+
hasCentrifugoToken,
|
|
44
|
+
},
|
|
45
|
+
websocket: {
|
|
46
|
+
url: centrifugoToken?.centrifugo_url || null,
|
|
47
|
+
channels: centrifugoToken?.channels || [],
|
|
48
|
+
activeSubscriptions: baseClient?.getAllSubscriptions?.() || [],
|
|
49
|
+
},
|
|
50
|
+
autoConnect: {
|
|
51
|
+
shouldConnect,
|
|
52
|
+
reasons: {
|
|
53
|
+
authenticated: isAuthenticated,
|
|
54
|
+
authLoading: !authLoading,
|
|
55
|
+
centrifugoEnabled: enabled,
|
|
56
|
+
hasToken: hasCentrifugoToken,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
}), [connectionState, isConnected, isConnecting, error, isAuthenticated, authLoading, user?.id, enabled, hasCentrifugoToken, centrifugoToken, baseClient, shouldConnect]);
|
|
60
|
+
|
|
61
|
+
const copyDebugInfo = () => {
|
|
62
|
+
copyToClipboard(JSON.stringify(debugInfo, null, 2), 'Debug info copied to clipboard');
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const handleReconnect = async () => {
|
|
66
|
+
setIsReconnecting(true);
|
|
67
|
+
try {
|
|
68
|
+
await reconnect();
|
|
69
|
+
} finally {
|
|
70
|
+
setTimeout(() => setIsReconnecting(false), 1000);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Only show in development mode and not in static builds
|
|
75
|
+
if (!centrifugoConfig.showDebugPanel) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<>
|
|
81
|
+
{/* FAB Button (always visible) */}
|
|
82
|
+
<button
|
|
83
|
+
onClick={() => setIsVisible(!isVisible)}
|
|
84
|
+
className="rounded-full bg-primary text-primary-foreground shadow-lg hover:shadow-xl transition-all duration-200 flex items-center justify-center"
|
|
85
|
+
style={{ position: 'fixed', bottom: '1rem', left: '1rem', width: '56px', height: '56px', zIndex: 9999 }}
|
|
86
|
+
title="Toggle Centrifugo Debug Panel"
|
|
87
|
+
>
|
|
88
|
+
{isConnected ? (
|
|
89
|
+
<Wifi style={{ width: '24px', height: '24px' }} />
|
|
90
|
+
) : (
|
|
91
|
+
<WifiOff style={{ width: '24px', height: '24px' }} />
|
|
92
|
+
)}
|
|
93
|
+
</button>
|
|
94
|
+
|
|
95
|
+
{/* Debug Panel */}
|
|
96
|
+
{isVisible && (
|
|
97
|
+
<div style={{ position: 'fixed', bottom: '5rem', left: '1rem', width: '420px', zIndex: 9999 }}>
|
|
98
|
+
<Card className="shadow-lg">
|
|
99
|
+
<CardHeader className="pb-3">
|
|
100
|
+
<div className="flex items-center justify-between">
|
|
101
|
+
<div className="flex items-center gap-2">
|
|
102
|
+
{isConnected ? (
|
|
103
|
+
<Wifi className="text-green-600 dark:text-green-500" style={{ width: '16px', height: '16px' }} />
|
|
104
|
+
) : (
|
|
105
|
+
<WifiOff className="text-red-600 dark:text-red-500" style={{ width: '16px', height: '16px' }} />
|
|
106
|
+
)}
|
|
107
|
+
<CardTitle className="text-base">Centrifugo Debug</CardTitle>
|
|
108
|
+
</div>
|
|
109
|
+
<div className="flex items-center gap-1">
|
|
110
|
+
<Button
|
|
111
|
+
variant="ghost"
|
|
112
|
+
size="sm"
|
|
113
|
+
onClick={handleReconnect}
|
|
114
|
+
disabled={isReconnecting || isConnecting}
|
|
115
|
+
className="px-2 py-1"
|
|
116
|
+
title="Reconnect"
|
|
117
|
+
>
|
|
118
|
+
<RefreshCw
|
|
119
|
+
style={{ width: '14px', height: '14px' }}
|
|
120
|
+
className={isReconnecting ? 'animate-spin' : ''}
|
|
121
|
+
/>
|
|
122
|
+
</Button>
|
|
123
|
+
<Button
|
|
124
|
+
variant="ghost"
|
|
125
|
+
size="sm"
|
|
126
|
+
onClick={copyDebugInfo}
|
|
127
|
+
className="px-2 py-1"
|
|
128
|
+
title="Copy debug info"
|
|
129
|
+
>
|
|
130
|
+
<Copy style={{ width: '14px', height: '14px' }} />
|
|
131
|
+
</Button>
|
|
132
|
+
<Button
|
|
133
|
+
variant="ghost"
|
|
134
|
+
size="sm"
|
|
135
|
+
onClick={() => setIsVisible(false)}
|
|
136
|
+
className="px-2 py-1"
|
|
137
|
+
>
|
|
138
|
+
<X style={{ width: '14px', height: '14px' }} />
|
|
139
|
+
</Button>
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
</CardHeader>
|
|
143
|
+
|
|
144
|
+
<CardContent className="space-y-3">
|
|
145
|
+
{/* Connection Status Badge */}
|
|
146
|
+
<div className="flex items-center justify-between">
|
|
147
|
+
<span className="text-sm font-medium">Status:</span>
|
|
148
|
+
<Badge variant={isConnected ? 'default' : isConnecting ? 'secondary' : 'destructive'}>
|
|
149
|
+
{connectionState.toUpperCase()}
|
|
150
|
+
</Badge>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
{/* Auto-Connect Decision */}
|
|
154
|
+
<div className="flex items-center justify-between">
|
|
155
|
+
<span className="text-sm font-medium">Should Connect:</span>
|
|
156
|
+
<Badge variant={shouldConnect ? 'default' : 'outline'}>
|
|
157
|
+
{shouldConnect ? 'YES' : 'NO'}
|
|
158
|
+
</Badge>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
<Separator />
|
|
162
|
+
|
|
163
|
+
{/* JSON Debug Output */}
|
|
164
|
+
<div className="space-y-2">
|
|
165
|
+
<div className="flex items-center gap-2">
|
|
166
|
+
<Radio className="text-muted-foreground" style={{ width: '14px', height: '14px' }} />
|
|
167
|
+
<span className="text-sm font-medium">Debug State:</span>
|
|
168
|
+
</div>
|
|
169
|
+
<div className="max-h-[300px] overflow-y-auto">
|
|
170
|
+
<PrettyCode data={debugInfo} language="json" />
|
|
171
|
+
</div>
|
|
172
|
+
</div>
|
|
173
|
+
</CardContent>
|
|
174
|
+
</Card>
|
|
175
|
+
</div>
|
|
176
|
+
)}
|
|
177
|
+
</>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Alias for backward compatibility
|
|
182
|
+
export const WSRPCDebug = CentrifugoDebug;
|
package/src/config.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centrifugo Package Configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export const isDevelopment = process.env.NODE_ENV === 'development';
|
|
6
|
+
export const isProduction = !isDevelopment;
|
|
7
|
+
export const isStaticBuild = process.env.NEXT_PUBLIC_STATIC_BUILD === 'true';
|
|
8
|
+
|
|
9
|
+
const showDebugPanel = isDevelopment && !isStaticBuild;
|
|
10
|
+
|
|
11
|
+
export const centrifugoConfig = {
|
|
12
|
+
// Show debug panel only in development and not in static builds
|
|
13
|
+
showDebugPanel,
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
export type CentrifugoConfig = typeof centrifugoConfig;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Centrifugo Provider
|
|
3
|
+
*
|
|
4
|
+
* Generic WebSocket RPC provider that can be used in any app.
|
|
5
|
+
* Automatically accesses auth context from @djangocfg/layouts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use client';
|
|
9
|
+
|
|
10
|
+
import { createContext, useContext, useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
|
11
|
+
import { useAuth } from '@djangocfg/layouts';
|
|
12
|
+
import type { CentrifugoProviderProps, CentrifugoContextValue } from '../types';
|
|
13
|
+
import { useCentrifugoLogger } from '../hooks';
|
|
14
|
+
import { CentrifugoDebug } from '../components';
|
|
15
|
+
|
|
16
|
+
const CentrifugoContext = createContext<CentrifugoContextValue | undefined>(undefined);
|
|
17
|
+
|
|
18
|
+
export function CentrifugoProvider({
|
|
19
|
+
children,
|
|
20
|
+
enabled = false,
|
|
21
|
+
RPCClientClass,
|
|
22
|
+
APIClientClass,
|
|
23
|
+
url,
|
|
24
|
+
autoConnect: autoConnectProp = true,
|
|
25
|
+
}: CentrifugoProviderProps) {
|
|
26
|
+
const { isAuthenticated, isLoading, user } = useAuth();
|
|
27
|
+
|
|
28
|
+
const [client, setClient] = useState<any>(null);
|
|
29
|
+
const [baseClient, setBaseClient] = useState<any>(null);
|
|
30
|
+
const [isConnected, setIsConnected] = useState(false);
|
|
31
|
+
const [isConnecting, setIsConnecting] = useState(false);
|
|
32
|
+
const [error, setError] = useState<Error | null>(null);
|
|
33
|
+
|
|
34
|
+
const logger = useCentrifugoLogger();
|
|
35
|
+
|
|
36
|
+
const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
37
|
+
const hasConnectedRef = useRef(false);
|
|
38
|
+
const isConnectingRef = useRef(false);
|
|
39
|
+
const isMountedRef = useRef(true);
|
|
40
|
+
|
|
41
|
+
const centrifugoToken = user?.centrifugo;
|
|
42
|
+
const hasCentrifugoToken = !!centrifugoToken?.token;
|
|
43
|
+
|
|
44
|
+
const wsUrl = useMemo(() => {
|
|
45
|
+
if (url) return url;
|
|
46
|
+
if (centrifugoToken?.centrifugo_url) return centrifugoToken.centrifugo_url;
|
|
47
|
+
return '';
|
|
48
|
+
}, [url, centrifugoToken?.centrifugo_url]);
|
|
49
|
+
|
|
50
|
+
const autoConnect = autoConnectProp &&
|
|
51
|
+
(isAuthenticated && !isLoading) &&
|
|
52
|
+
enabled &&
|
|
53
|
+
hasCentrifugoToken;
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
if (!isLoading) {
|
|
57
|
+
logger.info(`[RPC] Auto-connect decision: ${autoConnect ? 'YES' : 'NO'}`);
|
|
58
|
+
logger.info(`[RPC] - Authenticated: ${isAuthenticated}`);
|
|
59
|
+
logger.info(`[RPC] - Auth loading: ${isLoading}`);
|
|
60
|
+
logger.info(`[RPC] - Centrifugo enabled: ${enabled}`);
|
|
61
|
+
logger.info(`[RPC] - Centrifugo token from profile: ${hasCentrifugoToken}`);
|
|
62
|
+
logger.info(`[RPC] - WebSocket URL: ${wsUrl}`);
|
|
63
|
+
|
|
64
|
+
if (hasCentrifugoToken && centrifugoToken) {
|
|
65
|
+
logger.info(`[RPC] - Channels: ${centrifugoToken.channels?.join(', ')}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}, [autoConnect, isAuthenticated, isLoading, enabled, hasCentrifugoToken, centrifugoToken, logger, wsUrl]);
|
|
69
|
+
|
|
70
|
+
const connect = useCallback(async () => {
|
|
71
|
+
if (hasConnectedRef.current || isConnectingRef.current) return;
|
|
72
|
+
if (isConnecting || isConnected) return;
|
|
73
|
+
|
|
74
|
+
isConnectingRef.current = true;
|
|
75
|
+
setIsConnecting(true);
|
|
76
|
+
setError(null);
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
logger.info('Connecting to WebSocket RPC server...');
|
|
80
|
+
|
|
81
|
+
if (!centrifugoToken?.token) {
|
|
82
|
+
throw new Error('No Centrifugo token available in user profile. Please refresh the page.');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const token = centrifugoToken.token;
|
|
86
|
+
let userId = user?.id?.toString() || '1';
|
|
87
|
+
|
|
88
|
+
if (!user?.id) {
|
|
89
|
+
try {
|
|
90
|
+
const tokenPayload = JSON.parse(atob(token.split('.')[1]));
|
|
91
|
+
userId = tokenPayload.user_id?.toString() || tokenPayload.sub?.toString() || '1';
|
|
92
|
+
} catch (err) {
|
|
93
|
+
// Silently fallback
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const baseRPC = new RPCClientClass(wsUrl, token, userId);
|
|
98
|
+
await baseRPC.connect();
|
|
99
|
+
|
|
100
|
+
if (!isMountedRef.current) {
|
|
101
|
+
baseRPC.disconnect();
|
|
102
|
+
isConnectingRef.current = false;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const rpcClient = new APIClientClass(baseRPC);
|
|
107
|
+
|
|
108
|
+
hasConnectedRef.current = true;
|
|
109
|
+
isConnectingRef.current = false;
|
|
110
|
+
|
|
111
|
+
setBaseClient(baseRPC);
|
|
112
|
+
setClient(rpcClient);
|
|
113
|
+
setIsConnected(true);
|
|
114
|
+
setError(null);
|
|
115
|
+
|
|
116
|
+
logger.success('WebSocket RPC connected successfully');
|
|
117
|
+
} catch (err) {
|
|
118
|
+
const error = err instanceof Error ? err : new Error('Connection failed');
|
|
119
|
+
setError(error);
|
|
120
|
+
setBaseClient(null);
|
|
121
|
+
setClient(null);
|
|
122
|
+
setIsConnected(false);
|
|
123
|
+
hasConnectedRef.current = false;
|
|
124
|
+
isConnectingRef.current = false;
|
|
125
|
+
|
|
126
|
+
const isAuthError = error.message.includes('token') ||
|
|
127
|
+
error.message.includes('auth') ||
|
|
128
|
+
error.message.includes('expired');
|
|
129
|
+
|
|
130
|
+
if (isAuthError) {
|
|
131
|
+
logger.error('WebSocket RPC authentication failed', error);
|
|
132
|
+
} else {
|
|
133
|
+
logger.error('WebSocket RPC connection failed', error);
|
|
134
|
+
reconnectTimeoutRef.current = setTimeout(() => {
|
|
135
|
+
logger.info('Attempting to reconnect...');
|
|
136
|
+
connect();
|
|
137
|
+
}, 5000);
|
|
138
|
+
}
|
|
139
|
+
} finally {
|
|
140
|
+
setIsConnecting(false);
|
|
141
|
+
}
|
|
142
|
+
}, [wsUrl, centrifugoToken, user, logger, RPCClientClass, APIClientClass, isConnecting, isConnected]);
|
|
143
|
+
|
|
144
|
+
const disconnect = useCallback(() => {
|
|
145
|
+
if (isConnectingRef.current) return;
|
|
146
|
+
|
|
147
|
+
if (reconnectTimeoutRef.current) {
|
|
148
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
149
|
+
reconnectTimeoutRef.current = null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (baseClient) {
|
|
153
|
+
logger.info('Disconnecting from WebSocket RPC server...');
|
|
154
|
+
baseClient.disconnect();
|
|
155
|
+
setBaseClient(null);
|
|
156
|
+
setClient(null);
|
|
157
|
+
setIsConnected(false);
|
|
158
|
+
setError(null);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
hasConnectedRef.current = false;
|
|
162
|
+
isConnectingRef.current = false;
|
|
163
|
+
}, [baseClient, logger]);
|
|
164
|
+
|
|
165
|
+
const reconnect = useCallback(async () => {
|
|
166
|
+
disconnect();
|
|
167
|
+
await connect();
|
|
168
|
+
}, [connect, disconnect]);
|
|
169
|
+
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
isMountedRef.current = true;
|
|
172
|
+
|
|
173
|
+
if (autoConnect && !hasConnectedRef.current) {
|
|
174
|
+
connect();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return () => {
|
|
178
|
+
if (isConnectingRef.current && !hasConnectedRef.current) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!hasConnectedRef.current) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
isMountedRef.current = false;
|
|
187
|
+
disconnect();
|
|
188
|
+
};
|
|
189
|
+
}, [autoConnect, connect, disconnect]);
|
|
190
|
+
|
|
191
|
+
const connectionState = isConnected
|
|
192
|
+
? 'connected'
|
|
193
|
+
: isConnecting
|
|
194
|
+
? 'connecting'
|
|
195
|
+
: error
|
|
196
|
+
? 'error'
|
|
197
|
+
: 'disconnected';
|
|
198
|
+
|
|
199
|
+
const value: CentrifugoContextValue = {
|
|
200
|
+
client,
|
|
201
|
+
baseClient,
|
|
202
|
+
isConnected,
|
|
203
|
+
isConnecting,
|
|
204
|
+
error,
|
|
205
|
+
connectionState,
|
|
206
|
+
enabled,
|
|
207
|
+
connect,
|
|
208
|
+
disconnect,
|
|
209
|
+
reconnect,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
<CentrifugoContext.Provider value={value}>
|
|
214
|
+
{children}
|
|
215
|
+
<CentrifugoDebug />
|
|
216
|
+
</CentrifugoContext.Provider>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function useCentrifugo(): CentrifugoContextValue {
|
|
221
|
+
const context = useContext(CentrifugoContext);
|
|
222
|
+
|
|
223
|
+
if (context === undefined) {
|
|
224
|
+
throw new Error('useCentrifugo must be used within a CentrifugoProvider');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return context;
|
|
228
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centrifugo Hooks
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { useCentrifugoLogger, useRPCLogger } from './useLogger';
|
|
6
|
+
export type { CentrifugoLogger, RPCLogger } from './useLogger';
|
|
7
|
+
|
|
8
|
+
export { useSubscription } from './useSubscription';
|
|
9
|
+
export type { useSubscriptionOptions } from './useSubscription';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centrifugo Logger Hook
|
|
3
|
+
*
|
|
4
|
+
* React hook for Centrifugo logging using consola library.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use client';
|
|
8
|
+
|
|
9
|
+
import { useMemo } from 'react';
|
|
10
|
+
import { createConsola, type ConsolaInstance } from 'consola';
|
|
11
|
+
|
|
12
|
+
export interface CentrifugoLogger {
|
|
13
|
+
info: (message: string) => void;
|
|
14
|
+
debug: (message: string) => void;
|
|
15
|
+
warning: (message: string) => void;
|
|
16
|
+
error: (message: string, error?: Error) => void;
|
|
17
|
+
success: (message: string) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* React hook for Centrifugo logger with consola
|
|
22
|
+
*
|
|
23
|
+
* @param isDevelopment - Enable debug logging (optional)
|
|
24
|
+
* @returns Centrifugo logger instance
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```tsx
|
|
28
|
+
* function MyComponent() {
|
|
29
|
+
* const logger = useCentrifugoLogger();
|
|
30
|
+
* logger.info('Connected to Centrifugo server');
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function useCentrifugoLogger(isDevelopment = process.env.NODE_ENV === 'development'): CentrifugoLogger {
|
|
35
|
+
const logger = useMemo(() => {
|
|
36
|
+
const consola = createConsola({
|
|
37
|
+
level: isDevelopment ? 4 : 3, // debug in dev, info in prod
|
|
38
|
+
formatOptions: {
|
|
39
|
+
colors: true,
|
|
40
|
+
date: false,
|
|
41
|
+
compact: !isDevelopment,
|
|
42
|
+
},
|
|
43
|
+
}).withTag('Centrifugo');
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
info: (message: string) => {
|
|
47
|
+
if (isDevelopment) consola.info(message);
|
|
48
|
+
},
|
|
49
|
+
debug: (message: string) => {
|
|
50
|
+
if (isDevelopment) consola.debug(message);
|
|
51
|
+
},
|
|
52
|
+
warning: (message: string) => {
|
|
53
|
+
if (isDevelopment) consola.warn(message);
|
|
54
|
+
},
|
|
55
|
+
error: (message: string, error?: Error) => {
|
|
56
|
+
consola.error(message, error || '');
|
|
57
|
+
},
|
|
58
|
+
success: (message: string) => {
|
|
59
|
+
if (isDevelopment) consola.success(message);
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
}, [isDevelopment]);
|
|
63
|
+
|
|
64
|
+
return logger;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Alias for backward compatibility
|
|
68
|
+
export const useRPCLogger = useCentrifugoLogger;
|
|
69
|
+
export type RPCLogger = CentrifugoLogger;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook for subscribing to Centrifugo channels.
|
|
3
|
+
*
|
|
4
|
+
* Automatically handles subscription lifecycle (mount/unmount).
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* // Subscribe to bot heartbeat
|
|
8
|
+
* useSubscription('bot#bot-123#heartbeat', (data) => {
|
|
9
|
+
* console.log('Heartbeat:', data);
|
|
10
|
+
* updateMetrics(data);
|
|
11
|
+
* });
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* // Conditional subscription
|
|
15
|
+
* useSubscription(
|
|
16
|
+
* 'bot#bot-123#status',
|
|
17
|
+
* (data) => console.log('Status:', data),
|
|
18
|
+
* { enabled: isMonitoring }
|
|
19
|
+
* );
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { useEffect, useRef } from 'react';
|
|
23
|
+
import { useCentrifugo } from '../context';
|
|
24
|
+
|
|
25
|
+
export interface useSubscriptionOptions {
|
|
26
|
+
/**
|
|
27
|
+
* Enable/disable subscription.
|
|
28
|
+
* Defaults to true.
|
|
29
|
+
*/
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Hook for subscribing to a Centrifugo channel.
|
|
35
|
+
*
|
|
36
|
+
* @param channel - Channel name (e.g., 'bot#bot-123#heartbeat'). Set to null to disable.
|
|
37
|
+
* @param callback - Callback function for received messages
|
|
38
|
+
* @param options - Subscription options
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* function BotMonitor({ botId }: { botId: string }) {
|
|
43
|
+
* const [heartbeat, setHeartbeat] = useState(null);
|
|
44
|
+
*
|
|
45
|
+
* useSubscription(`bot#${botId}#heartbeat`, (data) => {
|
|
46
|
+
* setHeartbeat(data);
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* return <div>CPU: {heartbeat?.cpu_usage}%</div>;
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function useSubscription<T = any>(
|
|
54
|
+
channel: string | null,
|
|
55
|
+
callback: (data: T) => void,
|
|
56
|
+
options: useSubscriptionOptions = {}
|
|
57
|
+
) {
|
|
58
|
+
const { baseClient, isConnected } = useCentrifugo();
|
|
59
|
+
const callbackRef = useRef(callback);
|
|
60
|
+
const { enabled = true } = options;
|
|
61
|
+
|
|
62
|
+
// Keep callback ref updated (avoid stale closures)
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
callbackRef.current = callback;
|
|
65
|
+
}, [callback]);
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
// Don't subscribe if disabled, not connected, or no channel
|
|
69
|
+
if (!enabled || !isConnected || !baseClient || !channel) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
console.log(`🎣 useSubscription: Subscribing to ${channel}`);
|
|
74
|
+
|
|
75
|
+
// Subscribe with stable callback wrapper
|
|
76
|
+
const unsubscribe = baseClient.subscribe(channel, (data) => {
|
|
77
|
+
callbackRef.current(data);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Cleanup on unmount or channel change
|
|
81
|
+
return () => {
|
|
82
|
+
console.log(`🎣 useSubscription: Unsubscribing from ${channel}`);
|
|
83
|
+
unsubscribe();
|
|
84
|
+
};
|
|
85
|
+
}, [channel, enabled, isConnected, baseClient]);
|
|
86
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @djangocfg/centrifugo
|
|
3
|
+
*
|
|
4
|
+
* WebSocket client for Django-CFG + Centrifugo integration
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Context & Provider
|
|
8
|
+
export { CentrifugoProvider, useCentrifugo } from './context';
|
|
9
|
+
|
|
10
|
+
// Hooks
|
|
11
|
+
export { useCentrifugoLogger, useRPCLogger, useSubscription } from './hooks';
|
|
12
|
+
export type { CentrifugoLogger, RPCLogger } from './hooks';
|
|
13
|
+
export type { useSubscriptionOptions } from './hooks';
|
|
14
|
+
|
|
15
|
+
// Components
|
|
16
|
+
export { CentrifugoDebug, WSRPCDebug } from './components';
|
|
17
|
+
|
|
18
|
+
// Types
|
|
19
|
+
export type {
|
|
20
|
+
CentrifugoToken,
|
|
21
|
+
User,
|
|
22
|
+
CentrifugoProviderProps,
|
|
23
|
+
CentrifugoContextValue,
|
|
24
|
+
} from './types';
|
|
25
|
+
|
|
26
|
+
// Config
|
|
27
|
+
export { isDevelopment, isProduction, isStaticBuild, centrifugoConfig } from './config';
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centrifugo Package Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface CentrifugoToken {
|
|
6
|
+
token: string;
|
|
7
|
+
centrifugo_url: string;
|
|
8
|
+
channels: string[];
|
|
9
|
+
expires_at: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface User {
|
|
13
|
+
id: number;
|
|
14
|
+
centrifugo?: CentrifugoToken;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface CentrifugoProviderProps {
|
|
18
|
+
children: React.ReactNode;
|
|
19
|
+
/** Is Centrifugo enabled */
|
|
20
|
+
enabled?: boolean;
|
|
21
|
+
/** Base RPC client class */
|
|
22
|
+
RPCClientClass: any;
|
|
23
|
+
/** API client wrapper class */
|
|
24
|
+
APIClientClass: any;
|
|
25
|
+
/** Custom WebSocket URL (defaults to user profile URL) */
|
|
26
|
+
url?: string;
|
|
27
|
+
/** Auto-connect on mount (default: true) */
|
|
28
|
+
autoConnect?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface CentrifugoContextValue {
|
|
32
|
+
// Connection State
|
|
33
|
+
client: any;
|
|
34
|
+
baseClient: any;
|
|
35
|
+
isConnected: boolean;
|
|
36
|
+
isConnecting: boolean;
|
|
37
|
+
error: Error | null;
|
|
38
|
+
connectionState: string;
|
|
39
|
+
enabled: boolean;
|
|
40
|
+
|
|
41
|
+
// Connection Methods
|
|
42
|
+
connect: () => Promise<void>;
|
|
43
|
+
disconnect: () => void;
|
|
44
|
+
reconnect: () => Promise<void>;
|
|
45
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"fileNames":["../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.dom.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.scripthost.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.d.ts","../../node_modules/.pnpm/typescript@5.9.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","./src/context/index.ts","../../node_modules/.pnpm/@types+react@19.2.2/node_modules/@types/react/global.d.ts","../../node_modules/.pnpm/csstype@3.1.3/node_modules/csstype/index.d.ts","../../node_modules/.pnpm/@types+react@19.2.2/node_modules/@types/react/index.d.ts","./src/hooks/uselogger.ts","./src/hooks/usesubscription.ts","./src/hooks/index.ts","./src/components/index.ts","./src/types/index.ts","./src/index.ts","../ui/src/components/index.ts","../../node_modules/.pnpm/moment@2.30.1/node_modules/moment/ts3.1-typings/moment.d.ts","../ui/src/hooks/usecountdown.ts","../ui/src/hooks/usedebouncedcallback.ts","../ui/src/hooks/usedebugtools.ts","../ui/src/hooks/useeventsbus.ts","../ui/src/hooks/uselocalstorage.ts","../ui/src/hooks/usesessionstorage.ts","../ui/src/hooks/usetoast.ts","../ui/src/hooks/usecopy.ts","../ui/src/hooks/usetheme.ts","../ui/src/hooks/useimageloader.ts","../ui/src/hooks/index.ts","../ui/src/blocks/index.ts","../ui/src/animations/index.ts","../../node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/clsx.d.ts","../../node_modules/.pnpm/tailwind-merge@3.3.1/node_modules/tailwind-merge/dist/types.d.ts","../ui/src/lib/utils.ts","../ui/src/lib/index.ts","../ui/src/tools/index.ts","../ui/src/theme/index.ts","../ui/src/index.ts","../../node_modules/.pnpm/lucide-react@0.468.0_react@19.1.0/node_modules/lucide-react/dist/lucide-react.d.ts","./src/components/centrifugodebug.tsx","./src/context/centrifugoprovider.tsx","../../node_modules/.pnpm/@types+react-dom@19.2.1_@types+react@19.2.2/node_modules/@types/react-dom/index.d.ts","../../node_modules/.pnpm/@types+estree@1.0.8/node_modules/@types/estree/index.d.ts","../../node_modules/.pnpm/@types+json-schema@7.0.15/node_modules/@types/json-schema/index.d.ts","../../node_modules/.pnpm/@types+eslint@9.6.1/node_modules/@types/eslint/use-at-your-own-risk.d.ts","../../node_modules/.pnpm/@types+eslint@9.6.1/node_modules/@types/eslint/index.d.ts","../../node_modules/.pnpm/@types+eslint-scope@3.7.7/node_modules/@types/eslint-scope/index.d.ts"],"fileIdsList":[[44,47],[44,45,46],[47],[11],[9,10],[8,11,16,39,40],[41],[11,14,16],[42],[12,13],[8,14,15,16],[20,21,22,23,24,25,26,27,28,29],[11,26],[11,19],[18,30,31,32,36,37,38],[35],[33,34]],"fileInfos":[{"version":"a7297ff837fcdf174a9524925966429eb8e5feecc2cc55cc06574e6b092c1eaa","impliedFormat":1},{"version":"c430d44666289dae81f30fa7b2edebf186ecc91a2d4c71266ea6ae76388792e1","affectsGlobalScope":true,"impliedFormat":1},{"version":"080941d9f9ff9307f7e27a83bcd888b7c8270716c39af943532438932ec1d0b9","affectsGlobalScope":true,"impliedFormat":1},{"version":"80e18897e5884b6723488d4f5652167e7bb5024f946743134ecc4aa4ee731f89","affectsGlobalScope":true,"impliedFormat":1},{"version":"cd034f499c6cdca722b60c04b5b1b78e058487a7085a8e0d6fb50809947ee573","affectsGlobalScope":true,"impliedFormat":1},{"version":"8e7f8264d0fb4c5339605a15daadb037bf238c10b654bb3eee14208f860a32ea","affectsGlobalScope":true,"impliedFormat":1},{"version":"782dec38049b92d4e85c1585fbea5474a219c6984a35b004963b00beb1aab538","affectsGlobalScope":true,"impliedFormat":1},"4757b95eb7c31ff9e4acbc3cdc2e9c9077c0904e85c6d8c7b7033b056c6945c7",{"version":"170d4db14678c68178ee8a3d5a990d5afb759ecb6ec44dbd885c50f6da6204f6","affectsGlobalScope":true,"impliedFormat":1},{"version":"8a8eb4ebffd85e589a1cc7c178e291626c359543403d58c9cd22b81fab5b1fb9","impliedFormat":1},{"version":"0ff1b165090b491f5e1407ae680b9a0bc3806dc56827ec85f93c57390491e732","impliedFormat":1},"066a67181338eaae17358afd59a454beecb0dae8e767a25bb37994de2a014f43","af7ede2ef236aaa2083b492eb0b57b833bf813249ee91c94be63bd8880478ee3","f8a94383d6f72b5d288fa4fa09260fcd679a54403aa1527cb67b71e88fa0400b","d165064e8883f2570d35d0a780d80a394b2efa03445c99737b1d09bffcaa354d","8d14cf166c8f6f0a8563f7ea58bb88dfa507bc1374690d72e07b2414e7f0ec72","60794174640b02141441fbbba83cec65397fa462f2dc86fb512f1f3cfbd6e8af","086cceb0b6b332a1e74131104e911ea622aeb4cfb388c295c87d229ecb4a2e6c",{"version":"4051f6311deb0ce6052329eeb1cd4b1b104378fe52f882f483130bea75f92197","impliedFormat":1},"160eb2c958e4f6d73677c19a6f401d301698c392f0e76838f59955579b3a3e06","3745ae4cd217bdb6f775bfe6a8e92582a8b8316d691847df0aaccac491341e61","f9463828bb8ace2ca6d8267c2d7fe1d82fefd619123692d89a0ce6ecad012d37","f5c7c443338e2764761a9e402f98d3f3754b929ccd112e4cf0df58d13e44960f","a79e5251a9910e79d2eee01a3967ee550292fc5dd63e3f7a8eaca4270739c4cd","7b8583231528ebae505db574f6b2310f50bb3e62cf1ba59c7deaa0e33ed040d4","11422f1a01a0d5247d278dce4ca54c1bfb4964922a24fca846a3526e1a9338e6","2bf5d00c4a73796533c3cf504c8dcef8e910a7c57d4ff8a24b858e24e488de8f","cfd9d21f2d2c7d9b124518c8ea653b956aea17f6271315845b28151422b5ceb3","17a15fb9bbc36593d6e1a98270ed1825229020dea4377073273481211e50c242","1421ff768aeba1e18ae6b562cec87de9e00860d1c1df7ccf179ca441f01609a2","8fedb76b4ba3d059c073da3aa2cd4585c694c7f4c96292f604a58b16a1564062","eaf0b63207c6771cbce157645473b11faf74809a722dbad297edf979a06a4e65",{"version":"ef73bcfef9907c8b772a30e5a64a6bd86a5669cba3d210fcdcc6b625e3312459","impliedFormat":1},{"version":"8b15d05f236e8537d3ecbe4422ce46bf0de4e4cd40b2f909c91c5818af4ff17a","impliedFormat":1},"977654e3a808b6e4be8a7bb8965d36ef88fb4e9a7b69aa0aed36c44cf733477b","4af8c78ed17e46cde04b067d8c72a511b0af5d195f6adb2708a82c1f89287336","07dc4a80bdcf4c0b192e155a9b42b249d5ea02f3d50c93dda2f9d7e0c7f80a2d","dfdbbbc3eb8f4ec5d0e5dd0b7da41d71e672fe7a31b51095452ae4801b5f360f","35f22d21ca29d0d2789ccb04a77f8e922b396a06660121882df0ab5127661d72",{"version":"40eb4eb274d7def754b3352c23328af96a69261fe5e99d20df41f1d5de8d0f5e","impliedFormat":1},"c055c6dfdfe0bc857811e0969dabab2ba3f2fee469ba1ed4da93e1d9702176a3","227ce148b7c12185acd0ad544a580e467ae84253a01139e7aa0672f4990a1126",{"version":"be1cc4d94ea60cbe567bc29ed479d42587bf1e6cba490f123d329976b0fe4ee5","impliedFormat":1},{"version":"151ff381ef9ff8da2da9b9663ebf657eac35c4c9a19183420c05728f31a6761d","impliedFormat":1},{"version":"f3d8c757e148ad968f0d98697987db363070abada5f503da3c06aefd9d4248c1","impliedFormat":1},{"version":"a4a39b5714adfcadd3bbea6698ca2e942606d833bde62ad5fb6ec55f5e438ff8","impliedFormat":1},{"version":"bbc1d029093135d7d9bfa4b38cbf8761db505026cc458b5e9c8b74f4000e5e75","impliedFormat":1},{"version":"1f68ab0e055994eb337b67aa87d2a15e0200951e9664959b3866ee6f6b11a0fe","impliedFormat":1}],"root":[8,[12,17],41,42],"options":{"composite":true,"declarationMap":true,"outDir":"./dist","rootDir":"./src"},"referencedMap":[[48,1],[47,2],[46,3],[43,4],[11,5],[40,4],[41,6],[15,7],[42,8],[8,9],[14,10],[12,4],[13,4],[17,11],[30,12],[27,13],[20,14],[21,4],[22,4],[23,4],[29,4],[24,4],[25,4],[28,4],[26,4],[39,15],[36,16],[35,17]],"semanticDiagnosticsPerFile":[[8,[{"start":83,"length":22,"messageText":"Module './CentrifugoProvider' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/centrifugo/src/context/CentrifugoProvider.tsx', but '--jsx' is not set.","category":1,"code":6142}]],[11,[{"start":1482,"length":8,"messageText":"Cannot find name 'Iterable'.","category":1,"code":2304},{"start":14019,"length":9,"messageText":"Type alias 'ReactNode' circularly references itself.","category":1,"code":2456},{"start":14115,"length":8,"messageText":"Cannot find name 'Iterable'.","category":1,"code":2304}]],[12,[{"start":198,"length":9,"messageText":"Cannot find module 'consola' or its corresponding type declarations.","category":1,"code":2307},{"start":816,"length":7,"messageText":"Cannot find name 'process'. Do you need to install type definitions for node? Try `npm i --save-dev @types/node`.","category":1,"code":2580}]],[13,[{"start":549,"length":11,"messageText":"Cannot find module './context' or its corresponding type declarations.","category":1,"code":2307}]],[15,[{"start":78,"length":19,"messageText":"Module './CentrifugoDebug' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/centrifugo/src/components/CentrifugoDebug.tsx', but '--jsx' is not set.","category":1,"code":6142}]],[18,[{"start":55,"length":10,"messageText":"Module './button' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/button.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":98,"length":19,"messageText":"Module './button-download' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/button-download.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":160,"length":19,"messageText":"Module './button-download' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/button-download.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":267,"length":8,"messageText":"Module './card' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/card.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":299,"length":9,"messageText":"Module './badge' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/badge.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":332,"length":9,"messageText":"Module './input' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/input.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":368,"length":12,"messageText":"Module './textarea' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/textarea.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":404,"length":9,"messageText":"Module './label' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/label.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":454,"length":11,"messageText":"Module './section' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/section.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":501,"length":23,"messageText":"Module './image-with-fallback' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/image-with-fallback.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":571,"length":12,"messageText":"Module './checkbox' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/checkbox.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":628,"length":15,"messageText":"Module './radio-group' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/radio-group.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":723,"length":10,"messageText":"Module './select' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/select.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":760,"length":12,"messageText":"Module './combobox' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/combobox.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":825,"length":12,"messageText":"Module './combobox' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/combobox.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":862,"length":10,"messageText":"Module './switch' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/switch.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":897,"length":10,"messageText":"Module './slider' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/slider.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":963,"length":13,"messageText":"Module './input-otp' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/input-otp.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":1026,"length":13,"messageText":"Module './separator' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/separator.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":1066,"length":12,"messageText":"Module './skeleton' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/skeleton.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":1108,"length":16,"messageText":"Module './aspect-ratio' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/aspect-ratio.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":1164,"length":15,"messageText":"Module './scroll-area' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/scroll-area.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":1250,"length":13,"messageText":"Module './resizable' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/resizable.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":1288,"length":10,"messageText":"Module './sticky' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/sticky.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":1515,"length":19,"messageText":"Module './navigation-menu' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/navigation-menu.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":1668,"length":14,"messageText":"Module './breadcrumb' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/breadcrumb.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":1721,"length":25,"messageText":"Module './breadcrumb-navigation' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/breadcrumb-navigation.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":1839,"length":25,"messageText":"Module './breadcrumb-navigation' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/breadcrumb-navigation.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":1983,"length":11,"messageText":"Module './menubar' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/menubar.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":2145,"length":10,"messageText":"Module './dialog' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/dialog.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":2343,"length":16,"messageText":"Module './alert-dialog' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/alert-dialog.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":2417,"length":11,"messageText":"Module './popover' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/popover.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":2548,"length":9,"messageText":"Module './sheet' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/sheet.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":2685,"length":10,"messageText":"Module './drawer' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/drawer.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":2759,"length":14,"messageText":"Module './hover-card' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/hover-card.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":2848,"length":11,"messageText":"Module './tooltip' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/tooltip.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":2981,"length":9,"messageText":"Module './table' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/table.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3049,"length":8,"messageText":"Module './tabs' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/tabs.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3136,"length":13,"messageText":"Module './accordion' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/accordion.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3219,"length":15,"messageText":"Module './collapsible' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/collapsible.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3261,"length":12,"messageText":"Module './progress' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/progress.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3327,"length":10,"messageText":"Module './avatar' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/avatar.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3364,"length":12,"messageText":"Module './calendar' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/calendar.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3466,"length":12,"messageText":"Module './carousel' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/carousel.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3614,"length":14,"messageText":"Module './pagination' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/pagination.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3660,"length":18,"messageText":"Module './ssr-pagination' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/ssr-pagination.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3753,"length":21,"messageText":"Module './pagination-static' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/pagination-static.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3818,"length":21,"messageText":"Module './pagination-static' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/pagination-static.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3867,"length":14,"messageText":"Module './token-icon' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/token-icon.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":3919,"length":14,"messageText":"Module './token-icon' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/token-icon.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":4067,"length":9,"messageText":"Module './chart' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/chart.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":4128,"length":10,"messageText":"Module './toggle' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/toggle.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":4185,"length":16,"messageText":"Module './toggle-group' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/toggle-group.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":4345,"length":11,"messageText":"Module './command' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/command.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":4636,"length":16,"messageText":"Module './context-menu' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/context-menu.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":4964,"length":17,"messageText":"Module './dropdown-menu' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/dropdown-menu.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":5059,"length":9,"messageText":"Module './alert' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/alert.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":5177,"length":9,"messageText":"Module './toast' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/toast.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":5212,"length":11,"messageText":"Module './toaster' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/toaster.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":5355,"length":8,"messageText":"Module './form' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/form.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":5393,"length":16,"messageText":"Module './button-group' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/button-group.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":5502,"length":9,"messageText":"Module './empty' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/empty.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":5537,"length":11,"messageText":"Module './spinner' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/spinner.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":5580,"length":7,"messageText":"Module './kbd' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/kbd.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":5616,"length":15,"messageText":"Module './input-group' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/input-group.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":5654,"length":8,"messageText":"Module './item' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/item.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":5686,"length":9,"messageText":"Module './field' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/field.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":6182,"length":11,"messageText":"Module './sidebar' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/sidebar.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":6223,"length":15,"messageText":"Module './phone-input' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/phone-input.tsx', but '--jsx' is not set.","category":1,"code":6142}]],[20,[{"start":7,"length":6,"messageText":"Module '\"/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/node_modules/.pnpm/moment@2.30.1/node_modules/moment/ts3.1-typings/moment\"' can only be default-imported using the 'esModuleInterop' flag","category":1,"code":1259,"relatedInformation":[{"file":"../../node_modules/.pnpm/moment@2.30.1/node_modules/moment/ts3.1-typings/moment.d.ts","start":23282,"length":16,"messageText":"This module is declared with 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.","category":1,"code":2594}]}]],[21,[{"start":481,"length":6,"messageText":"Cannot find namespace 'NodeJS'.","category":1,"code":2503}]],[22,[{"start":727,"length":7,"code":2550,"category":1,"messageText":"Property 'entries' does not exist on type 'ObjectConstructor'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2017' or later."}]],[23,[{"start":267,"length":3,"messageText":"Cannot find name 'Set'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later.","category":1,"code":2583},{"start":297,"length":3,"messageText":"Cannot find name 'Set'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later.","category":1,"code":2583}]],[26,[{"start":143,"length":21,"messageText":"Module '../components/toast' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/components/toast.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":586,"length":16,"code":2339,"category":1,"messageText":"Property 'MAX_SAFE_INTEGER' does not exist on type 'NumberConstructor'."},{"start":1090,"length":3,"messageText":"Cannot find name 'Map'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later.","category":1,"code":2583}]],[30,[{"start":492,"length":13,"messageText":"Module './useMobile' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/hooks/useMobile.tsx', but '--jsx' is not set.","category":1,"code":6142}]],[31,[{"start":14,"length":14,"messageText":"Module './CTASection' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/blocks/CTASection.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":44,"length":18,"messageText":"Module './FeatureSection' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/blocks/FeatureSection.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":78,"length":8,"messageText":"Module './Hero' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/blocks/Hero.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":102,"length":21,"messageText":"Module './NewsletterSection' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/blocks/NewsletterSection.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":139,"length":16,"messageText":"Module './StatsSection' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/blocks/StatsSection.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":171,"length":13,"messageText":"Module './SuperHero' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/blocks/SuperHero.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":200,"length":22,"messageText":"Module './TestimonialSection' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/blocks/TestimonialSection.tsx', but '--jsx' is not set.","category":1,"code":6142}]],[32,[{"start":35,"length":22,"messageText":"Module './AnimatedBackground' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/animations/AnimatedBackground.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":98,"length":22,"messageText":"Module './AnimatedBackground' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/animations/AnimatedBackground.tsx', but '--jsx' is not set.","category":1,"code":6142}]],[37,[{"start":367,"length":12,"messageText":"Module './JsonTree' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/tools/JsonTree/index.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":417,"length":12,"messageText":"Module './JsonTree' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/tools/JsonTree/index.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":466,"length":11,"messageText":"Module './Mermaid' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/tools/Mermaid/index.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":517,"length":14,"messageText":"Module './PrettyCode' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/tools/PrettyCode/index.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":563,"length":14,"messageText":"Module './PrettyCode' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/tools/PrettyCode/index.tsx', but '--jsx' is not set.","category":1,"code":6142}]],[38,[{"start":47,"length":17,"messageText":"Module './ThemeProvider' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/theme/ThemeProvider.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":94,"length":15,"messageText":"Module './ThemeToggle' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/theme/ThemeToggle.tsx', but '--jsx' is not set.","category":1,"code":6142},{"start":138,"length":14,"messageText":"Module './ForceTheme' was resolved to '/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/packages/ui/src/theme/ForceTheme.tsx', but '--jsx' is not set.","category":1,"code":6142}]],[40,[{"start":41,"length":8,"messageText":"Module '\"../../../../../../packages/centrifugo/node_modules/@types/react\"' has no exported member 'ReactSVG'.","category":1,"code":2305}]],[41,[{"start":130,"length":5,"messageText":"Module '\"/Users/markinmatrix/Documents/htdocs/@CARAPIS/encar_parser_new/@projects/django-cfg/projects/django-cfg-dev/src/django_admin/node_modules/.pnpm/@types+react@19.2.2/node_modules/@types/react/index\"' can only be default-imported using the 'esModuleInterop' flag","category":1,"code":1259,"relatedInformation":[{"file":"../../node_modules/.pnpm/@types+react@19.2.2/node_modules/@types/react/index.d.ts","start":2169,"length":15,"messageText":"This module is declared with 'export =', and can only be used with a default import when using the 'esModuleInterop' flag.","category":1,"code":2594}]},{"start":1110,"length":72,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":1189,"length":28,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":1226,"length":29,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":1266,"length":51,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":1330,"length":41,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":1419,"length":77,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":1533,"length":78,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":1643,"length":33,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":1736,"length":161,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":1912,"length":47,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":2030,"length":43,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":2120,"length":51,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":2184,"length":40,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":2251,"length":57,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":2420,"length":51,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":2484,"length":40,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":2558,"length":57,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":2743,"length":51,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":2807,"length":40,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":2880,"length":55,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":3028,"length":30,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":3095,"length":51,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":3159,"length":40,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":3226,"length":52,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":3408,"length":51,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":3472,"length":40,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":3551,"length":59,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":3744,"length":51,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":3808,"length":40,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":3878,"length":60,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":4120,"length":27,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":4162,"length":40,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":4231,"length":59,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":4514,"length":27,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":4556,"length":41,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":4614,"length":85,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":4716,"length":40,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":4844,"length":49,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":5095,"length":27,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":5137,"length":40,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":5211,"length":49,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":5453,"length":27,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":5495,"length":61,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":5584,"length":100,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":5781,"length":30,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":5863,"length":73,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":5949,"length":62,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":6080,"length":50,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":6180,"length":50,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":6327,"length":29,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":6504,"length":68,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":6610,"length":5,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":6674,"length":5,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":6740,"length":5,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004},{"start":6817,"length":5,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004}]],[42,[{"file":false,"messageText":"Cannot find global value 'Promise'.","category":1,"code":2468},{"start":1100,"length":6,"messageText":"Cannot find namespace 'NodeJS'.","category":1,"code":2503},{"start":2568,"length":13,"messageText":"An async function or method in ES5 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option.","category":1,"code":2705},{"start":4214,"length":8,"code":2550,"category":1,"messageText":"Property 'includes' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later."},{"start":4275,"length":8,"code":2550,"category":1,"messageText":"Property 'includes' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later."},{"start":4335,"length":8,"code":2550,"category":1,"messageText":"Property 'includes' does not exist on type 'string'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later."},{"start":5425,"length":13,"messageText":"An async function or method in ES5 requires the 'Promise' constructor. Make sure you have a declaration for the 'Promise' constructor or include 'ES2015' in your '--lib' option.","category":1,"code":2705},{"start":6260,"length":42,"messageText":"Cannot use JSX unless the '--jsx' flag is provided.","category":1,"code":17004}]],[46,[{"start":90,"length":3,"messageText":"Cannot find name 'Map'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later.","category":1,"code":2583},{"start":571,"length":16,"messageText":"Cannot find name 'IterableIterator'.","category":1,"code":2304}]],[47,[{"start":1567,"length":3,"messageText":"Cannot find name 'Map'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later.","category":1,"code":2583},{"start":36676,"length":16,"messageText":"Cannot find name 'IterableIterator'.","category":1,"code":2304},{"start":39333,"length":3,"messageText":"Cannot find name 'Map'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later.","category":1,"code":2583}]],[48,[{"start":755,"length":3,"messageText":"Cannot find name 'Map'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2015' or later.","category":1,"code":2583}]]],"affectedFilesPendingEmit":[41,15,42,8,14,12,13,17,16],"emitSignatures":[8,12,13,14,15,16,17,41,42],"version":"5.9.3"}
|