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,111 +0,0 @@
1
- // 🔥 FluxStack Live - Hook para componentes real-time
2
- //
3
- // Uso:
4
- // import { Live } from '@/core/client'
5
- // import { LiveForm } from '@server/live/LiveForm'
6
- //
7
- // // Sem estado inicial - usa defaultState do componente
8
- // const form = Live.use(LiveForm)
9
- //
10
- // // Com estado inicial parcial (override)
11
- // const form = Live.use(LiveForm, { name: 'João' })
12
- //
13
- // return (
14
- // <input {...form.$field('name', { syncOn: 'blur' })} />
15
- // <button onClick={() => form.submit()}>Enviar</button>
16
- // )
17
- //
18
- // 🔥 Broadcasts Tipados (Discriminated Union):
19
- // // No servidor, defina a interface de broadcasts:
20
- // export interface LiveFormBroadcasts {
21
- // FORM_SUBMITTED: { formId: string; data: any }
22
- // FIELD_CHANGED: { field: string; value: any }
23
- // }
24
- //
25
- // // No cliente, use com tipagem automática (discriminated union):
26
- // import { LiveForm, type LiveFormBroadcasts } from '@server/live/LiveForm'
27
- //
28
- // const form = Live.use(LiveForm)
29
- // form.$onBroadcast<LiveFormBroadcasts>((event) => {
30
- // switch (event.type) {
31
- // case 'FORM_SUBMITTED':
32
- // console.log(event.data.formId) // ✅ Tipado como string!
33
- // break
34
- // case 'FIELD_CHANGED':
35
- // console.log(event.data.field) // ✅ Tipado como string!
36
- // break
37
- // }
38
- // })
39
-
40
- import { useLiveComponent } from '../hooks/useLiveComponent'
41
- import type { UseLiveComponentOptions, LiveProxy, LiveProxyWithBroadcasts } from '../hooks/useLiveComponent'
42
-
43
- // ===== Tipos para Inferência do Servidor =====
44
-
45
- // Extrai o defaultState estático da classe
46
- type ExtractDefaultState<T> = T extends { defaultState: infer S }
47
- ? S extends Record<string, any> ? S : Record<string, any>
48
- : Record<string, any>
49
-
50
- // Extrai o State da classe do servidor (via instance.state)
51
- type ExtractState<T> = T extends { new(...args: any[]): { state: infer S } }
52
- ? S extends Record<string, any> ? S : Record<string, any>
53
- : ExtractDefaultState<T>
54
-
55
- // Extrai os nomes de publicActions como union type
56
- type ExtractPublicActionNames<T> = T extends { publicActions: readonly (infer A)[] }
57
- ? A extends string ? A : never
58
- : never
59
-
60
- // Extrai as Actions respeitando publicActions (MANDATORY)
61
- // - Se publicActions está definido: somente métodos listados são expostos
62
- // - Se publicActions NÃO está definido: nenhuma action disponível (secure by default)
63
- type ExtractActions<T> = T extends { new(...args: any[]): infer Instance }
64
- ? T extends { publicActions: readonly string[] }
65
- ? {
66
- [K in keyof Instance as K extends ExtractPublicActionNames<T>
67
- ? Instance[K] extends (...args: any[]) => Promise<any> ? K : never
68
- : never
69
- ]: Instance[K]
70
- }
71
- : Record<string, never>
72
- : Record<string, never>
73
-
74
- // ===== Opções do Live.use() =====
75
-
76
- interface LiveUseOptions<TState> extends UseLiveComponentOptions {
77
- /** Estado inicial para o componente */
78
- initialState?: Partial<TState>
79
- }
80
-
81
- // ===== Hook Principal =====
82
-
83
- function useLive<
84
- T extends { new(...args: any[]): any; defaultState?: Record<string, any>; componentName: string; publicActions?: readonly string[] },
85
- TBroadcasts extends Record<string, any> = Record<string, any>
86
- >(
87
- ComponentClass: T,
88
- options?: LiveUseOptions<ExtractState<T>>
89
- ): LiveProxyWithBroadcasts<ExtractState<T>, ExtractActions<T>, TBroadcasts> {
90
- // Use static componentName (required for production builds with minification)
91
- const componentName = ComponentClass.componentName
92
-
93
- // Usa defaultState da classe se não passar initialState
94
- const defaultState = (ComponentClass as any).defaultState || {}
95
- const { initialState, ...restOptions } = options || {}
96
- const mergedState = { ...defaultState, ...initialState } as ExtractState<T>
97
-
98
- return useLiveComponent<ExtractState<T>, ExtractActions<T>, TBroadcasts>(
99
- componentName,
100
- mergedState,
101
- restOptions
102
- )
103
- }
104
-
105
- // ===== Export =====
106
-
107
- export const Live = {
108
- use: useLive
109
- }
110
-
111
- export default Live
@@ -1,215 +0,0 @@
1
- // 🚀 Adaptive Chunk Sizing - Dynamic chunk size adjustment based on connection speed
2
- // Automatically optimizes upload speed by adjusting chunk sizes
3
-
4
- export interface AdaptiveChunkConfig {
5
- minChunkSize: number // Minimum chunk size (default: 16KB)
6
- maxChunkSize: number // Maximum chunk size (default: 1MB)
7
- initialChunkSize: number // Starting chunk size (default: 64KB)
8
- targetLatency: number // Target latency per chunk in ms (default: 200ms)
9
- adjustmentFactor: number // How aggressively to adjust (default: 1.5)
10
- measurementWindow: number // Number of chunks to measure (default: 3)
11
- }
12
-
13
- export interface ChunkMetrics {
14
- chunkIndex: number
15
- chunkSize: number
16
- startTime: number
17
- endTime: number
18
- latency: number
19
- throughput: number // bytes per second
20
- success: boolean
21
- }
22
-
23
- export class AdaptiveChunkSizer {
24
- private config: Required<AdaptiveChunkConfig>
25
- private currentChunkSize: number
26
- private metrics: ChunkMetrics[] = []
27
- private consecutiveErrors = 0
28
- private consecutiveSuccesses = 0
29
-
30
- constructor(config: Partial<AdaptiveChunkConfig> = {}) {
31
- this.config = {
32
- minChunkSize: config.minChunkSize ?? 16 * 1024, // 16KB
33
- maxChunkSize: config.maxChunkSize ?? 1024 * 1024, // 1MB
34
- initialChunkSize: config.initialChunkSize ?? 64 * 1024, // 64KB
35
- targetLatency: config.targetLatency ?? 200, // 200ms
36
- adjustmentFactor: config.adjustmentFactor ?? 1.5,
37
- measurementWindow: config.measurementWindow ?? 3
38
- }
39
-
40
- this.currentChunkSize = this.config.initialChunkSize
41
- }
42
-
43
- /**
44
- * Get the current optimal chunk size
45
- */
46
- getChunkSize(): number {
47
- return this.currentChunkSize
48
- }
49
-
50
- /**
51
- * Record the start of a chunk upload
52
- */
53
- recordChunkStart(chunkIndex: number): number {
54
- return Date.now()
55
- }
56
-
57
- /**
58
- * Record the completion of a chunk upload and adjust chunk size
59
- */
60
- recordChunkComplete(
61
- chunkIndex: number,
62
- chunkSize: number,
63
- startTime: number,
64
- success: boolean
65
- ): void {
66
- const endTime = Date.now()
67
- const latency = endTime - startTime
68
- const throughput = success ? (chunkSize / latency) * 1000 : 0 // bytes per second
69
-
70
- const metric: ChunkMetrics = {
71
- chunkIndex,
72
- chunkSize,
73
- startTime,
74
- endTime,
75
- latency,
76
- throughput,
77
- success
78
- }
79
-
80
- this.metrics.push(metric)
81
-
82
- // Keep only recent measurements
83
- if (this.metrics.length > this.config.measurementWindow * 2) {
84
- this.metrics = this.metrics.slice(-this.config.measurementWindow * 2)
85
- }
86
-
87
- if (success) {
88
- this.consecutiveSuccesses++
89
- this.consecutiveErrors = 0
90
- this.adjustChunkSizeUp(latency)
91
- } else {
92
- this.consecutiveErrors++
93
- this.consecutiveSuccesses = 0
94
- this.adjustChunkSizeDown()
95
- }
96
-
97
- console.log(`📊 Adaptive Chunk Stats:`, {
98
- chunkIndex,
99
- currentSize: this.formatBytes(this.currentChunkSize),
100
- latency: `${latency}ms`,
101
- throughput: `${this.formatBytes(throughput)}/s`,
102
- avgThroughput: `${this.formatBytes(this.getAverageThroughput())}/s`,
103
- success
104
- })
105
- }
106
-
107
- /**
108
- * Increase chunk size if connection is fast
109
- */
110
- private adjustChunkSizeUp(latency: number): void {
111
- // Only increase if we have enough successful measurements
112
- if (this.consecutiveSuccesses < 2) return
113
-
114
- // Only increase if latency is below target
115
- if (latency > this.config.targetLatency) return
116
-
117
- // Calculate new chunk size based on how much faster we are than target
118
- const latencyRatio = this.config.targetLatency / latency
119
- let newSize = Math.floor(this.currentChunkSize * Math.min(latencyRatio, this.config.adjustmentFactor))
120
-
121
- // Cap at max chunk size
122
- newSize = Math.min(newSize, this.config.maxChunkSize)
123
-
124
- if (newSize > this.currentChunkSize) {
125
- console.log(`⬆️ Increasing chunk size: ${this.formatBytes(this.currentChunkSize)} → ${this.formatBytes(newSize)}`)
126
- this.currentChunkSize = newSize
127
- }
128
- }
129
-
130
- /**
131
- * Decrease chunk size if connection is slow or unstable
132
- */
133
- private adjustChunkSizeDown(): void {
134
- // Decrease more aggressively on errors
135
- const decreaseFactor = this.consecutiveErrors > 1 ? 2 : this.config.adjustmentFactor
136
-
137
- let newSize = Math.floor(this.currentChunkSize / decreaseFactor)
138
-
139
- // Cap at min chunk size
140
- newSize = Math.max(newSize, this.config.minChunkSize)
141
-
142
- if (newSize < this.currentChunkSize) {
143
- console.log(`⬇️ Decreasing chunk size: ${this.formatBytes(this.currentChunkSize)} → ${this.formatBytes(newSize)}`)
144
- this.currentChunkSize = newSize
145
- }
146
- }
147
-
148
- /**
149
- * Get average throughput from recent measurements
150
- */
151
- getAverageThroughput(): number {
152
- if (this.metrics.length === 0) return 0
153
-
154
- const recentMetrics = this.metrics
155
- .slice(-this.config.measurementWindow)
156
- .filter(m => m.success)
157
-
158
- if (recentMetrics.length === 0) return 0
159
-
160
- const totalThroughput = recentMetrics.reduce((sum, m) => sum + m.throughput, 0)
161
- return totalThroughput / recentMetrics.length
162
- }
163
-
164
- /**
165
- * Get average latency from recent measurements
166
- */
167
- getAverageLatency(): number {
168
- if (this.metrics.length === 0) return 0
169
-
170
- const recentMetrics = this.metrics
171
- .slice(-this.config.measurementWindow)
172
- .filter(m => m.success)
173
-
174
- if (recentMetrics.length === 0) return 0
175
-
176
- const totalLatency = recentMetrics.reduce((sum, m) => sum + m.latency, 0)
177
- return totalLatency / recentMetrics.length
178
- }
179
-
180
- /**
181
- * Get current performance statistics
182
- */
183
- getStats() {
184
- return {
185
- currentChunkSize: this.currentChunkSize,
186
- averageThroughput: this.getAverageThroughput(),
187
- averageLatency: this.getAverageLatency(),
188
- consecutiveSuccesses: this.consecutiveSuccesses,
189
- consecutiveErrors: this.consecutiveErrors,
190
- totalMeasurements: this.metrics.length,
191
- config: this.config
192
- }
193
- }
194
-
195
- /**
196
- * Reset the adaptive chunking state
197
- */
198
- reset(): void {
199
- this.currentChunkSize = this.config.initialChunkSize
200
- this.metrics = []
201
- this.consecutiveErrors = 0
202
- this.consecutiveSuccesses = 0
203
- }
204
-
205
- /**
206
- * Format bytes for display
207
- */
208
- private formatBytes(bytes: number): string {
209
- if (bytes === 0) return '0 B'
210
- const k = 1024
211
- const sizes = ['B', 'KB', 'MB', 'GB']
212
- const i = Math.floor(Math.log(bytes) / Math.log(k))
213
- return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i]
214
- }
215
- }
@@ -1,130 +0,0 @@
1
- // 🔥 State Validation Utilities
2
-
3
- import type { StateValidation, StateConflict, HybridState } from '@core/types/types'
4
-
5
- export class StateValidator {
6
- /**
7
- * Generate checksum for state object
8
- */
9
- static generateChecksum(state: any): string {
10
- const json = JSON.stringify(state, Object.keys(state).sort())
11
- let hash = 0
12
- for (let i = 0; i < json.length; i++) {
13
- const char = json.charCodeAt(i)
14
- hash = ((hash << 5) - hash) + char
15
- hash = hash & hash // Convert to 32-bit integer
16
- }
17
- return Math.abs(hash).toString(16)
18
- }
19
-
20
- /**
21
- * Create validation metadata
22
- */
23
- static createValidation(
24
- state: any,
25
- source: 'client' | 'server' | 'mount' = 'client'
26
- ): StateValidation {
27
- return {
28
- checksum: this.generateChecksum(state),
29
- version: Date.now(),
30
- timestamp: Date.now(),
31
- source
32
- }
33
- }
34
-
35
- /**
36
- * Compare two states and detect conflicts
37
- */
38
- static detectConflicts<T>(
39
- clientState: T,
40
- serverState: T,
41
- excludeFields: string[] = ['lastUpdated', 'version']
42
- ): StateConflict[] {
43
- const conflicts: StateConflict[] = []
44
-
45
- const clientKeys = Object.keys(clientState as any)
46
- const serverKeys = Object.keys(serverState as any)
47
- const allKeys = Array.from(new Set([...clientKeys, ...serverKeys]))
48
-
49
- for (const key of allKeys) {
50
- if (excludeFields.includes(key)) continue
51
-
52
- const clientValue = (clientState as any)?.[key]
53
- const serverValue = (serverState as any)?.[key]
54
-
55
- if (JSON.stringify(clientValue) !== JSON.stringify(serverValue)) {
56
- conflicts.push({
57
- property: key as string,
58
- clientValue,
59
- serverValue,
60
- timestamp: Date.now(),
61
- resolved: false
62
- })
63
- }
64
- }
65
-
66
- return conflicts
67
- }
68
-
69
- /**
70
- * Merge states with conflict resolution
71
- */
72
- static mergeStates<T>(
73
- clientState: T,
74
- serverState: T,
75
- conflicts: StateConflict[],
76
- strategy: 'client' | 'server' | 'smart' = 'smart'
77
- ): T {
78
- const merged = { ...clientState }
79
-
80
- for (const conflict of conflicts) {
81
- switch (strategy) {
82
- case 'client':
83
- // Keep client value
84
- break
85
-
86
- case 'server':
87
- (merged as any)[conflict.property] = conflict.serverValue
88
- break
89
-
90
- case 'smart':
91
- // Smart resolution based on field type and context
92
- if (conflict.property === 'lastUpdated') {
93
- // Server timestamp wins
94
- (merged as any)[conflict.property] = conflict.serverValue
95
- } else if (typeof conflict.serverValue === 'number' && typeof conflict.clientValue === 'number') {
96
- // For numbers, use the higher value (e.g., counters)
97
- (merged as any)[conflict.property] = Math.max(conflict.serverValue, conflict.clientValue)
98
- } else {
99
- // Default to server for other types
100
- (merged as any)[conflict.property] = conflict.serverValue
101
- }
102
- break
103
- }
104
- }
105
-
106
- return merged
107
- }
108
-
109
- /**
110
- * Validate state integrity
111
- */
112
- static validateState<T>(hybridState: HybridState<T>): boolean {
113
- const currentChecksum = this.generateChecksum(hybridState.data)
114
- return currentChecksum === hybridState.validation.checksum
115
- }
116
-
117
- /**
118
- * Update validation after state change
119
- */
120
- static updateValidation<T>(
121
- hybridState: HybridState<T>,
122
- source: 'client' | 'server' | 'mount' = 'client'
123
- ): HybridState<T> {
124
- return {
125
- ...hybridState,
126
- validation: this.createValidation(hybridState.data, source),
127
- status: 'synced'
128
- }
129
- }
130
- }