create-fluxstack 1.5.5 → 1.7.3
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 +172 -215
- package/app/client/src/App.tsx +45 -19
- package/app/client/src/components/FluxStackConfig.tsx +1 -1
- package/app/client/src/components/HybridLiveCounter.tsx +1 -1
- package/app/client/src/components/LiveClock.tsx +1 -1
- package/app/client/src/components/MainLayout.tsx +0 -2
- package/app/client/src/components/SidebarNavigation.tsx +1 -1
- package/app/client/src/components/SystemMonitor.tsx +16 -10
- package/app/client/src/components/UserProfile.tsx +1 -1
- package/app/server/live/FluxStackConfig.ts +2 -1
- package/app/server/live/LiveClockComponent.ts +8 -7
- package/app/server/live/SidebarNavigation.ts +2 -1
- package/app/server/live/SystemMonitor.ts +1 -0
- package/app/server/live/UserProfileComponent.ts +36 -30
- package/config/server.config.ts +1 -0
- package/core/build/index.ts +14 -0
- package/core/cli/command-registry.ts +10 -10
- package/core/cli/commands/plugin-deps.ts +13 -5
- package/core/cli/plugin-discovery.ts +1 -1
- package/core/client/LiveComponentsProvider.tsx +414 -0
- package/core/client/hooks/useHybridLiveComponent.ts +194 -530
- package/core/client/index.ts +16 -0
- package/core/framework/server.ts +144 -63
- package/core/index.ts +4 -1
- package/core/plugins/built-in/monitoring/index.ts +1 -1
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +1 -1
- package/core/plugins/built-in/vite/index.ts +1 -1
- package/core/plugins/config.ts +1 -1
- package/core/plugins/discovery.ts +1 -1
- package/core/plugins/executor.ts +1 -1
- package/core/plugins/index.ts +1 -0
- package/core/server/live/ComponentRegistry.ts +3 -1
- package/core/server/live/WebSocketConnectionManager.ts +14 -4
- package/core/server/live/websocket-plugin.ts +453 -434
- package/core/server/middleware/elysia-helpers.ts +3 -5
- package/core/server/plugins/database.ts +1 -1
- package/core/server/plugins/static-files-plugin.ts +1 -1
- package/core/templates/create-project.ts +1 -0
- package/core/types/index.ts +1 -1
- package/core/types/plugin.ts +1 -1
- package/core/types/types.ts +6 -2
- package/core/utils/logger/colors.ts +4 -4
- package/core/utils/logger/index.ts +37 -4
- package/core/utils/logger/winston-logger.ts +1 -1
- package/core/utils/sync-version.ts +67 -0
- package/core/utils/version.ts +6 -5
- package/package.json +3 -2
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +1 -1
- package/vite.config.ts +8 -0
- package/.dockerignore +0 -50
- package/CRYPTO-AUTH-MIDDLEWARE-GUIDE.md +0 -475
- package/CRYPTO-AUTH-MIDDLEWARES.md +0 -473
- package/CRYPTO-AUTH-USAGE.md +0 -491
- package/EXEMPLO-ROTA-PROTEGIDA.md +0 -347
- package/QUICK-START-CRYPTO-AUTH.md +0 -221
- package/app/client/src/components/Teste.tsx +0 -104
- package/app/server/live/TesteComponent.ts +0 -87
- package/test-crypto-auth.ts +0 -101
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
// 🔥 Live Components Provider - Singleton WebSocket Connection
|
|
2
|
+
// Single WebSocket connection shared by all live components in the app
|
|
3
|
+
|
|
4
|
+
import React, { createContext, useContext, useEffect, useRef, useState, useCallback } from 'react'
|
|
5
|
+
import type { WebSocketMessage, WebSocketResponse } from '../types/types'
|
|
6
|
+
|
|
7
|
+
export interface LiveComponentsContextValue {
|
|
8
|
+
connected: boolean
|
|
9
|
+
connecting: boolean
|
|
10
|
+
error: string | null
|
|
11
|
+
connectionId: string | null
|
|
12
|
+
|
|
13
|
+
// Send message without waiting for response
|
|
14
|
+
sendMessage: (message: WebSocketMessage) => Promise<void>
|
|
15
|
+
|
|
16
|
+
// Send message and wait for specific response
|
|
17
|
+
sendMessageAndWait: (message: WebSocketMessage, timeout?: number) => Promise<WebSocketResponse>
|
|
18
|
+
|
|
19
|
+
// Register message listener for a component
|
|
20
|
+
registerComponent: (componentId: string, callback: (message: WebSocketResponse) => void) => () => void
|
|
21
|
+
|
|
22
|
+
// Unregister component
|
|
23
|
+
unregisterComponent: (componentId: string) => void
|
|
24
|
+
|
|
25
|
+
// Manual reconnect
|
|
26
|
+
reconnect: () => void
|
|
27
|
+
|
|
28
|
+
// Get current WebSocket instance (for advanced use)
|
|
29
|
+
getWebSocket: () => WebSocket | null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const LiveComponentsContext = createContext<LiveComponentsContextValue | null>(null)
|
|
33
|
+
|
|
34
|
+
export interface LiveComponentsProviderProps {
|
|
35
|
+
children: React.ReactNode
|
|
36
|
+
url?: string
|
|
37
|
+
autoConnect?: boolean
|
|
38
|
+
reconnectInterval?: number
|
|
39
|
+
maxReconnectAttempts?: number
|
|
40
|
+
heartbeatInterval?: number
|
|
41
|
+
debug?: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function LiveComponentsProvider({
|
|
45
|
+
children,
|
|
46
|
+
url,
|
|
47
|
+
autoConnect = true,
|
|
48
|
+
reconnectInterval = 1000,
|
|
49
|
+
maxReconnectAttempts = 5,
|
|
50
|
+
heartbeatInterval = 30000,
|
|
51
|
+
debug = false
|
|
52
|
+
}: WebSocketProviderProps) {
|
|
53
|
+
|
|
54
|
+
// Get WebSocket URL dynamically
|
|
55
|
+
const getWebSocketUrl = () => {
|
|
56
|
+
if (url) return url
|
|
57
|
+
if (typeof window === 'undefined') return 'ws://localhost:3000/api/live/ws'
|
|
58
|
+
|
|
59
|
+
const hostname = window.location.hostname
|
|
60
|
+
const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1'
|
|
61
|
+
|
|
62
|
+
if (!isLocalhost) {
|
|
63
|
+
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
64
|
+
return `${protocol}//${window.location.host}/api/live/ws`
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return 'ws://localhost:3000/api/live/ws'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const wsUrl = getWebSocketUrl()
|
|
71
|
+
|
|
72
|
+
// State
|
|
73
|
+
const [connected, setConnected] = useState(false)
|
|
74
|
+
const [connecting, setConnecting] = useState(false)
|
|
75
|
+
const [error, setError] = useState<string | null>(null)
|
|
76
|
+
const [connectionId, setConnectionId] = useState<string | null>(null)
|
|
77
|
+
|
|
78
|
+
// Refs
|
|
79
|
+
const wsRef = useRef<WebSocket | null>(null)
|
|
80
|
+
const reconnectAttemptsRef = useRef(0)
|
|
81
|
+
const reconnectTimeoutRef = useRef<number | null>(null)
|
|
82
|
+
const heartbeatIntervalRef = useRef<number | null>(null)
|
|
83
|
+
|
|
84
|
+
// Component callbacks registry: componentId -> callback
|
|
85
|
+
const componentCallbacksRef = useRef<Map<string, (message: WebSocketResponse) => void>>(new Map())
|
|
86
|
+
|
|
87
|
+
// Pending requests: requestId -> { resolve, reject, timeout }
|
|
88
|
+
const pendingRequestsRef = useRef<Map<string, {
|
|
89
|
+
resolve: (value: any) => void
|
|
90
|
+
reject: (error: any) => void
|
|
91
|
+
timeout: NodeJS.Timeout
|
|
92
|
+
}>>(new Map())
|
|
93
|
+
|
|
94
|
+
const log = useCallback((message: string, data?: any) => {
|
|
95
|
+
if (debug) {
|
|
96
|
+
console.log(`[WebSocketProvider] ${message}`, data || '')
|
|
97
|
+
}
|
|
98
|
+
}, [debug])
|
|
99
|
+
|
|
100
|
+
// Generate unique request ID
|
|
101
|
+
const generateRequestId = useCallback(() => {
|
|
102
|
+
return `req-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
|
|
103
|
+
}, [])
|
|
104
|
+
|
|
105
|
+
// Connect to WebSocket
|
|
106
|
+
const connect = useCallback(() => {
|
|
107
|
+
if (wsRef.current?.readyState === WebSocket.CONNECTING) {
|
|
108
|
+
log('Already connecting, skipping...')
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
113
|
+
log('Already connected, skipping...')
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
setConnecting(true)
|
|
117
|
+
setError(null)
|
|
118
|
+
log('🔌 Connecting to WebSocket...', { url: wsUrl })
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
const ws = new WebSocket(wsUrl)
|
|
122
|
+
wsRef.current = ws
|
|
123
|
+
|
|
124
|
+
ws.onopen = () => {
|
|
125
|
+
log('✅ WebSocket connected')
|
|
126
|
+
setConnected(true)
|
|
127
|
+
setConnecting(false)
|
|
128
|
+
reconnectAttemptsRef.current = 0
|
|
129
|
+
|
|
130
|
+
// Start heartbeat
|
|
131
|
+
startHeartbeat()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
ws.onmessage = (event) => {
|
|
135
|
+
try {
|
|
136
|
+
const response: WebSocketResponse = JSON.parse(event.data)
|
|
137
|
+
log('📨 Received message', { type: response.type, componentId: response.componentId })
|
|
138
|
+
|
|
139
|
+
// Handle connection established
|
|
140
|
+
if (response.type === 'CONNECTION_ESTABLISHED') {
|
|
141
|
+
setConnectionId(response.connectionId || null)
|
|
142
|
+
log('🔗 Connection ID:', response.connectionId)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Handle pending requests (request-response pattern)
|
|
146
|
+
if (response.requestId && pendingRequestsRef.current.has(response.requestId)) {
|
|
147
|
+
const request = pendingRequestsRef.current.get(response.requestId)!
|
|
148
|
+
clearTimeout(request.timeout)
|
|
149
|
+
pendingRequestsRef.current.delete(response.requestId)
|
|
150
|
+
|
|
151
|
+
if (response.success !== false) {
|
|
152
|
+
request.resolve(response)
|
|
153
|
+
} else {
|
|
154
|
+
// Don't reject re-hydration errors - let component handle them
|
|
155
|
+
if (response.error?.includes?.('COMPONENT_REHYDRATION_REQUIRED')) {
|
|
156
|
+
request.resolve(response)
|
|
157
|
+
} else {
|
|
158
|
+
request.reject(new Error(response.error || 'Request failed'))
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Route message to specific component
|
|
165
|
+
if (response.componentId) {
|
|
166
|
+
const callback = componentCallbacksRef.current.get(response.componentId)
|
|
167
|
+
if (callback) {
|
|
168
|
+
callback(response)
|
|
169
|
+
} else {
|
|
170
|
+
log('⚠️ No callback registered for component:', response.componentId)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Broadcast messages (no specific componentId)
|
|
175
|
+
if (response.type === 'BROADCAST' && !response.componentId) {
|
|
176
|
+
// Send to all registered components
|
|
177
|
+
componentCallbacksRef.current.forEach(callback => {
|
|
178
|
+
callback(response)
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
} catch (error) {
|
|
183
|
+
log('❌ Failed to parse message', error)
|
|
184
|
+
setError('Failed to parse message')
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
ws.onclose = () => {
|
|
189
|
+
log('🔌 WebSocket closed')
|
|
190
|
+
setConnected(false)
|
|
191
|
+
setConnecting(false)
|
|
192
|
+
setConnectionId(null)
|
|
193
|
+
|
|
194
|
+
// Stop heartbeat
|
|
195
|
+
stopHeartbeat()
|
|
196
|
+
|
|
197
|
+
// Auto-reconnect
|
|
198
|
+
if (reconnectAttemptsRef.current < maxReconnectAttempts) {
|
|
199
|
+
reconnectAttemptsRef.current++
|
|
200
|
+
log(`🔄 Reconnecting... (${reconnectAttemptsRef.current}/${maxReconnectAttempts})`)
|
|
201
|
+
|
|
202
|
+
reconnectTimeoutRef.current = window.setTimeout(() => {
|
|
203
|
+
connect()
|
|
204
|
+
}, reconnectInterval)
|
|
205
|
+
} else {
|
|
206
|
+
setError('Max reconnection attempts reached')
|
|
207
|
+
log('❌ Max reconnection attempts reached')
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
ws.onerror = (event) => {
|
|
212
|
+
log('❌ WebSocket error', event)
|
|
213
|
+
setError('WebSocket connection error')
|
|
214
|
+
setConnecting(false)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
} catch (error) {
|
|
218
|
+
setConnecting(false)
|
|
219
|
+
setError(error instanceof Error ? error.message : 'Connection failed')
|
|
220
|
+
log('❌ Failed to create WebSocket', error)
|
|
221
|
+
}
|
|
222
|
+
}, [wsUrl, reconnectInterval, maxReconnectAttempts, log])
|
|
223
|
+
|
|
224
|
+
// Disconnect
|
|
225
|
+
const disconnect = useCallback(() => {
|
|
226
|
+
if (reconnectTimeoutRef.current) {
|
|
227
|
+
clearTimeout(reconnectTimeoutRef.current)
|
|
228
|
+
reconnectTimeoutRef.current = null
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
stopHeartbeat()
|
|
232
|
+
|
|
233
|
+
if (wsRef.current) {
|
|
234
|
+
wsRef.current.close()
|
|
235
|
+
wsRef.current = null
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
reconnectAttemptsRef.current = maxReconnectAttempts // Prevent auto-reconnect
|
|
239
|
+
setConnected(false)
|
|
240
|
+
setConnecting(false)
|
|
241
|
+
setConnectionId(null)
|
|
242
|
+
log('🔌 WebSocket disconnected manually')
|
|
243
|
+
}, [maxReconnectAttempts, log])
|
|
244
|
+
|
|
245
|
+
// Manual reconnect
|
|
246
|
+
const reconnect = useCallback(() => {
|
|
247
|
+
disconnect()
|
|
248
|
+
reconnectAttemptsRef.current = 0
|
|
249
|
+
setTimeout(() => connect(), 100)
|
|
250
|
+
}, [connect, disconnect])
|
|
251
|
+
|
|
252
|
+
// Start heartbeat (ping components periodically)
|
|
253
|
+
const startHeartbeat = useCallback(() => {
|
|
254
|
+
stopHeartbeat()
|
|
255
|
+
|
|
256
|
+
heartbeatIntervalRef.current = window.setInterval(() => {
|
|
257
|
+
if (wsRef.current?.readyState === WebSocket.OPEN) {
|
|
258
|
+
// Send ping to all registered components
|
|
259
|
+
componentCallbacksRef.current.forEach((_, componentId) => {
|
|
260
|
+
sendMessage({
|
|
261
|
+
type: 'COMPONENT_PING',
|
|
262
|
+
componentId,
|
|
263
|
+
timestamp: Date.now()
|
|
264
|
+
}).catch(err => {
|
|
265
|
+
log('❌ Heartbeat ping failed for component:', componentId)
|
|
266
|
+
})
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
}, heartbeatInterval)
|
|
270
|
+
}, [heartbeatInterval, log])
|
|
271
|
+
|
|
272
|
+
// Stop heartbeat
|
|
273
|
+
const stopHeartbeat = useCallback(() => {
|
|
274
|
+
if (heartbeatIntervalRef.current) {
|
|
275
|
+
clearInterval(heartbeatIntervalRef.current)
|
|
276
|
+
heartbeatIntervalRef.current = null
|
|
277
|
+
}
|
|
278
|
+
}, [])
|
|
279
|
+
|
|
280
|
+
// Send message without waiting for response
|
|
281
|
+
const sendMessage = useCallback(async (message: WebSocketMessage): Promise<void> => {
|
|
282
|
+
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
|
|
283
|
+
throw new Error('WebSocket is not connected')
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
try {
|
|
287
|
+
const messageWithTimestamp = { ...message, timestamp: Date.now() }
|
|
288
|
+
wsRef.current.send(JSON.stringify(messageWithTimestamp))
|
|
289
|
+
log('📤 Sent message', { type: message.type, componentId: message.componentId })
|
|
290
|
+
} catch (error) {
|
|
291
|
+
log('❌ Failed to send message', error)
|
|
292
|
+
throw error
|
|
293
|
+
}
|
|
294
|
+
}, [log])
|
|
295
|
+
|
|
296
|
+
// Send message and wait for response
|
|
297
|
+
const sendMessageAndWait = useCallback(async (
|
|
298
|
+
message: WebSocketMessage,
|
|
299
|
+
timeout: number = 10000
|
|
300
|
+
): Promise<WebSocketResponse> => {
|
|
301
|
+
return new Promise((resolve, reject) => {
|
|
302
|
+
if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) {
|
|
303
|
+
reject(new Error('WebSocket is not connected'))
|
|
304
|
+
return
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const requestId = generateRequestId()
|
|
308
|
+
|
|
309
|
+
// Set up timeout
|
|
310
|
+
const timeoutHandle = setTimeout(() => {
|
|
311
|
+
pendingRequestsRef.current.delete(requestId)
|
|
312
|
+
reject(new Error(`Request timeout after ${timeout}ms`))
|
|
313
|
+
}, timeout)
|
|
314
|
+
|
|
315
|
+
// Store pending request
|
|
316
|
+
pendingRequestsRef.current.set(requestId, {
|
|
317
|
+
resolve,
|
|
318
|
+
reject,
|
|
319
|
+
timeout: timeoutHandle
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
try {
|
|
323
|
+
const messageWithRequestId = {
|
|
324
|
+
...message,
|
|
325
|
+
requestId,
|
|
326
|
+
expectResponse: true,
|
|
327
|
+
timestamp: Date.now()
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
wsRef.current.send(JSON.stringify(messageWithRequestId))
|
|
331
|
+
log('📤 Sent message with request ID', { requestId, type: message.type })
|
|
332
|
+
} catch (error) {
|
|
333
|
+
clearTimeout(timeoutHandle)
|
|
334
|
+
pendingRequestsRef.current.delete(requestId)
|
|
335
|
+
reject(error)
|
|
336
|
+
}
|
|
337
|
+
})
|
|
338
|
+
}, [log, generateRequestId])
|
|
339
|
+
|
|
340
|
+
// Register component callback
|
|
341
|
+
const registerComponent = useCallback((
|
|
342
|
+
componentId: string,
|
|
343
|
+
callback: (message: WebSocketResponse) => void
|
|
344
|
+
): (() => void) => {
|
|
345
|
+
log('📝 Registering component', componentId)
|
|
346
|
+
componentCallbacksRef.current.set(componentId, callback)
|
|
347
|
+
|
|
348
|
+
// Return unregister function
|
|
349
|
+
return () => {
|
|
350
|
+
log('🗑️ Unregistering component', componentId)
|
|
351
|
+
componentCallbacksRef.current.delete(componentId)
|
|
352
|
+
}
|
|
353
|
+
}, [log])
|
|
354
|
+
|
|
355
|
+
// Unregister component
|
|
356
|
+
const unregisterComponent = useCallback((componentId: string) => {
|
|
357
|
+
componentCallbacksRef.current.delete(componentId)
|
|
358
|
+
log('🗑️ Component unregistered', componentId)
|
|
359
|
+
}, [log])
|
|
360
|
+
|
|
361
|
+
// Get WebSocket instance
|
|
362
|
+
const getWebSocket = useCallback(() => {
|
|
363
|
+
return wsRef.current
|
|
364
|
+
}, [])
|
|
365
|
+
|
|
366
|
+
// Auto-connect on mount
|
|
367
|
+
useEffect(() => {
|
|
368
|
+
if (autoConnect) {
|
|
369
|
+
connect()
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return () => {
|
|
373
|
+
disconnect()
|
|
374
|
+
}
|
|
375
|
+
}, [autoConnect, connect, disconnect])
|
|
376
|
+
|
|
377
|
+
const value: LiveComponentsContextValue = {
|
|
378
|
+
connected,
|
|
379
|
+
connecting,
|
|
380
|
+
error,
|
|
381
|
+
connectionId,
|
|
382
|
+
sendMessage,
|
|
383
|
+
sendMessageAndWait,
|
|
384
|
+
registerComponent,
|
|
385
|
+
unregisterComponent,
|
|
386
|
+
reconnect,
|
|
387
|
+
getWebSocket
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
<LiveComponentsContext.Provider value={value}>
|
|
392
|
+
{children}
|
|
393
|
+
</LiveComponentsContext.Provider>
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Hook to use Live Components context
|
|
398
|
+
export function useLiveComponents(): LiveComponentsContextValue {
|
|
399
|
+
const context = useContext(LiveComponentsContext)
|
|
400
|
+
if (!context) {
|
|
401
|
+
throw new Error('useLiveComponents must be used within LiveComponentsProvider')
|
|
402
|
+
}
|
|
403
|
+
return context
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// ⚠️ DEPRECATED: Use useLiveComponents instead
|
|
407
|
+
// Kept for backward compatibility
|
|
408
|
+
export const useWebSocketContext = useLiveComponents
|
|
409
|
+
|
|
410
|
+
// ⚠️ DEPRECATED: Use LiveComponentsProvider instead
|
|
411
|
+
// Kept for backward compatibility
|
|
412
|
+
export const WebSocketProvider = LiveComponentsProvider
|
|
413
|
+
export type WebSocketProviderProps = LiveComponentsProviderProps
|
|
414
|
+
export type WebSocketContextValue = LiveComponentsContextValue
|