create-fluxstack 1.10.1 → 1.12.1

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 (257) hide show
  1. package/.dockerignore +1 -2
  2. package/Dockerfile +8 -8
  3. package/LLMD/INDEX.md +64 -0
  4. package/LLMD/MAINTENANCE.md +197 -0
  5. package/LLMD/MIGRATION.md +156 -0
  6. package/LLMD/config/.gitkeep +1 -0
  7. package/LLMD/config/declarative-system.md +268 -0
  8. package/LLMD/config/environment-vars.md +327 -0
  9. package/LLMD/config/runtime-reload.md +401 -0
  10. package/LLMD/core/.gitkeep +1 -0
  11. package/LLMD/core/build-system.md +599 -0
  12. package/LLMD/core/framework-lifecycle.md +229 -0
  13. package/LLMD/core/plugin-system.md +451 -0
  14. package/LLMD/patterns/.gitkeep +1 -0
  15. package/LLMD/patterns/anti-patterns.md +297 -0
  16. package/LLMD/patterns/project-structure.md +264 -0
  17. package/LLMD/patterns/type-safety.md +440 -0
  18. package/LLMD/reference/.gitkeep +1 -0
  19. package/LLMD/reference/cli-commands.md +250 -0
  20. package/LLMD/reference/plugin-hooks.md +357 -0
  21. package/LLMD/reference/routing.md +39 -0
  22. package/LLMD/reference/troubleshooting.md +364 -0
  23. package/LLMD/resources/.gitkeep +1 -0
  24. package/LLMD/resources/controllers.md +465 -0
  25. package/LLMD/resources/live-components.md +703 -0
  26. package/LLMD/resources/live-rooms.md +482 -0
  27. package/LLMD/resources/live-upload.md +130 -0
  28. package/LLMD/resources/plugins-external.md +617 -0
  29. package/LLMD/resources/routes-eden.md +254 -0
  30. package/README.md +37 -17
  31. package/app/client/index.html +0 -1
  32. package/app/client/src/App.tsx +107 -150
  33. package/app/client/src/components/AppLayout.tsx +68 -0
  34. package/app/client/src/components/BackButton.tsx +13 -0
  35. package/app/client/src/components/DemoPage.tsx +20 -0
  36. package/app/client/src/components/LiveUploadWidget.tsx +204 -0
  37. package/app/client/src/lib/eden-api.ts +85 -60
  38. package/app/client/src/live/ChatDemo.tsx +107 -0
  39. package/app/client/src/live/CounterDemo.tsx +206 -0
  40. package/app/client/src/live/FormDemo.tsx +119 -0
  41. package/app/client/src/live/RoomChatDemo.tsx +161 -0
  42. package/app/client/src/live/UploadDemo.tsx +21 -0
  43. package/app/client/src/main.tsx +4 -1
  44. package/app/client/src/pages/ApiTestPage.tsx +108 -0
  45. package/app/client/src/pages/HomePage.tsx +76 -0
  46. package/app/server/app.ts +1 -4
  47. package/app/server/controllers/users.controller.ts +36 -44
  48. package/app/server/index.ts +25 -35
  49. package/app/server/live/LiveChat.ts +77 -0
  50. package/app/server/live/LiveCounter.ts +67 -0
  51. package/app/server/live/LiveForm.ts +63 -0
  52. package/app/server/live/LiveLocalCounter.ts +32 -0
  53. package/app/server/live/LiveRoomChat.ts +127 -0
  54. package/app/server/live/LiveUpload.ts +81 -0
  55. package/app/server/routes/index.ts +3 -1
  56. package/app/server/routes/room.routes.ts +117 -0
  57. package/app/server/routes/users.routes.ts +35 -27
  58. package/app/shared/types/index.ts +14 -2
  59. package/config/app.config.ts +2 -62
  60. package/config/client.config.ts +2 -95
  61. package/config/database.config.ts +2 -99
  62. package/config/fluxstack.config.ts +25 -45
  63. package/config/index.ts +57 -38
  64. package/config/monitoring.config.ts +2 -114
  65. package/config/plugins.config.ts +2 -80
  66. package/config/server.config.ts +2 -68
  67. package/config/services.config.ts +2 -130
  68. package/config/system/app.config.ts +29 -0
  69. package/config/system/build.config.ts +49 -0
  70. package/config/system/client.config.ts +68 -0
  71. package/config/system/database.config.ts +17 -0
  72. package/config/system/fluxstack.config.ts +114 -0
  73. package/config/{logger.config.ts → system/logger.config.ts} +3 -1
  74. package/config/system/monitoring.config.ts +114 -0
  75. package/config/system/plugins.config.ts +84 -0
  76. package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
  77. package/config/system/server.config.ts +68 -0
  78. package/config/system/services.config.ts +46 -0
  79. package/config/{system.config.ts → system/system.config.ts} +1 -1
  80. package/core/build/flux-plugins-generator.ts +325 -325
  81. package/core/build/index.ts +39 -27
  82. package/core/build/live-components-generator.ts +3 -3
  83. package/core/build/optimizer.ts +235 -235
  84. package/core/cli/command-registry.ts +6 -4
  85. package/core/cli/commands/build.ts +79 -0
  86. package/core/cli/commands/create.ts +54 -0
  87. package/core/cli/commands/dev.ts +101 -0
  88. package/core/cli/commands/help.ts +34 -0
  89. package/core/cli/commands/index.ts +34 -0
  90. package/core/cli/commands/make-plugin.ts +90 -0
  91. package/core/cli/commands/plugin-add.ts +197 -0
  92. package/core/cli/commands/plugin-deps.ts +2 -2
  93. package/core/cli/commands/plugin-list.ts +208 -0
  94. package/core/cli/commands/plugin-remove.ts +170 -0
  95. package/core/cli/generators/component.ts +769 -769
  96. package/core/cli/generators/controller.ts +1 -1
  97. package/core/cli/generators/index.ts +146 -146
  98. package/core/cli/generators/interactive.ts +227 -227
  99. package/core/cli/generators/plugin.ts +2 -2
  100. package/core/cli/generators/prompts.ts +82 -82
  101. package/core/cli/generators/route.ts +6 -6
  102. package/core/cli/generators/service.ts +2 -2
  103. package/core/cli/generators/template-engine.ts +4 -3
  104. package/core/cli/generators/types.ts +2 -2
  105. package/core/cli/generators/utils.ts +191 -191
  106. package/core/cli/index.ts +115 -686
  107. package/core/cli/plugin-discovery.ts +2 -2
  108. package/core/client/LiveComponentsProvider.tsx +60 -8
  109. package/core/client/api/eden.ts +183 -0
  110. package/core/client/api/index.ts +11 -0
  111. package/core/client/components/Live.tsx +104 -0
  112. package/core/client/fluxstack.ts +1 -9
  113. package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
  114. package/core/client/hooks/state-validator.ts +1 -1
  115. package/core/client/hooks/useAuth.ts +48 -48
  116. package/core/client/hooks/useChunkedUpload.ts +85 -35
  117. package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
  118. package/core/client/hooks/useLiveComponent.ts +800 -0
  119. package/core/client/hooks/useLiveUpload.ts +71 -0
  120. package/core/client/hooks/useRoom.ts +409 -0
  121. package/core/client/hooks/useRoomProxy.ts +382 -0
  122. package/core/client/index.ts +17 -68
  123. package/core/client/standalone-entry.ts +8 -0
  124. package/core/client/standalone.ts +74 -53
  125. package/core/client/state/createStore.ts +192 -192
  126. package/core/client/state/index.ts +14 -14
  127. package/core/config/index.ts +70 -291
  128. package/core/config/schema.ts +42 -723
  129. package/core/framework/client.ts +131 -131
  130. package/core/framework/index.ts +7 -7
  131. package/core/framework/server.ts +47 -40
  132. package/core/framework/types.ts +2 -2
  133. package/core/index.ts +23 -4
  134. package/core/live/ComponentRegistry.ts +3 -3
  135. package/core/live/types.ts +77 -0
  136. package/core/plugins/built-in/index.ts +134 -134
  137. package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
  138. package/core/plugins/built-in/live-components/index.ts +1 -1
  139. package/core/plugins/built-in/monitoring/index.ts +111 -47
  140. package/core/plugins/built-in/static/index.ts +1 -1
  141. package/core/plugins/built-in/swagger/index.ts +68 -265
  142. package/core/plugins/built-in/vite/index.ts +85 -185
  143. package/core/plugins/built-in/vite/vite-dev.ts +10 -16
  144. package/core/plugins/config.ts +9 -7
  145. package/core/plugins/dependency-manager.ts +31 -1
  146. package/core/plugins/discovery.ts +19 -7
  147. package/core/plugins/executor.ts +2 -2
  148. package/core/plugins/index.ts +203 -203
  149. package/core/plugins/manager.ts +27 -39
  150. package/core/plugins/module-resolver.ts +19 -8
  151. package/core/plugins/registry.ts +255 -19
  152. package/core/plugins/types.ts +20 -53
  153. package/core/server/framework.ts +66 -43
  154. package/core/server/index.ts +15 -15
  155. package/core/server/live/ComponentRegistry.ts +78 -71
  156. package/core/server/live/FileUploadManager.ts +23 -10
  157. package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
  158. package/core/server/live/LiveRoomManager.ts +261 -0
  159. package/core/server/live/RoomEventBus.ts +234 -0
  160. package/core/server/live/RoomStateManager.ts +172 -0
  161. package/core/server/live/StateSignature.ts +643 -643
  162. package/core/server/live/WebSocketConnectionManager.ts +30 -19
  163. package/core/server/live/auto-generated-components.ts +21 -9
  164. package/core/server/live/index.ts +14 -0
  165. package/core/server/live/websocket-plugin.ts +214 -67
  166. package/core/server/middleware/elysia-helpers.ts +7 -2
  167. package/core/server/middleware/errorHandling.ts +1 -1
  168. package/core/server/middleware/index.ts +31 -31
  169. package/core/server/plugins/database.ts +180 -180
  170. package/core/server/plugins/static-files-plugin.ts +69 -69
  171. package/core/server/plugins/swagger.ts +1 -1
  172. package/core/server/rooms/RoomBroadcaster.ts +357 -0
  173. package/core/server/rooms/RoomSystem.ts +463 -0
  174. package/core/server/rooms/index.ts +13 -0
  175. package/core/server/services/BaseService.ts +1 -1
  176. package/core/server/services/ServiceContainer.ts +1 -1
  177. package/core/server/services/index.ts +8 -8
  178. package/core/templates/create-project.ts +12 -12
  179. package/core/testing/index.ts +9 -9
  180. package/core/testing/setup.ts +73 -73
  181. package/core/types/api.ts +168 -168
  182. package/core/types/build.ts +219 -219
  183. package/core/types/config.ts +56 -26
  184. package/core/types/index.ts +4 -4
  185. package/core/types/plugin.ts +107 -107
  186. package/core/types/types.ts +353 -14
  187. package/core/utils/build-logger.ts +324 -324
  188. package/core/utils/config-schema.ts +480 -480
  189. package/core/utils/env.ts +2 -8
  190. package/core/utils/errors/codes.ts +114 -114
  191. package/core/utils/errors/handlers.ts +36 -1
  192. package/core/utils/errors/index.ts +49 -5
  193. package/core/utils/errors/middleware.ts +113 -113
  194. package/core/utils/helpers.ts +6 -16
  195. package/core/utils/index.ts +17 -17
  196. package/core/utils/logger/colors.ts +114 -114
  197. package/core/utils/logger/config.ts +13 -9
  198. package/core/utils/logger/formatter.ts +82 -82
  199. package/core/utils/logger/group-logger.ts +101 -101
  200. package/core/utils/logger/index.ts +6 -1
  201. package/core/utils/logger/stack-trace.ts +3 -1
  202. package/core/utils/logger/startup-banner.ts +82 -82
  203. package/core/utils/logger/winston-logger.ts +152 -152
  204. package/core/utils/monitoring/index.ts +211 -211
  205. package/core/utils/sync-version.ts +66 -66
  206. package/core/utils/version.ts +1 -1
  207. package/create-fluxstack.ts +8 -7
  208. package/package.json +12 -13
  209. package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
  210. package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
  211. package/plugins/crypto-auth/client/components/index.ts +11 -11
  212. package/plugins/crypto-auth/client/index.ts +11 -11
  213. package/plugins/crypto-auth/config/index.ts +1 -1
  214. package/plugins/crypto-auth/index.ts +4 -4
  215. package/plugins/crypto-auth/package.json +65 -65
  216. package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
  217. package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
  218. package/plugins/crypto-auth/server/index.ts +21 -21
  219. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
  220. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
  221. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
  222. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
  223. package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
  224. package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
  225. package/tsconfig.api-strict.json +16 -0
  226. package/tsconfig.json +48 -52
  227. package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
  228. package/types/global.d.ts +29 -29
  229. package/types/vitest.d.ts +8 -8
  230. package/vite.config.ts +38 -62
  231. package/vitest.config.live.ts +10 -9
  232. package/vitest.config.ts +29 -17
  233. package/app/client/README.md +0 -69
  234. package/app/client/SIMPLIFICATION.md +0 -140
  235. package/app/client/frontend-only.ts +0 -12
  236. package/app/client/src/live/FileUploadExample.tsx +0 -359
  237. package/app/client/src/live/MinimalLiveClock.tsx +0 -47
  238. package/app/client/src/live/QuickUploadTest.tsx +0 -193
  239. package/app/client/tsconfig.app.json +0 -45
  240. package/app/client/tsconfig.json +0 -7
  241. package/app/client/zustand-setup.md +0 -65
  242. package/app/server/backend-only.ts +0 -18
  243. package/app/server/live/LiveClockComponent.ts +0 -215
  244. package/app/server/live/LiveFileUploadComponent.ts +0 -77
  245. package/app/server/routes/env-test.ts +0 -110
  246. package/core/client/hooks/index.ts +0 -7
  247. package/core/client/hooks/useHybridLiveComponent.ts +0 -685
  248. package/core/client/hooks/useTypedLiveComponent.ts +0 -133
  249. package/core/client/hooks/useWebSocket.ts +0 -361
  250. package/core/config/env.ts +0 -546
  251. package/core/config/loader.ts +0 -522
  252. package/core/config/runtime-config.ts +0 -327
  253. package/core/config/validator.ts +0 -540
  254. package/core/server/backend-entry.ts +0 -51
  255. package/core/server/standalone.ts +0 -106
  256. package/core/utils/regenerate-files.ts +0 -69
  257. package/fluxstack.config.ts +0 -354
@@ -0,0 +1,703 @@
1
+ # Live Components
2
+
3
+ **Version:** 1.12.0 | **Updated:** 2025-02-09
4
+
5
+ ## Quick Facts
6
+
7
+ - Server-side state management with WebSocket sync
8
+ - **Reactive state Proxy** - `this.state.count++` auto-syncs
9
+ - Automatic state persistence and re-hydration
10
+ - Room-based event system for multi-user sync
11
+ - Type-safe client-server communication (FluxStackWebSocket)
12
+ - Built-in connection management and recovery
13
+ - **Client component links** - Ctrl+Click navigation
14
+
15
+ ## LiveComponent Class Structure (v1.12.0)
16
+
17
+ Server-side component extends `LiveComponent` with **static defaultState**:
18
+
19
+ ```typescript
20
+ // app/server/live/LiveCounter.ts
21
+ import { LiveComponent } from '@core/types/types'
22
+
23
+ // Componente Cliente (Ctrl+Click para navegar)
24
+ import type { CounterDemo as _Client } from '@client/src/live/CounterDemo'
25
+
26
+ export class LiveCounter extends LiveComponent<typeof LiveCounter.defaultState> {
27
+ static componentName = 'LiveCounter'
28
+ static defaultState = {
29
+ count: 0
30
+ }
31
+
32
+ // ✅ Reactive state - auto-syncs with frontend
33
+ async increment() {
34
+ this.state.count++
35
+ return { success: true, count: this.state.count }
36
+ }
37
+
38
+ async decrement() {
39
+ this.state.count--
40
+ return { success: true, count: this.state.count }
41
+ }
42
+
43
+ async reset() {
44
+ this.state.count = 0
45
+ return { success: true }
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### Key Changes in v1.12.0
51
+
52
+ 1. **Static defaultState inside class** - No external export needed
53
+ 2. **Reactive Proxy** - `this.state.count++` triggers sync automatically
54
+ 3. **No constructor needed** - Base class handles defaultState merge
55
+ 4. **Client link** - `import type { Demo as _Client }` enables Ctrl+Click
56
+ 5. **Type-safe WebSocket** - `FluxStackWebSocket` interface
57
+
58
+ ### With Room Events (Advanced)
59
+
60
+ ```typescript
61
+ import { LiveComponent, type FluxStackWebSocket } from '@core/types/types'
62
+
63
+ export class LiveCounter extends LiveComponent<typeof LiveCounter.defaultState> {
64
+ static componentName = 'LiveCounter'
65
+ static defaultState = {
66
+ count: 0,
67
+ lastUpdatedBy: null as string | null,
68
+ connectedUsers: 0
69
+ }
70
+ protected roomType = 'counter'
71
+
72
+ // Constructor only needed for room event subscriptions
73
+ constructor(
74
+ initialState: Partial<typeof LiveCounter.defaultState>,
75
+ ws: FluxStackWebSocket,
76
+ options?: { room?: string; userId?: string }
77
+ ) {
78
+ super(initialState, ws, options)
79
+
80
+ this.onRoomEvent<{ count: number }>('COUNT_CHANGED', (data) => {
81
+ this.setState({ count: data.count })
82
+ })
83
+ }
84
+
85
+ async increment() {
86
+ this.state.count++
87
+ this.emitRoomEventWithState('COUNT_CHANGED',
88
+ { count: this.state.count },
89
+ { count: this.state.count }
90
+ )
91
+ return { success: true, count: this.state.count }
92
+ }
93
+
94
+ destroy() {
95
+ super.destroy()
96
+ }
97
+ }
98
+ ```
99
+
100
+ ## Lifecycle Methods
101
+
102
+ ```typescript
103
+ export class MyComponent extends LiveComponent<typeof MyComponent.defaultState> {
104
+ static componentName = 'MyComponent'
105
+ static defaultState = {
106
+ // Define state here
107
+ }
108
+
109
+ // Constructor ONLY needed if:
110
+ // - Subscribing to room events
111
+ // - Custom initialization logic
112
+ // Otherwise, omit it entirely!
113
+
114
+ destroy() {
115
+ // Cleanup subscriptions, timers, etc.
116
+ super.destroy()
117
+ }
118
+ }
119
+ ```
120
+
121
+ ## State Management
122
+
123
+ ### Reactive State (v1.12.0) ✨
124
+
125
+ State mutations auto-sync with frontend via Proxy:
126
+
127
+ ```typescript
128
+ // ✅ Direct mutation - auto-syncs!
129
+ this.state.count++
130
+ this.state.message = 'Hello'
131
+
132
+ // No need for setState() on single property updates
133
+ ```
134
+
135
+ ### setState (Batch Updates)
136
+
137
+ Use `setState` for multiple properties at once (single emit):
138
+
139
+ ```typescript
140
+ // ✅ Batch update - one sync event
141
+ this.setState({
142
+ count: newCount,
143
+ lastUpdatedBy: userId,
144
+ updatedAt: new Date().toISOString()
145
+ })
146
+ ```
147
+
148
+ ### getSerializableState
149
+
150
+ Get current state for serialization:
151
+
152
+ ```typescript
153
+ const currentState = this.getSerializableState()
154
+ ```
155
+
156
+ ### State Persistence
157
+
158
+ State is automatically signed and persisted on client. On reconnection, state is re-hydrated:
159
+
160
+ ```typescript
161
+ // Automatic - no code needed
162
+ // Client stores signed state in localStorage
163
+ // On reconnect, sends signed state to server
164
+ // Server validates signature and restores component
165
+ ```
166
+
167
+ ## Room Events System
168
+
169
+ ### Subscribe to Room Events
170
+
171
+ ```typescript
172
+ constructor(initialState, ws, options) {
173
+ super(initialState, ws, options)
174
+
175
+ // Listen for room events
176
+ this.onRoomEvent<{ count: number }>('COUNT_CHANGED', (data) => {
177
+ this.setState({ count: data.count })
178
+ })
179
+
180
+ this.onRoomEvent<{ message: string }>('MESSAGE_SENT', (data) => {
181
+ // Handle message
182
+ })
183
+ }
184
+ ```
185
+
186
+ ### Emit Room Events
187
+
188
+ ```typescript
189
+ // Emit event to all room members
190
+ this.emitRoomEvent('MESSAGE_SENT', {
191
+ message: 'Hello',
192
+ userId: this.userId
193
+ })
194
+
195
+ // Emit event AND update local state
196
+ this.emitRoomEventWithState('COUNT_CHANGED',
197
+ { count: newCount }, // Event data
198
+ { count: newCount } // State update
199
+ )
200
+ ```
201
+
202
+ ### Room Subscription
203
+
204
+ Components automatically join rooms specified in options:
205
+
206
+ ```typescript
207
+ // Client-side
208
+ const counter = Live.use(LiveCounter, {
209
+ room: 'global-counter' // All instances in this room sync
210
+ })
211
+ ```
212
+
213
+ ## Actions
214
+
215
+ Actions are methods called from the client:
216
+
217
+ ```typescript
218
+ // Server-side
219
+ export class LiveForm extends LiveComponent<FormState> {
220
+ async submit() {
221
+ const { name, email } = this.state
222
+
223
+ if (!name || !email) {
224
+ throw new Error('Name and email required')
225
+ }
226
+
227
+ // Process submission
228
+ this.setState({ submitted: true })
229
+
230
+ return { success: true, data: { name, email } }
231
+ }
232
+
233
+ async validate() {
234
+ const errors: Record<string, string> = {}
235
+
236
+ if (!this.state.name) errors.name = 'Name required'
237
+ if (!this.state.email) errors.email = 'Email required'
238
+
239
+ return { valid: Object.keys(errors).length === 0, errors }
240
+ }
241
+ }
242
+ ```
243
+
244
+ ## Client-Side Integration
245
+
246
+ ### Provider Setup
247
+
248
+ Wrap app with LiveComponentsProvider:
249
+
250
+ ```typescript
251
+ // app/client/src/App.tsx
252
+ import { LiveComponentsProvider } from '@/core/client'
253
+
254
+ function App() {
255
+ return (
256
+ <LiveComponentsProvider
257
+ url="ws://localhost:3000"
258
+ autoConnect={true}
259
+ reconnectInterval={1000}
260
+ debug={true}
261
+ >
262
+ <AppContent />
263
+ </LiveComponentsProvider>
264
+ )
265
+ }
266
+ ```
267
+
268
+ ### Using Components
269
+
270
+ ```typescript
271
+ import { Live } from '@/core/client'
272
+ import { LiveCounter } from '@server/live/LiveCounter'
273
+
274
+ export function CounterDemo() {
275
+ // Mount component with options
276
+ const counter = Live.use(LiveCounter, {
277
+ room: 'global-counter',
278
+ initialState: LiveCounter.defaultState // ✅ Use static defaultState
279
+ })
280
+
281
+ // Access state
282
+ const count = counter.$state.count
283
+
284
+ // Check connection status
285
+ const isConnected = counter.$connected
286
+
287
+ // Check loading state
288
+ const isLoading = counter.$loading
289
+
290
+ // Call actions
291
+ const handleIncrement = async () => {
292
+ await counter.increment()
293
+ }
294
+
295
+ return (
296
+ <div>
297
+ <p>Count: {count}</p>
298
+ <p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>
299
+ <button onClick={handleIncrement} disabled={isLoading}>
300
+ Increment
301
+ </button>
302
+ </div>
303
+ )
304
+ }
305
+ ```
306
+
307
+ ### Field Binding
308
+
309
+ For form components, use `$field` helper:
310
+
311
+ ```typescript
312
+ const form = Live.use(LiveForm)
313
+
314
+ // Sync on blur
315
+ <input {...form.$field('name', { syncOn: 'blur' })} />
316
+
317
+ // Sync on change with debounce
318
+ <input {...form.$field('email', { syncOn: 'change', debounce: 500 })} />
319
+
320
+ // Manual sync
321
+ await form.$sync()
322
+ ```
323
+
324
+ ### Client API
325
+
326
+ ```typescript
327
+ // State access
328
+ counter.$state.count
329
+
330
+ // Connection status
331
+ counter.$connected
332
+
333
+ // Loading state
334
+ counter.$loading
335
+
336
+ // Call action
337
+ await counter.increment()
338
+
339
+ // Field binding (forms)
340
+ form.$field('fieldName', options)
341
+
342
+ // Manual sync
343
+ await form.$sync()
344
+ ```
345
+
346
+ ## Component Registry
347
+
348
+ Components are auto-discovered from `app/server/live/`:
349
+
350
+ ```typescript
351
+ // app/server/live/register-components.ts
352
+ import { componentRegistry } from '@core/server/live'
353
+
354
+ // Auto-discover all components in directory
355
+ await componentRegistry.autoDiscoverComponents('./app/server/live')
356
+
357
+ // Or manually register
358
+ componentRegistry.registerComponent({
359
+ name: 'MyComponent',
360
+ component: MyComponent,
361
+ initialState: defaultState
362
+ }, '1.0.0')
363
+ ```
364
+
365
+ ## WebSocket Connection Handling
366
+
367
+ ### Automatic Reconnection
368
+
369
+ Client automatically reconnects on disconnect:
370
+
371
+ ```typescript
372
+ <LiveComponentsProvider
373
+ reconnectInterval={1000} // Retry every 1 second
374
+ autoConnect={true}
375
+ >
376
+ ```
377
+
378
+ ### State Re-hydration
379
+
380
+ On reconnect, components restore previous state:
381
+
382
+ 1. Client stores signed state in localStorage
383
+ 2. On reconnect, sends signed state to server
384
+ 3. Server validates signature
385
+ 4. Component re-hydrated with previous state
386
+
387
+ No manual code needed - automatic.
388
+
389
+ ## Multi-User Synchronization
390
+
391
+ ### Room-Based Sync
392
+
393
+ All components in same room receive events:
394
+
395
+ ```typescript
396
+ // User A increments
397
+ await counter.increment()
398
+ // Emits COUNT_CHANGED to room
399
+
400
+ // User B's component receives event
401
+ this.onRoomEvent('COUNT_CHANGED', (data) => {
402
+ this.setState({ count: data.count })
403
+ })
404
+ // User B sees updated count
405
+ ```
406
+
407
+ ### User Tracking
408
+
409
+ Track connected users in room:
410
+
411
+ ```typescript
412
+ constructor(initialState, ws, options) {
413
+ super(initialState, ws, options)
414
+
415
+ // Notify room of new user
416
+ const newCount = this.state.connectedUsers + 1
417
+ this.emitRoomEventWithState('USER_COUNT_CHANGED',
418
+ { connectedUsers: newCount },
419
+ { connectedUsers: newCount }
420
+ )
421
+ }
422
+
423
+ destroy() {
424
+ // Notify room of user leaving
425
+ const newCount = Math.max(0, this.state.connectedUsers - 1)
426
+ this.emitRoomEvent('USER_COUNT_CHANGED', { connectedUsers: newCount })
427
+ super.destroy()
428
+ }
429
+ ```
430
+
431
+ ## Error Handling
432
+
433
+ ```typescript
434
+ // Server-side - throw errors
435
+ async submit() {
436
+ if (!this.state.email) {
437
+ throw new Error('Email required')
438
+ }
439
+ // Process...
440
+ }
441
+
442
+ // Client-side - catch errors
443
+ try {
444
+ await form.submit()
445
+ } catch (error) {
446
+ alert(error.message)
447
+ }
448
+ ```
449
+
450
+ ## Performance Monitoring
451
+
452
+ Built-in performance tracking:
453
+
454
+ ```typescript
455
+ // Automatic metrics collection
456
+ // - Render times
457
+ // - Action execution times
458
+ // - Error counts
459
+ // - Memory usage
460
+
461
+ // Access via registry
462
+ const health = componentRegistry.getComponentHealth(componentId)
463
+ // { status: 'healthy', metrics: {...} }
464
+ ```
465
+
466
+ ## Component Organization
467
+
468
+ ```
469
+ app/server/live/
470
+ ├── LiveCounter.ts # Counter component
471
+ ├── LiveForm.ts # Form component
472
+ ├── LiveChat.ts # Chat component
473
+ ├── LiveLocalCounter.ts # Local counter (no room)
474
+ └── register-components.ts # Registration
475
+
476
+ app/client/src/live/
477
+ ├── CounterDemo.tsx # Counter UI
478
+ ├── FormDemo.tsx # Form UI
479
+ ├── ChatDemo.tsx # Chat UI
480
+ └── ...
481
+ ```
482
+
483
+ Each server file contains:
484
+ - `static componentName` - Component identifier
485
+ - `static defaultState` - Initial state object
486
+ - Component class extending `LiveComponent`
487
+ - Client link via `import type { Demo as _Client }`
488
+
489
+ ## Testing Components
490
+
491
+ ```typescript
492
+ // tests/unit/live/LiveCounter.test.ts
493
+ import { describe, it, expect } from 'vitest'
494
+ import { LiveCounter, defaultState } from '@app/server/live/LiveCounter'
495
+
496
+ describe('LiveCounter', () => {
497
+ it('should increment count', async () => {
498
+ const mockWs = { send: vi.fn() }
499
+ const counter = new LiveCounter(defaultState, mockWs)
500
+
501
+ const result = await counter.increment()
502
+
503
+ expect(result.success).toBe(true)
504
+ expect(result.count).toBe(1)
505
+ expect(counter.state.count).toBe(1)
506
+ })
507
+ })
508
+ ```
509
+
510
+ ## Advanced: Dependencies
511
+
512
+ Register services for dependency injection:
513
+
514
+ ```typescript
515
+ // Register service
516
+ componentRegistry.registerService('database', () => db)
517
+
518
+ // Register dependencies
519
+ componentRegistry.registerDependencies('MyComponent', [
520
+ { name: 'database', version: '1.0.0', required: true, factory: () => db }
521
+ ])
522
+
523
+ // Component receives service
524
+ export class MyComponent extends LiveComponent<State> {
525
+ private database: any
526
+
527
+ setDatabase(db: any) {
528
+ this.database = db
529
+ }
530
+ }
531
+ ```
532
+
533
+ ## Critical Rules
534
+
535
+ **ALWAYS:**
536
+ - Define `static componentName` matching class name
537
+ - Define `static defaultState` inside the class
538
+ - Use `typeof ClassName.defaultState` for type parameter
539
+ - Call `super.destroy()` in destroy method if overriding
540
+ - Use `emitRoomEventWithState` for state changes in rooms
541
+ - Handle errors in actions (throw Error)
542
+ - Add client link: `import type { Demo as _Client } from '@client/...'`
543
+
544
+ **NEVER:**
545
+ - Export separate `defaultState` constant (use static)
546
+ - Create constructor just to call super() (not needed)
547
+ - Forget `static componentName` (breaks minification)
548
+ - Emit room events without subscribing first
549
+ - Store non-serializable data in state
550
+
551
+ **STATE UPDATES (v1.12.0):**
552
+ ```typescript
553
+ // ✅ Single property - use direct mutation
554
+ this.state.count++
555
+
556
+ // ✅ Multiple properties - use setState (one sync)
557
+ this.setState({ a: 1, b: 2, c: 3 })
558
+
559
+ // ❌ Don't use setState for single property (unnecessary)
560
+ this.setState({ count: this.state.count + 1 })
561
+ ```
562
+
563
+ ---
564
+
565
+ ## Live Upload (Chunked Upload via WebSocket)
566
+
567
+ This project includes a Live Component-based upload system that streams file chunks
568
+ over the Live Components WebSocket. The client uses a chunked upload hook; the server
569
+ tracks progress and assembles the file in `uploads/`.
570
+
571
+ ### Server: LiveUpload Component
572
+
573
+ Create server-side upload actions inside a Live Component. This example is the base
574
+ implementation used by the demos:
575
+
576
+ ```typescript
577
+ // app/server/live/LiveUpload.ts
578
+ import { LiveComponent } from '@core/types/types'
579
+ import { liveUploadDefaultState, type LiveUploadState } from '@app/shared'
580
+
581
+ export const defaultState: LiveUploadState = liveUploadDefaultState
582
+
583
+ export class LiveUpload extends LiveComponent<LiveUploadState> {
584
+ static defaultState = defaultState
585
+
586
+ constructor(initialState: Partial<typeof defaultState>, ws: any, options?: { room?: string; userId?: string }) {
587
+ super({ ...defaultState, ...initialState }, ws, options)
588
+ }
589
+
590
+ async startUpload(payload: { fileName: string; fileSize: number; fileType: string }) {
591
+ // Basic validation (example)
592
+ const normalized = payload.fileName.toLowerCase()
593
+ if (normalized.includes('..') || normalized.includes('/') || normalized.includes('\\')) {
594
+ throw new Error('Invalid file name')
595
+ }
596
+
597
+ const ext = normalized.includes('.') ? normalized.split('.').pop() || '' : ''
598
+ const blocked = ['exe', 'bat', 'cmd', 'sh', 'ps1', 'msi', 'jar']
599
+ if (ext && blocked.includes(ext)) {
600
+ throw new Error(`File extension not allowed: .${ext}`)
601
+ }
602
+
603
+ this.setState({
604
+ status: 'uploading',
605
+ progress: 0,
606
+ fileName: payload.fileName,
607
+ fileSize: payload.fileSize,
608
+ fileType: payload.fileType,
609
+ fileUrl: '',
610
+ bytesUploaded: 0,
611
+ totalBytes: payload.fileSize,
612
+ error: null
613
+ })
614
+
615
+ return { success: true }
616
+ }
617
+
618
+ async updateProgress(payload: { progress: number; bytesUploaded: number; totalBytes: number }) {
619
+ const progress = Math.max(0, Math.min(100, payload.progress))
620
+ this.setState({
621
+ progress,
622
+ bytesUploaded: payload.bytesUploaded,
623
+ totalBytes: payload.totalBytes
624
+ })
625
+
626
+ return { success: true, progress }
627
+ }
628
+
629
+ async completeUpload(payload: { fileUrl: string }) {
630
+ this.setState({
631
+ status: 'complete',
632
+ progress: 100,
633
+ fileUrl: payload.fileUrl,
634
+ error: null
635
+ })
636
+
637
+ return { success: true }
638
+ }
639
+
640
+ async failUpload(payload: { error: string }) {
641
+ this.setState({
642
+ status: 'error',
643
+ error: payload.error || 'Upload failed'
644
+ })
645
+
646
+ return { success: true }
647
+ }
648
+
649
+ async reset() {
650
+ this.setState({ ...defaultState })
651
+ return { success: true }
652
+ }
653
+ }
654
+ ```
655
+
656
+ ### Client: useLiveUpload + Widget
657
+
658
+ Use the client hook and UI widget to wire the upload to the Live Component:
659
+
660
+ ```typescript
661
+ // app/client/src/live/UploadDemo.tsx
662
+ import { useLiveUpload } from './useLiveUpload'
663
+ import { LiveUploadWidget } from '../components/LiveUploadWidget'
664
+
665
+ export function UploadDemo() {
666
+ const { live } = useLiveUpload()
667
+
668
+ return (
669
+ <LiveUploadWidget live={live} />
670
+ )
671
+ }
672
+ ```
673
+
674
+ ### Chunked Upload Flow
675
+
676
+ 1. Client calls `startUpload()` (Live Component action).
677
+ 2. Client streams file chunks over WebSocket with `useChunkedUpload`.
678
+ 3. Server assembles file in `uploads/` and returns `/uploads/...`.
679
+ 4. Client maps to `/api/uploads/...` for access.
680
+
681
+ ### Error Handling
682
+
683
+ - If an action throws, the error surfaces in `live.$error` on the client.
684
+ - The widget shows `localError || state.error || $error`.
685
+
686
+ ### Files Involved
687
+
688
+ **Server**
689
+ - `app/server/live/LiveUpload.ts`
690
+ - `core/server/live/FileUploadManager.ts` (chunk handling + file assembly)
691
+ - `core/server/live/websocket-plugin.ts` (upload message routing)
692
+
693
+ **Client**
694
+ - `core/client/hooks/useChunkedUpload.ts` (streaming chunks)
695
+ - `core/client/hooks/useLiveUpload.ts` (Live Component wrapper)
696
+ - `app/client/src/components/LiveUploadWidget.tsx` (UI)
697
+
698
+ ## Related
699
+
700
+ - [Project Structure](../patterns/project-structure.md)
701
+ - [Type Safety Patterns](../patterns/type-safety.md)
702
+ - [WebSocket Plugin](../core/plugin-system.md)
703
+ - [Live Upload](./live-upload.md)