create-fluxstack 1.14.0 → 1.15.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/resources/live-components.md +207 -12
- package/app/client/.live-stubs/LiveAdminPanel.js +5 -0
- package/app/client/.live-stubs/LiveChat.js +7 -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/LiveRoomChat.js +10 -0
- package/app/client/.live-stubs/LiveTodoList.js +9 -0
- package/app/client/.live-stubs/LiveUpload.js +15 -0
- package/app/client/src/App.tsx +11 -0
- package/app/client/src/components/AppLayout.tsx +16 -8
- package/app/client/src/live/LiveDebuggerPanel.tsx +1 -1
- package/app/client/src/live/TodoListDemo.tsx +158 -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/LiveProtectedChat.ts +1 -1
- package/app/server/live/LiveTodoList.ts +110 -0
- package/app/server/routes/room.routes.ts +1 -2
- package/core/build/live-components-generator.ts +1 -1
- package/core/build/vite-plugins.ts +28 -0
- package/core/client/components/LiveDebugger.tsx +1 -1
- package/core/client/hooks/useLiveUpload.ts +3 -4
- package/core/client/index.ts +37 -31
- package/core/framework/server.ts +1 -1
- package/core/server/index.ts +1 -2
- package/core/server/live/auto-generated-components.ts +6 -3
- package/core/server/live/index.ts +95 -21
- package/core/server/live/websocket-plugin.ts +27 -1087
- package/core/types/types.ts +76 -1025
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +1 -1
- package/package.json +5 -1
- package/plugins/crypto-auth/index.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +2 -2
- package/vite.config.ts +40 -12
- package/core/client/LiveComponentsProvider.tsx +0 -531
- package/core/client/components/Live.tsx +0 -111
- 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,392 +0,0 @@
|
|
|
1
|
-
// 🔍 FluxStack Live Component Debugger - Client Hook
|
|
2
|
-
//
|
|
3
|
-
// Connects to the debug WebSocket endpoint and streams debug events.
|
|
4
|
-
// Provides reactive state for building debugger UIs.
|
|
5
|
-
//
|
|
6
|
-
// Usage:
|
|
7
|
-
// const debugger = useLiveDebugger()
|
|
8
|
-
// debugger.components // Active components with current states
|
|
9
|
-
// debugger.events // Event timeline
|
|
10
|
-
// debugger.connected // Connection status
|
|
11
|
-
|
|
12
|
-
import { useState, useEffect, useRef, useCallback } from 'react'
|
|
13
|
-
|
|
14
|
-
// ===== Types (mirrored from server) =====
|
|
15
|
-
|
|
16
|
-
export type DebugEventType =
|
|
17
|
-
| 'COMPONENT_MOUNT'
|
|
18
|
-
| 'COMPONENT_UNMOUNT'
|
|
19
|
-
| 'COMPONENT_REHYDRATE'
|
|
20
|
-
| 'STATE_CHANGE'
|
|
21
|
-
| 'ACTION_CALL'
|
|
22
|
-
| 'ACTION_RESULT'
|
|
23
|
-
| 'ACTION_ERROR'
|
|
24
|
-
| 'ROOM_JOIN'
|
|
25
|
-
| 'ROOM_LEAVE'
|
|
26
|
-
| 'ROOM_EMIT'
|
|
27
|
-
| 'ROOM_EVENT_RECEIVED'
|
|
28
|
-
| 'WS_CONNECT'
|
|
29
|
-
| 'WS_DISCONNECT'
|
|
30
|
-
| 'ERROR'
|
|
31
|
-
|
|
32
|
-
export interface DebugEvent {
|
|
33
|
-
id: string
|
|
34
|
-
timestamp: number
|
|
35
|
-
type: DebugEventType
|
|
36
|
-
componentId: string | null
|
|
37
|
-
componentName: string | null
|
|
38
|
-
data: Record<string, unknown>
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface ComponentSnapshot {
|
|
42
|
-
componentId: string
|
|
43
|
-
componentName: string
|
|
44
|
-
/** Developer-defined label for easier identification in the debugger */
|
|
45
|
-
debugLabel?: string
|
|
46
|
-
state: Record<string, unknown>
|
|
47
|
-
rooms: string[]
|
|
48
|
-
mountedAt: number
|
|
49
|
-
lastActivity: number
|
|
50
|
-
actionCount: number
|
|
51
|
-
stateChangeCount: number
|
|
52
|
-
errorCount: number
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export interface DebugSnapshot {
|
|
56
|
-
components: ComponentSnapshot[]
|
|
57
|
-
connections: number
|
|
58
|
-
uptime: number
|
|
59
|
-
totalEvents: number
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export interface DebugFilter {
|
|
63
|
-
componentId?: string | null
|
|
64
|
-
types?: Set<DebugEventType>
|
|
65
|
-
search?: string
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface UseLiveDebuggerReturn {
|
|
69
|
-
// Connection
|
|
70
|
-
connected: boolean
|
|
71
|
-
connecting: boolean
|
|
72
|
-
/** Server reported that debugging is disabled */
|
|
73
|
-
serverDisabled: boolean
|
|
74
|
-
|
|
75
|
-
// Data
|
|
76
|
-
components: ComponentSnapshot[]
|
|
77
|
-
events: DebugEvent[]
|
|
78
|
-
filteredEvents: DebugEvent[]
|
|
79
|
-
snapshot: DebugSnapshot | null
|
|
80
|
-
|
|
81
|
-
// Selected component
|
|
82
|
-
selectedComponentId: string | null
|
|
83
|
-
selectedComponent: ComponentSnapshot | null
|
|
84
|
-
selectComponent: (id: string | null) => void
|
|
85
|
-
|
|
86
|
-
// Filtering
|
|
87
|
-
filter: DebugFilter
|
|
88
|
-
setFilter: (filter: Partial<DebugFilter>) => void
|
|
89
|
-
|
|
90
|
-
// Controls
|
|
91
|
-
paused: boolean
|
|
92
|
-
togglePause: () => void
|
|
93
|
-
clearEvents: () => void
|
|
94
|
-
reconnect: () => void
|
|
95
|
-
|
|
96
|
-
// Stats
|
|
97
|
-
eventCount: number
|
|
98
|
-
componentCount: number
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export interface UseLiveDebuggerOptions {
|
|
102
|
-
/** Max events to keep in memory. Default: 500 */
|
|
103
|
-
maxEvents?: number
|
|
104
|
-
/** Auto-connect on mount. Default: true */
|
|
105
|
-
autoConnect?: boolean
|
|
106
|
-
/** Custom WebSocket URL */
|
|
107
|
-
url?: string
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// ===== Hook =====
|
|
111
|
-
|
|
112
|
-
export function useLiveDebugger(options: UseLiveDebuggerOptions = {}): UseLiveDebuggerReturn {
|
|
113
|
-
const {
|
|
114
|
-
maxEvents = 500,
|
|
115
|
-
autoConnect = true,
|
|
116
|
-
url
|
|
117
|
-
} = options
|
|
118
|
-
|
|
119
|
-
// State
|
|
120
|
-
const [connected, setConnected] = useState(false)
|
|
121
|
-
const [connecting, setConnecting] = useState(false)
|
|
122
|
-
const [components, setComponents] = useState<ComponentSnapshot[]>([])
|
|
123
|
-
const [events, setEvents] = useState<DebugEvent[]>([])
|
|
124
|
-
const [snapshot, setSnapshot] = useState<DebugSnapshot | null>(null)
|
|
125
|
-
const [selectedComponentId, setSelectedComponentId] = useState<string | null>(null)
|
|
126
|
-
const [filter, setFilterState] = useState<DebugFilter>({})
|
|
127
|
-
const [paused, setPaused] = useState(false)
|
|
128
|
-
const [serverDisabled, setServerDisabled] = useState(false)
|
|
129
|
-
|
|
130
|
-
// Refs
|
|
131
|
-
const wsRef = useRef<WebSocket | null>(null)
|
|
132
|
-
const pausedRef = useRef(false)
|
|
133
|
-
const serverDisabledRef = useRef(false)
|
|
134
|
-
const reconnectTimeoutRef = useRef<number | null>(null)
|
|
135
|
-
|
|
136
|
-
// Keep refs in sync
|
|
137
|
-
pausedRef.current = paused
|
|
138
|
-
serverDisabledRef.current = serverDisabled
|
|
139
|
-
|
|
140
|
-
// Build WebSocket URL
|
|
141
|
-
const getWsUrl = useCallback(() => {
|
|
142
|
-
if (url) return url
|
|
143
|
-
if (typeof window === 'undefined') return 'ws://localhost:3000/api/live/debug/ws'
|
|
144
|
-
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
145
|
-
return `${protocol}//${window.location.host}/api/live/debug/ws`
|
|
146
|
-
}, [url])
|
|
147
|
-
|
|
148
|
-
// Connect
|
|
149
|
-
const connect = useCallback(() => {
|
|
150
|
-
if (wsRef.current?.readyState === WebSocket.CONNECTING) return
|
|
151
|
-
if (wsRef.current?.readyState === WebSocket.OPEN) return
|
|
152
|
-
|
|
153
|
-
setConnecting(true)
|
|
154
|
-
|
|
155
|
-
try {
|
|
156
|
-
const ws = new WebSocket(getWsUrl())
|
|
157
|
-
wsRef.current = ws
|
|
158
|
-
|
|
159
|
-
ws.onopen = () => {
|
|
160
|
-
setConnected(true)
|
|
161
|
-
setConnecting(false)
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
ws.onmessage = (event) => {
|
|
165
|
-
try {
|
|
166
|
-
const msg = JSON.parse(event.data)
|
|
167
|
-
|
|
168
|
-
if (msg.type === 'DEBUG_DISABLED') {
|
|
169
|
-
// Server has debugging disabled — stop reconnecting
|
|
170
|
-
setServerDisabled(true)
|
|
171
|
-
return
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (msg.type === 'DEBUG_WELCOME') {
|
|
175
|
-
// Initial snapshot
|
|
176
|
-
setServerDisabled(false)
|
|
177
|
-
const snap = msg.snapshot as DebugSnapshot
|
|
178
|
-
setSnapshot(snap)
|
|
179
|
-
setComponents(snap.components)
|
|
180
|
-
} else if (msg.type === 'DEBUG_EVENT') {
|
|
181
|
-
if (pausedRef.current) return
|
|
182
|
-
|
|
183
|
-
const debugEvent = msg.event as DebugEvent
|
|
184
|
-
|
|
185
|
-
// Update events list
|
|
186
|
-
setEvents(prev => {
|
|
187
|
-
const next = [...prev, debugEvent]
|
|
188
|
-
return next.length > maxEvents ? next.slice(-maxEvents) : next
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
// Update component snapshots based on event
|
|
192
|
-
updateComponentsFromEvent(debugEvent)
|
|
193
|
-
} else if (msg.type === 'DEBUG_SNAPSHOT') {
|
|
194
|
-
const snap = msg.snapshot as DebugSnapshot
|
|
195
|
-
setSnapshot(snap)
|
|
196
|
-
setComponents(snap.components)
|
|
197
|
-
}
|
|
198
|
-
} catch {
|
|
199
|
-
// Ignore parse errors
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
ws.onclose = () => {
|
|
204
|
-
setConnected(false)
|
|
205
|
-
setConnecting(false)
|
|
206
|
-
wsRef.current = null
|
|
207
|
-
|
|
208
|
-
// Don't reconnect if server told us debug is disabled
|
|
209
|
-
if (serverDisabledRef.current) return
|
|
210
|
-
|
|
211
|
-
// Auto-reconnect after 3 seconds
|
|
212
|
-
reconnectTimeoutRef.current = window.setTimeout(() => {
|
|
213
|
-
if (autoConnect) connect()
|
|
214
|
-
}, 3000)
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
ws.onerror = () => {
|
|
218
|
-
setConnecting(false)
|
|
219
|
-
}
|
|
220
|
-
} catch {
|
|
221
|
-
setConnecting(false)
|
|
222
|
-
}
|
|
223
|
-
}, [getWsUrl, maxEvents, autoConnect])
|
|
224
|
-
|
|
225
|
-
// Update components from incoming events
|
|
226
|
-
const updateComponentsFromEvent = useCallback((event: DebugEvent) => {
|
|
227
|
-
setComponents(prev => {
|
|
228
|
-
switch (event.type) {
|
|
229
|
-
case 'COMPONENT_MOUNT': {
|
|
230
|
-
if (!event.componentId || !event.componentName) return prev
|
|
231
|
-
const existing = prev.find(c => c.componentId === event.componentId)
|
|
232
|
-
if (existing) return prev
|
|
233
|
-
return [...prev, {
|
|
234
|
-
componentId: event.componentId,
|
|
235
|
-
componentName: event.componentName,
|
|
236
|
-
debugLabel: (event.data.debugLabel as string) || undefined,
|
|
237
|
-
state: (event.data.initialState as Record<string, unknown>) || {},
|
|
238
|
-
rooms: event.data.room ? [event.data.room as string] : [],
|
|
239
|
-
mountedAt: event.timestamp,
|
|
240
|
-
lastActivity: event.timestamp,
|
|
241
|
-
actionCount: 0,
|
|
242
|
-
stateChangeCount: 0,
|
|
243
|
-
errorCount: 0
|
|
244
|
-
}]
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
case 'COMPONENT_UNMOUNT': {
|
|
248
|
-
return prev.filter(c => c.componentId !== event.componentId)
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
case 'STATE_CHANGE': {
|
|
252
|
-
return prev.map(c => {
|
|
253
|
-
if (c.componentId !== event.componentId) return c
|
|
254
|
-
return {
|
|
255
|
-
...c,
|
|
256
|
-
state: (event.data.fullState as Record<string, unknown>) || c.state,
|
|
257
|
-
stateChangeCount: c.stateChangeCount + 1,
|
|
258
|
-
lastActivity: event.timestamp
|
|
259
|
-
}
|
|
260
|
-
})
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
case 'ACTION_CALL': {
|
|
264
|
-
return prev.map(c => {
|
|
265
|
-
if (c.componentId !== event.componentId) return c
|
|
266
|
-
return {
|
|
267
|
-
...c,
|
|
268
|
-
actionCount: c.actionCount + 1,
|
|
269
|
-
lastActivity: event.timestamp
|
|
270
|
-
}
|
|
271
|
-
})
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
case 'ACTION_ERROR':
|
|
275
|
-
case 'ERROR': {
|
|
276
|
-
return prev.map(c => {
|
|
277
|
-
if (c.componentId !== event.componentId) return c
|
|
278
|
-
return {
|
|
279
|
-
...c,
|
|
280
|
-
errorCount: c.errorCount + 1,
|
|
281
|
-
lastActivity: event.timestamp
|
|
282
|
-
}
|
|
283
|
-
})
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
case 'ROOM_JOIN': {
|
|
287
|
-
return prev.map(c => {
|
|
288
|
-
if (c.componentId !== event.componentId) return c
|
|
289
|
-
const roomId = event.data.roomId as string
|
|
290
|
-
if (c.rooms.includes(roomId)) return c
|
|
291
|
-
return { ...c, rooms: [...c.rooms, roomId] }
|
|
292
|
-
})
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
case 'ROOM_LEAVE': {
|
|
296
|
-
return prev.map(c => {
|
|
297
|
-
if (c.componentId !== event.componentId) return c
|
|
298
|
-
const roomId = event.data.roomId as string
|
|
299
|
-
return { ...c, rooms: c.rooms.filter(r => r !== roomId) }
|
|
300
|
-
})
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
default:
|
|
304
|
-
return prev.map(c => {
|
|
305
|
-
if (c.componentId !== event.componentId) return c
|
|
306
|
-
return { ...c, lastActivity: event.timestamp }
|
|
307
|
-
})
|
|
308
|
-
}
|
|
309
|
-
})
|
|
310
|
-
}, [])
|
|
311
|
-
|
|
312
|
-
// Disconnect
|
|
313
|
-
const disconnect = useCallback(() => {
|
|
314
|
-
if (reconnectTimeoutRef.current) {
|
|
315
|
-
clearTimeout(reconnectTimeoutRef.current)
|
|
316
|
-
reconnectTimeoutRef.current = null
|
|
317
|
-
}
|
|
318
|
-
if (wsRef.current) {
|
|
319
|
-
wsRef.current.close()
|
|
320
|
-
wsRef.current = null
|
|
321
|
-
}
|
|
322
|
-
setConnected(false)
|
|
323
|
-
setConnecting(false)
|
|
324
|
-
}, [])
|
|
325
|
-
|
|
326
|
-
// Reconnect
|
|
327
|
-
const reconnect = useCallback(() => {
|
|
328
|
-
disconnect()
|
|
329
|
-
setTimeout(() => connect(), 100)
|
|
330
|
-
}, [connect, disconnect])
|
|
331
|
-
|
|
332
|
-
// Filter events
|
|
333
|
-
const filteredEvents = events.filter(event => {
|
|
334
|
-
if (filter.componentId && event.componentId !== filter.componentId) return false
|
|
335
|
-
if (filter.types && filter.types.size > 0 && !filter.types.has(event.type)) return false
|
|
336
|
-
if (filter.search) {
|
|
337
|
-
const search = filter.search.toLowerCase()
|
|
338
|
-
const matchesData = JSON.stringify(event.data).toLowerCase().includes(search)
|
|
339
|
-
const matchesName = event.componentName?.toLowerCase().includes(search)
|
|
340
|
-
const matchesType = event.type.toLowerCase().includes(search)
|
|
341
|
-
if (!matchesData && !matchesName && !matchesType) return false
|
|
342
|
-
}
|
|
343
|
-
return true
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
// Selected component
|
|
347
|
-
const selectedComponent = selectedComponentId
|
|
348
|
-
? components.find(c => c.componentId === selectedComponentId) ?? null
|
|
349
|
-
: null
|
|
350
|
-
|
|
351
|
-
// Filter setter
|
|
352
|
-
const setFilter = useCallback((partial: Partial<DebugFilter>) => {
|
|
353
|
-
setFilterState(prev => ({ ...prev, ...partial }))
|
|
354
|
-
}, [])
|
|
355
|
-
|
|
356
|
-
// Toggle pause
|
|
357
|
-
const togglePause = useCallback(() => {
|
|
358
|
-
setPaused(prev => !prev)
|
|
359
|
-
}, [])
|
|
360
|
-
|
|
361
|
-
// Clear events
|
|
362
|
-
const clearEvents = useCallback(() => {
|
|
363
|
-
setEvents([])
|
|
364
|
-
}, [])
|
|
365
|
-
|
|
366
|
-
// Auto-connect
|
|
367
|
-
useEffect(() => {
|
|
368
|
-
if (autoConnect) connect()
|
|
369
|
-
return () => disconnect()
|
|
370
|
-
}, [autoConnect, connect, disconnect])
|
|
371
|
-
|
|
372
|
-
return {
|
|
373
|
-
connected,
|
|
374
|
-
connecting,
|
|
375
|
-
serverDisabled,
|
|
376
|
-
components,
|
|
377
|
-
events,
|
|
378
|
-
filteredEvents,
|
|
379
|
-
snapshot,
|
|
380
|
-
selectedComponentId,
|
|
381
|
-
selectedComponent,
|
|
382
|
-
selectComponent: setSelectedComponentId,
|
|
383
|
-
filter,
|
|
384
|
-
setFilter,
|
|
385
|
-
paused,
|
|
386
|
-
togglePause,
|
|
387
|
-
clearEvents,
|
|
388
|
-
reconnect,
|
|
389
|
-
eventCount: events.length,
|
|
390
|
-
componentCount: components.length
|
|
391
|
-
}
|
|
392
|
-
}
|