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.
Files changed (62) hide show
  1. package/LLMD/resources/live-components.md +207 -12
  2. package/app/client/.live-stubs/LiveAdminPanel.js +5 -0
  3. package/app/client/.live-stubs/LiveChat.js +7 -0
  4. package/app/client/.live-stubs/LiveCounter.js +9 -0
  5. package/app/client/.live-stubs/LiveForm.js +11 -0
  6. package/app/client/.live-stubs/LiveLocalCounter.js +8 -0
  7. package/app/client/.live-stubs/LiveRoomChat.js +10 -0
  8. package/app/client/.live-stubs/LiveTodoList.js +9 -0
  9. package/app/client/.live-stubs/LiveUpload.js +15 -0
  10. package/app/client/src/App.tsx +11 -0
  11. package/app/client/src/components/AppLayout.tsx +16 -8
  12. package/app/client/src/live/LiveDebuggerPanel.tsx +1 -1
  13. package/app/client/src/live/TodoListDemo.tsx +158 -0
  14. package/app/server/auth/DevAuthProvider.ts +2 -2
  15. package/app/server/auth/JWTAuthProvider.example.ts +2 -2
  16. package/app/server/index.ts +2 -2
  17. package/app/server/live/LiveAdminPanel.ts +1 -1
  18. package/app/server/live/LiveProtectedChat.ts +1 -1
  19. package/app/server/live/LiveTodoList.ts +110 -0
  20. package/app/server/routes/room.routes.ts +1 -2
  21. package/core/build/live-components-generator.ts +1 -1
  22. package/core/build/vite-plugins.ts +28 -0
  23. package/core/client/components/LiveDebugger.tsx +1 -1
  24. package/core/client/hooks/useLiveUpload.ts +3 -4
  25. package/core/client/index.ts +37 -31
  26. package/core/framework/server.ts +1 -1
  27. package/core/server/index.ts +1 -2
  28. package/core/server/live/auto-generated-components.ts +6 -3
  29. package/core/server/live/index.ts +95 -21
  30. package/core/server/live/websocket-plugin.ts +27 -1087
  31. package/core/types/types.ts +76 -1025
  32. package/core/utils/version.ts +1 -1
  33. package/create-fluxstack.ts +1 -1
  34. package/package.json +5 -1
  35. package/plugins/crypto-auth/index.ts +1 -1
  36. package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +2 -2
  37. package/vite.config.ts +40 -12
  38. package/core/client/LiveComponentsProvider.tsx +0 -531
  39. package/core/client/components/Live.tsx +0 -111
  40. package/core/client/hooks/AdaptiveChunkSizer.ts +0 -215
  41. package/core/client/hooks/state-validator.ts +0 -130
  42. package/core/client/hooks/useChunkedUpload.ts +0 -359
  43. package/core/client/hooks/useLiveChunkedUpload.ts +0 -87
  44. package/core/client/hooks/useLiveComponent.ts +0 -853
  45. package/core/client/hooks/useLiveDebugger.ts +0 -392
  46. package/core/client/hooks/useRoom.ts +0 -409
  47. package/core/client/hooks/useRoomProxy.ts +0 -382
  48. package/core/server/live/ComponentRegistry.ts +0 -1128
  49. package/core/server/live/FileUploadManager.ts +0 -446
  50. package/core/server/live/LiveComponentPerformanceMonitor.ts +0 -931
  51. package/core/server/live/LiveDebugger.ts +0 -462
  52. package/core/server/live/LiveLogger.ts +0 -144
  53. package/core/server/live/LiveRoomManager.ts +0 -278
  54. package/core/server/live/RoomEventBus.ts +0 -234
  55. package/core/server/live/RoomStateManager.ts +0 -172
  56. package/core/server/live/SingleConnectionManager.ts +0 -0
  57. package/core/server/live/StateSignature.ts +0 -705
  58. package/core/server/live/WebSocketConnectionManager.ts +0 -710
  59. package/core/server/live/auth/LiveAuthContext.ts +0 -71
  60. package/core/server/live/auth/LiveAuthManager.ts +0 -304
  61. package/core/server/live/auth/index.ts +0 -19
  62. 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
- }