create-fluxstack 1.14.0 → 1.16.0
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/LLMD/INDEX.md +4 -3
- package/LLMD/resources/live-binary-delta.md +507 -0
- package/LLMD/resources/live-components.md +208 -12
- package/LLMD/resources/live-rooms.md +731 -333
- package/app/client/.live-stubs/LiveAdminPanel.js +5 -0
- package/app/client/.live-stubs/LiveCounter.js +9 -0
- package/app/client/.live-stubs/LiveForm.js +11 -0
- package/app/client/.live-stubs/LiveLocalCounter.js +8 -0
- package/app/client/.live-stubs/LivePingPong.js +10 -0
- package/app/client/.live-stubs/LiveRoomChat.js +11 -0
- package/app/client/.live-stubs/LiveSharedCounter.js +10 -0
- package/app/client/.live-stubs/LiveUpload.js +15 -0
- package/app/client/src/App.tsx +19 -7
- package/app/client/src/components/AppLayout.tsx +18 -10
- package/app/client/src/live/PingPongDemo.tsx +199 -0
- package/app/client/src/live/RoomChatDemo.tsx +187 -22
- package/app/client/src/live/SharedCounterDemo.tsx +142 -0
- package/app/server/auth/DevAuthProvider.ts +2 -2
- package/app/server/auth/JWTAuthProvider.example.ts +2 -2
- package/app/server/index.ts +2 -2
- package/app/server/live/LiveAdminPanel.ts +1 -1
- package/app/server/live/LivePingPong.ts +61 -0
- package/app/server/live/LiveProtectedChat.ts +1 -1
- package/app/server/live/LiveRoomChat.ts +106 -38
- package/app/server/live/LiveSharedCounter.ts +73 -0
- package/app/server/live/rooms/ChatRoom.ts +68 -0
- package/app/server/live/rooms/CounterRoom.ts +51 -0
- package/app/server/live/rooms/DirectoryRoom.ts +42 -0
- package/app/server/live/rooms/PingRoom.ts +40 -0
- package/app/server/routes/room.routes.ts +1 -2
- package/core/build/live-components-generator.ts +11 -2
- package/core/build/vite-plugins.ts +28 -0
- package/core/client/hooks/useLiveUpload.ts +3 -4
- package/core/client/index.ts +25 -35
- package/core/framework/server.ts +1 -1
- package/core/server/index.ts +1 -2
- package/core/server/live/auto-generated-components.ts +5 -8
- package/core/server/live/index.ts +90 -21
- package/core/server/live/websocket-plugin.ts +54 -1079
- package/core/types/types.ts +76 -1025
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +1 -1
- package/package.json +100 -95
- package/plugins/crypto-auth/index.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +2 -2
- package/tsconfig.json +4 -1
- package/vite.config.ts +40 -12
- package/app/client/src/live/ChatDemo.tsx +0 -107
- package/app/client/src/live/LiveDebuggerPanel.tsx +0 -779
- package/app/server/live/LiveChat.ts +0 -78
- package/core/client/LiveComponentsProvider.tsx +0 -531
- package/core/client/components/Live.tsx +0 -111
- package/core/client/components/LiveDebugger.tsx +0 -1324
- package/core/client/hooks/AdaptiveChunkSizer.ts +0 -215
- package/core/client/hooks/state-validator.ts +0 -130
- package/core/client/hooks/useChunkedUpload.ts +0 -359
- package/core/client/hooks/useLiveChunkedUpload.ts +0 -87
- package/core/client/hooks/useLiveComponent.ts +0 -853
- package/core/client/hooks/useLiveDebugger.ts +0 -392
- package/core/client/hooks/useRoom.ts +0 -409
- package/core/client/hooks/useRoomProxy.ts +0 -382
- package/core/server/live/ComponentRegistry.ts +0 -1128
- package/core/server/live/FileUploadManager.ts +0 -446
- package/core/server/live/LiveComponentPerformanceMonitor.ts +0 -931
- package/core/server/live/LiveDebugger.ts +0 -462
- package/core/server/live/LiveLogger.ts +0 -144
- package/core/server/live/LiveRoomManager.ts +0 -278
- package/core/server/live/RoomEventBus.ts +0 -234
- package/core/server/live/RoomStateManager.ts +0 -172
- package/core/server/live/SingleConnectionManager.ts +0 -0
- package/core/server/live/StateSignature.ts +0 -705
- package/core/server/live/WebSocketConnectionManager.ts +0 -710
- package/core/server/live/auth/LiveAuthContext.ts +0 -71
- package/core/server/live/auth/LiveAuthManager.ts +0 -304
- package/core/server/live/auth/index.ts +0 -19
- package/core/server/live/auth/types.ts +0 -179
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
// 🔥 FluxStack useRoom - Hook para conectar a salas do backend
|
|
2
|
-
|
|
3
|
-
import { useState, useEffect, useCallback, useRef } from 'react'
|
|
4
|
-
|
|
5
|
-
// Tipos de mensagens do servidor
|
|
6
|
-
interface RoomMessage {
|
|
7
|
-
type: 'room:event' | 'room:state' | 'room:system'
|
|
8
|
-
roomId: string
|
|
9
|
-
event: string
|
|
10
|
-
data: any
|
|
11
|
-
timestamp: number
|
|
12
|
-
senderId?: string
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Mensagens do cliente para o servidor
|
|
16
|
-
interface ClientMessage {
|
|
17
|
-
type: 'room:join' | 'room:leave' | 'room:emit'
|
|
18
|
-
roomId: string
|
|
19
|
-
event?: string
|
|
20
|
-
data?: any
|
|
21
|
-
timestamp: number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Opções do hook
|
|
25
|
-
interface UseRoomOptions<TState> {
|
|
26
|
-
initialState: TState
|
|
27
|
-
autoJoin?: boolean
|
|
28
|
-
onConnect?: () => void
|
|
29
|
-
onDisconnect?: () => void
|
|
30
|
-
onError?: (error: string) => void
|
|
31
|
-
onStateChange?: (state: TState, prevState: TState) => void
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Retorno do hook
|
|
35
|
-
interface UseRoomReturn<TState, TEvents extends Record<string, any>> {
|
|
36
|
-
// Estado
|
|
37
|
-
state: TState
|
|
38
|
-
|
|
39
|
-
// Status
|
|
40
|
-
connected: boolean
|
|
41
|
-
joined: boolean
|
|
42
|
-
roomId: string | null
|
|
43
|
-
|
|
44
|
-
// Ações
|
|
45
|
-
join: (roomId: string) => void
|
|
46
|
-
leave: () => void
|
|
47
|
-
emit: <K extends keyof TEvents>(event: K, data: TEvents[K]) => void
|
|
48
|
-
|
|
49
|
-
// Listeners
|
|
50
|
-
on: <K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void) => () => void
|
|
51
|
-
onSystem: (event: string, handler: (data: any) => void) => () => void
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Gerenciador de conexão WebSocket (singleton por URL)
|
|
55
|
-
class RoomWebSocketManager {
|
|
56
|
-
private static instances = new Map<string, RoomWebSocketManager>()
|
|
57
|
-
|
|
58
|
-
private ws: WebSocket | null = null
|
|
59
|
-
private url: string
|
|
60
|
-
private reconnectAttempts = 0
|
|
61
|
-
private maxReconnectAttempts = 5
|
|
62
|
-
private reconnectDelay = 1000
|
|
63
|
-
private listeners = new Map<string, Set<(msg: RoomMessage) => void>>()
|
|
64
|
-
private connectionListeners = new Set<{
|
|
65
|
-
onConnect?: () => void
|
|
66
|
-
onDisconnect?: () => void
|
|
67
|
-
onError?: (error: string) => void
|
|
68
|
-
}>()
|
|
69
|
-
private messageQueue: ClientMessage[] = []
|
|
70
|
-
private isConnected = false
|
|
71
|
-
|
|
72
|
-
static getInstance(url: string): RoomWebSocketManager {
|
|
73
|
-
if (!this.instances.has(url)) {
|
|
74
|
-
this.instances.set(url, new RoomWebSocketManager(url))
|
|
75
|
-
}
|
|
76
|
-
return this.instances.get(url)!
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
private constructor(url: string) {
|
|
80
|
-
this.url = url
|
|
81
|
-
this.connect()
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
private connect(): void {
|
|
85
|
-
try {
|
|
86
|
-
this.ws = new WebSocket(this.url)
|
|
87
|
-
|
|
88
|
-
this.ws.onopen = () => {
|
|
89
|
-
console.log('[RoomWS] Connected')
|
|
90
|
-
this.isConnected = true
|
|
91
|
-
this.reconnectAttempts = 0
|
|
92
|
-
|
|
93
|
-
// Enviar mensagens em fila
|
|
94
|
-
for (const msg of this.messageQueue) {
|
|
95
|
-
this.send(msg)
|
|
96
|
-
}
|
|
97
|
-
this.messageQueue = []
|
|
98
|
-
|
|
99
|
-
// Notificar listeners
|
|
100
|
-
for (const listener of this.connectionListeners) {
|
|
101
|
-
listener.onConnect?.()
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
this.ws.onmessage = (event) => {
|
|
106
|
-
try {
|
|
107
|
-
const msg: RoomMessage = JSON.parse(event.data)
|
|
108
|
-
this.handleMessage(msg)
|
|
109
|
-
} catch (error) {
|
|
110
|
-
console.error('[RoomWS] Failed to parse message:', error)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
this.ws.onclose = () => {
|
|
115
|
-
console.log('[RoomWS] Disconnected')
|
|
116
|
-
this.isConnected = false
|
|
117
|
-
|
|
118
|
-
for (const listener of this.connectionListeners) {
|
|
119
|
-
listener.onDisconnect?.()
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Tentar reconectar
|
|
123
|
-
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
124
|
-
this.reconnectAttempts++
|
|
125
|
-
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts - 1)
|
|
126
|
-
console.log(`[RoomWS] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`)
|
|
127
|
-
setTimeout(() => this.connect(), delay)
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
this.ws.onerror = (error) => {
|
|
132
|
-
console.error('[RoomWS] Error:', error)
|
|
133
|
-
for (const listener of this.connectionListeners) {
|
|
134
|
-
listener.onError?.('WebSocket error')
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
} catch (error) {
|
|
138
|
-
console.error('[RoomWS] Failed to connect:', error)
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
private handleMessage(msg: RoomMessage): void {
|
|
143
|
-
// Chave para listeners: roomId:event
|
|
144
|
-
const key = `${msg.roomId}:${msg.event}`
|
|
145
|
-
const listeners = this.listeners.get(key)
|
|
146
|
-
|
|
147
|
-
if (listeners) {
|
|
148
|
-
for (const handler of listeners) {
|
|
149
|
-
try {
|
|
150
|
-
handler(msg)
|
|
151
|
-
} catch (error) {
|
|
152
|
-
console.error('[RoomWS] Handler error:', error)
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Também notificar listeners de todos eventos da sala
|
|
158
|
-
const roomKey = `${msg.roomId}:*`
|
|
159
|
-
const roomListeners = this.listeners.get(roomKey)
|
|
160
|
-
if (roomListeners) {
|
|
161
|
-
for (const handler of roomListeners) {
|
|
162
|
-
try {
|
|
163
|
-
handler(msg)
|
|
164
|
-
} catch (error) {
|
|
165
|
-
console.error('[RoomWS] Handler error:', error)
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
send(message: ClientMessage): void {
|
|
172
|
-
if (this.isConnected && this.ws?.readyState === WebSocket.OPEN) {
|
|
173
|
-
this.ws.send(JSON.stringify(message))
|
|
174
|
-
} else {
|
|
175
|
-
this.messageQueue.push(message)
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
subscribe(roomId: string, event: string, handler: (msg: RoomMessage) => void): () => void {
|
|
180
|
-
const key = `${roomId}:${event}`
|
|
181
|
-
|
|
182
|
-
if (!this.listeners.has(key)) {
|
|
183
|
-
this.listeners.set(key, new Set())
|
|
184
|
-
}
|
|
185
|
-
this.listeners.get(key)!.add(handler)
|
|
186
|
-
|
|
187
|
-
return () => {
|
|
188
|
-
this.listeners.get(key)?.delete(handler)
|
|
189
|
-
if (this.listeners.get(key)?.size === 0) {
|
|
190
|
-
this.listeners.delete(key)
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
addConnectionListener(listener: {
|
|
196
|
-
onConnect?: () => void
|
|
197
|
-
onDisconnect?: () => void
|
|
198
|
-
onError?: (error: string) => void
|
|
199
|
-
}): () => void {
|
|
200
|
-
this.connectionListeners.add(listener)
|
|
201
|
-
|
|
202
|
-
// Se já conectado, chamar onConnect imediatamente
|
|
203
|
-
if (this.isConnected) {
|
|
204
|
-
listener.onConnect?.()
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
return () => {
|
|
208
|
-
this.connectionListeners.delete(listener)
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
getConnectionStatus(): boolean {
|
|
213
|
-
return this.isConnected
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Hook principal
|
|
218
|
-
export function useRoom<
|
|
219
|
-
TDef extends { state: any; events: Record<string, any> }
|
|
220
|
-
>(
|
|
221
|
-
wsUrl: string,
|
|
222
|
-
options: UseRoomOptions<TDef['state']>
|
|
223
|
-
): UseRoomReturn<TDef['state'], TDef['events']> {
|
|
224
|
-
const [state, setState] = useState<TDef['state']>(options.initialState)
|
|
225
|
-
const [connected, setConnected] = useState(false)
|
|
226
|
-
const [joined, setJoined] = useState(false)
|
|
227
|
-
const [roomId, setRoomId] = useState<string | null>(null)
|
|
228
|
-
|
|
229
|
-
const wsManager = useRef<RoomWebSocketManager | null>(null)
|
|
230
|
-
const eventHandlers = useRef<Map<string, Set<(data: any) => void>>>(new Map())
|
|
231
|
-
const unsubscribes = useRef<(() => void)[]>([])
|
|
232
|
-
|
|
233
|
-
// Inicializar WebSocket manager
|
|
234
|
-
useEffect(() => {
|
|
235
|
-
wsManager.current = RoomWebSocketManager.getInstance(wsUrl)
|
|
236
|
-
|
|
237
|
-
const unsub = wsManager.current.addConnectionListener({
|
|
238
|
-
onConnect: () => {
|
|
239
|
-
setConnected(true)
|
|
240
|
-
options.onConnect?.()
|
|
241
|
-
},
|
|
242
|
-
onDisconnect: () => {
|
|
243
|
-
setConnected(false)
|
|
244
|
-
setJoined(false)
|
|
245
|
-
options.onDisconnect?.()
|
|
246
|
-
},
|
|
247
|
-
onError: options.onError
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
setConnected(wsManager.current.getConnectionStatus())
|
|
251
|
-
|
|
252
|
-
return () => {
|
|
253
|
-
unsub()
|
|
254
|
-
// Limpar todas as subscriptions
|
|
255
|
-
for (const unsub of unsubscribes.current) {
|
|
256
|
-
unsub()
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}, [wsUrl])
|
|
260
|
-
|
|
261
|
-
// Join room
|
|
262
|
-
const join = useCallback((newRoomId: string) => {
|
|
263
|
-
if (!wsManager.current) return
|
|
264
|
-
|
|
265
|
-
// Sair da sala anterior se existir
|
|
266
|
-
if (roomId && roomId !== newRoomId) {
|
|
267
|
-
leave()
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
setRoomId(newRoomId)
|
|
271
|
-
|
|
272
|
-
// Enviar mensagem de join
|
|
273
|
-
wsManager.current.send({
|
|
274
|
-
type: 'room:join',
|
|
275
|
-
roomId: newRoomId,
|
|
276
|
-
data: { initialState: options.initialState },
|
|
277
|
-
timestamp: Date.now()
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
// Subscrever em todos os eventos da sala
|
|
281
|
-
const unsub = wsManager.current.subscribe(newRoomId, '*', (msg) => {
|
|
282
|
-
// Atualizar estado
|
|
283
|
-
if (msg.event === '$state:sync' || msg.event === '$state:update') {
|
|
284
|
-
setState(prev => {
|
|
285
|
-
const newState = { ...prev, ...msg.data.state }
|
|
286
|
-
options.onStateChange?.(newState, prev)
|
|
287
|
-
return newState
|
|
288
|
-
})
|
|
289
|
-
} else if (msg.event === '$state:change') {
|
|
290
|
-
setState(prev => {
|
|
291
|
-
const newState = { ...prev, [msg.data.path]: msg.data.newValue }
|
|
292
|
-
options.onStateChange?.(newState, prev)
|
|
293
|
-
return newState
|
|
294
|
-
})
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// Chamar handlers registrados
|
|
298
|
-
const handlers = eventHandlers.current.get(msg.event)
|
|
299
|
-
if (handlers) {
|
|
300
|
-
for (const handler of handlers) {
|
|
301
|
-
handler(msg.data)
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
unsubscribes.current.push(unsub)
|
|
307
|
-
setJoined(true)
|
|
308
|
-
}, [roomId, options.initialState])
|
|
309
|
-
|
|
310
|
-
// Leave room
|
|
311
|
-
const leave = useCallback(() => {
|
|
312
|
-
if (!wsManager.current || !roomId) return
|
|
313
|
-
|
|
314
|
-
wsManager.current.send({
|
|
315
|
-
type: 'room:leave',
|
|
316
|
-
roomId,
|
|
317
|
-
timestamp: Date.now()
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
// Limpar subscriptions
|
|
321
|
-
for (const unsub of unsubscribes.current) {
|
|
322
|
-
unsub()
|
|
323
|
-
}
|
|
324
|
-
unsubscribes.current = []
|
|
325
|
-
eventHandlers.current.clear()
|
|
326
|
-
|
|
327
|
-
setJoined(false)
|
|
328
|
-
setRoomId(null)
|
|
329
|
-
setState(options.initialState)
|
|
330
|
-
}, [roomId, options.initialState])
|
|
331
|
-
|
|
332
|
-
// Emit event
|
|
333
|
-
const emit = useCallback(<K extends keyof TDef['events']>(
|
|
334
|
-
event: K,
|
|
335
|
-
data: TDef['events'][K]
|
|
336
|
-
) => {
|
|
337
|
-
if (!wsManager.current || !roomId) return
|
|
338
|
-
|
|
339
|
-
wsManager.current.send({
|
|
340
|
-
type: 'room:emit',
|
|
341
|
-
roomId,
|
|
342
|
-
event: event as string,
|
|
343
|
-
data,
|
|
344
|
-
timestamp: Date.now()
|
|
345
|
-
})
|
|
346
|
-
}, [roomId])
|
|
347
|
-
|
|
348
|
-
// Subscribe to event
|
|
349
|
-
const on = useCallback(<K extends keyof TDef['events']>(
|
|
350
|
-
event: K,
|
|
351
|
-
handler: (data: TDef['events'][K]) => void
|
|
352
|
-
): (() => void) => {
|
|
353
|
-
const eventKey = event as string
|
|
354
|
-
|
|
355
|
-
if (!eventHandlers.current.has(eventKey)) {
|
|
356
|
-
eventHandlers.current.set(eventKey, new Set())
|
|
357
|
-
}
|
|
358
|
-
eventHandlers.current.get(eventKey)!.add(handler)
|
|
359
|
-
|
|
360
|
-
return () => {
|
|
361
|
-
eventHandlers.current.get(eventKey)?.delete(handler)
|
|
362
|
-
}
|
|
363
|
-
}, [])
|
|
364
|
-
|
|
365
|
-
// Subscribe to system event
|
|
366
|
-
const onSystem = useCallback((
|
|
367
|
-
event: string,
|
|
368
|
-
handler: (data: any) => void
|
|
369
|
-
): (() => void) => {
|
|
370
|
-
const eventKey = `$${event}`
|
|
371
|
-
|
|
372
|
-
if (!eventHandlers.current.has(eventKey)) {
|
|
373
|
-
eventHandlers.current.set(eventKey, new Set())
|
|
374
|
-
}
|
|
375
|
-
eventHandlers.current.get(eventKey)!.add(handler)
|
|
376
|
-
|
|
377
|
-
return () => {
|
|
378
|
-
eventHandlers.current.get(eventKey)?.delete(handler)
|
|
379
|
-
}
|
|
380
|
-
}, [])
|
|
381
|
-
|
|
382
|
-
// Auto-join
|
|
383
|
-
useEffect(() => {
|
|
384
|
-
if (options.autoJoin && connected && !joined && roomId) {
|
|
385
|
-
join(roomId)
|
|
386
|
-
}
|
|
387
|
-
}, [options.autoJoin, connected, joined, roomId, join])
|
|
388
|
-
|
|
389
|
-
return {
|
|
390
|
-
state,
|
|
391
|
-
connected,
|
|
392
|
-
joined,
|
|
393
|
-
roomId,
|
|
394
|
-
join,
|
|
395
|
-
leave,
|
|
396
|
-
emit,
|
|
397
|
-
on,
|
|
398
|
-
onSystem
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// Helper para criar hook tipado
|
|
403
|
-
export function createRoomHook<
|
|
404
|
-
TDef extends { state: any; events: Record<string, any> }
|
|
405
|
-
>(wsUrl: string) {
|
|
406
|
-
return (options: UseRoomOptions<TDef['state']>) => useRoom<TDef>(wsUrl, options)
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
export type { UseRoomOptions, UseRoomReturn, RoomMessage, ClientMessage }
|