create-fluxstack 1.13.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 (96) hide show
  1. package/LLMD/patterns/anti-patterns.md +100 -0
  2. package/LLMD/reference/routing.md +39 -39
  3. package/LLMD/resources/live-auth.md +20 -2
  4. package/LLMD/resources/live-components.md +300 -21
  5. package/LLMD/resources/live-logging.md +95 -33
  6. package/LLMD/resources/live-upload.md +59 -8
  7. package/app/client/.live-stubs/LiveAdminPanel.js +5 -0
  8. package/app/client/.live-stubs/LiveChat.js +7 -0
  9. package/app/client/.live-stubs/LiveCounter.js +9 -0
  10. package/app/client/.live-stubs/LiveForm.js +11 -0
  11. package/app/client/.live-stubs/LiveLocalCounter.js +8 -0
  12. package/app/client/.live-stubs/LiveRoomChat.js +10 -0
  13. package/app/client/.live-stubs/LiveTodoList.js +9 -0
  14. package/app/client/.live-stubs/LiveUpload.js +15 -0
  15. package/app/client/index.html +2 -2
  16. package/app/client/public/favicon.svg +46 -0
  17. package/app/client/src/App.tsx +13 -1
  18. package/app/client/src/assets/fluxstack-static.svg +46 -0
  19. package/app/client/src/assets/fluxstack.svg +183 -0
  20. package/app/client/src/components/AppLayout.tsx +146 -9
  21. package/app/client/src/components/BackButton.tsx +13 -13
  22. package/app/client/src/components/DemoPage.tsx +4 -4
  23. package/app/client/src/live/AuthDemo.tsx +23 -21
  24. package/app/client/src/live/ChatDemo.tsx +2 -2
  25. package/app/client/src/live/CounterDemo.tsx +12 -12
  26. package/app/client/src/live/FormDemo.tsx +2 -2
  27. package/app/client/src/live/LiveDebuggerPanel.tsx +779 -0
  28. package/app/client/src/live/RoomChatDemo.tsx +24 -16
  29. package/app/client/src/live/TodoListDemo.tsx +158 -0
  30. package/app/client/src/main.tsx +13 -13
  31. package/app/client/src/pages/ApiTestPage.tsx +6 -6
  32. package/app/client/src/pages/HomePage.tsx +80 -52
  33. package/app/server/auth/DevAuthProvider.ts +2 -2
  34. package/app/server/auth/JWTAuthProvider.example.ts +2 -2
  35. package/app/server/index.ts +2 -2
  36. package/app/server/live/LiveAdminPanel.ts +2 -1
  37. package/app/server/live/LiveChat.ts +78 -77
  38. package/app/server/live/LiveCounter.ts +1 -1
  39. package/app/server/live/LiveForm.ts +1 -0
  40. package/app/server/live/LiveLocalCounter.ts +38 -37
  41. package/app/server/live/LiveProtectedChat.ts +2 -1
  42. package/app/server/live/LiveRoomChat.ts +1 -0
  43. package/app/server/live/LiveTodoList.ts +110 -0
  44. package/app/server/live/LiveUpload.ts +1 -0
  45. package/app/server/live/register-components.ts +19 -19
  46. package/app/server/routes/room.routes.ts +1 -2
  47. package/config/system/runtime.config.ts +4 -0
  48. package/core/build/live-components-generator.ts +1 -1
  49. package/core/build/optimizer.ts +235 -235
  50. package/core/build/vite-plugins.ts +28 -0
  51. package/core/client/components/LiveDebugger.tsx +1324 -0
  52. package/core/client/hooks/useLiveUpload.ts +3 -4
  53. package/core/client/index.ts +41 -21
  54. package/core/framework/server.ts +1 -1
  55. package/core/plugins/built-in/index.ts +134 -134
  56. package/core/plugins/built-in/live-components/commands/create-live-component.ts +4 -0
  57. package/core/plugins/built-in/vite/index.ts +75 -21
  58. package/core/server/index.ts +14 -15
  59. package/core/server/live/auto-generated-components.ts +6 -3
  60. package/core/server/live/index.ts +95 -21
  61. package/core/server/live/websocket-plugin.ts +27 -862
  62. package/core/server/plugins/static-files-plugin.ts +179 -69
  63. package/core/types/build.ts +219 -219
  64. package/core/types/plugin.ts +107 -107
  65. package/core/types/types.ts +77 -890
  66. package/core/utils/logger/startup-banner.ts +82 -82
  67. package/core/utils/version.ts +6 -6
  68. package/create-fluxstack.ts +1 -1
  69. package/package.json +5 -1
  70. package/plugins/crypto-auth/index.ts +1 -1
  71. package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +2 -2
  72. package/vite.config.ts +40 -12
  73. package/app/client/src/assets/react.svg +0 -1
  74. package/core/client/LiveComponentsProvider.tsx +0 -531
  75. package/core/client/components/Live.tsx +0 -105
  76. package/core/client/hooks/AdaptiveChunkSizer.ts +0 -215
  77. package/core/client/hooks/state-validator.ts +0 -130
  78. package/core/client/hooks/useChunkedUpload.ts +0 -359
  79. package/core/client/hooks/useLiveChunkedUpload.ts +0 -87
  80. package/core/client/hooks/useLiveComponent.ts +0 -843
  81. package/core/client/hooks/useRoom.ts +0 -409
  82. package/core/client/hooks/useRoomProxy.ts +0 -382
  83. package/core/server/live/ComponentRegistry.ts +0 -1099
  84. package/core/server/live/FileUploadManager.ts +0 -282
  85. package/core/server/live/LiveComponentPerformanceMonitor.ts +0 -931
  86. package/core/server/live/LiveLogger.ts +0 -111
  87. package/core/server/live/LiveRoomManager.ts +0 -262
  88. package/core/server/live/RoomEventBus.ts +0 -234
  89. package/core/server/live/RoomStateManager.ts +0 -172
  90. package/core/server/live/SingleConnectionManager.ts +0 -0
  91. package/core/server/live/StateSignature.ts +0 -645
  92. package/core/server/live/WebSocketConnectionManager.ts +0 -709
  93. package/core/server/live/auth/LiveAuthContext.ts +0 -71
  94. package/core/server/live/auth/LiveAuthManager.ts +0 -304
  95. package/core/server/live/auth/index.ts +0 -19
  96. package/core/server/live/auth/types.ts +0 -179
@@ -277,6 +277,100 @@ if (import.meta.env.DEV) {
277
277
  }
278
278
  ```
279
279
 
280
+ ## Live Component Security Anti-Patterns
281
+
282
+ ### Never Omit publicActions
283
+
284
+ ```typescript
285
+ // ❌ WRONG - No publicActions = ALL remote actions blocked (secure by default)
286
+ export class MyComponent extends LiveComponent<State> {
287
+ static componentName = 'MyComponent'
288
+ static defaultState = { count: 0 }
289
+
290
+ async increment() { this.state.count++ } // Client CANNOT call this!
291
+ }
292
+
293
+ // ✅ CORRECT - Explicitly whitelist callable methods
294
+ export class MyComponent extends LiveComponent<State> {
295
+ static componentName = 'MyComponent'
296
+ static publicActions = ['increment'] as const // Only increment is callable
297
+ static defaultState = { count: 0 }
298
+
299
+ async increment() { this.state.count++ }
300
+
301
+ // Internal helper - not in publicActions, so not callable from client
302
+ private _recalculate() { /* ... */ }
303
+ }
304
+ ```
305
+
306
+ **Why**: Components without `publicActions` deny ALL remote actions. This is secure by default - if you forget, nothing is exposed rather than everything.
307
+
308
+ ### Never Include setValue Without Careful Consideration
309
+
310
+ ```typescript
311
+ // ❌ DANGEROUS - setValue allows client to set ANY state key
312
+ static publicActions = ['sendMessage', 'setValue'] as const
313
+ // Client can now do: component.setValue({ key: 'isAdmin', value: true })
314
+
315
+ // ✅ CORRECT - Only expose specific, safe actions
316
+ static publicActions = ['sendMessage', 'deleteMessage'] as const
317
+ ```
318
+
319
+ **Why**: `setValue` is a generic action that allows the client to modify any state property. Only include it if all state fields are safe for clients to modify.
320
+
321
+ ### Never Trust MIME Types Alone for Uploads
322
+
323
+ ```typescript
324
+ // ❌ WRONG - Only checking MIME type header (easily spoofed)
325
+ if (file.type === 'image/jpeg') {
326
+ // Accept file - but it could be an EXE with a fake MIME header!
327
+ }
328
+
329
+ // ✅ CORRECT - Framework validates magic bytes automatically
330
+ // FileUploadManager.validateContentMagicBytes() runs on completeUpload()
331
+ // No manual code needed - the framework handles this
332
+ ```
333
+
334
+ **Why**: MIME types come from the client and can be spoofed. The framework validates actual file content (magic bytes) against the claimed type.
335
+
336
+ ### Never Store Sensitive Data in State
337
+
338
+ ```typescript
339
+ // ❌ WRONG - Token goes to the client via STATE_UPDATE/STATE_DELTA
340
+ export class Chat extends LiveComponent<State> {
341
+ static defaultState = { messages: [], token: '' } // token synced to client!
342
+ static publicActions = ['connect'] as const
343
+
344
+ async connect(payload: { token: string }) {
345
+ this.state.token = payload.token // 💀 Visible in browser DevTools!
346
+ }
347
+ }
348
+
349
+ // ✅ CORRECT - Use $private for server-only data
350
+ export class Chat extends LiveComponent<State> {
351
+ static defaultState = { messages: [] as string[] }
352
+ static publicActions = ['connect'] as const
353
+
354
+ async connect(payload: { token: string }) {
355
+ this.$private.token = payload.token // 🔒 Never leaves the server
356
+ this.state.messages = await fetch(this.$private.token)
357
+ }
358
+ }
359
+ ```
360
+
361
+ **Why**: Everything in `state` is serialized and sent to the client via WebSocket. Use `$private` for tokens, API keys, internal IDs, or any data the client should not see.
362
+
363
+ ### Never Ignore Double Extensions
364
+
365
+ ```typescript
366
+ // ❌ WRONG - Only checking last extension
367
+ const ext = filename.split('.').pop() // Returns 'jpg' for 'malware.exe.jpg'
368
+
369
+ // ✅ CORRECT - Framework checks all intermediate extensions automatically
370
+ // FileUploadManager blocks files like 'malware.exe.jpg'
371
+ // No manual code needed - handled at framework level
372
+ ```
373
+
280
374
  ## Summary Table
281
375
 
282
376
  | Anti-Pattern | Impact | Solution |
@@ -288,6 +382,10 @@ if (import.meta.env.DEV) {
288
382
  | Deep relative imports | Fragile paths | Use aliases |
289
383
  | NPM plugins without whitelist | Security risk | Set PLUGINS_ALLOWED |
290
384
  | Business logic in routes | Unmaintainable | Use controllers |
385
+ | Missing `publicActions` | All actions blocked | Always define whitelist |
386
+ | Including `setValue` carelessly | Privilege escalation | Use specific actions |
387
+ | Sensitive data in `state` | Data leak to client | Use `$private` instead |
388
+ | Trusting MIME types alone | File disguise attacks | Framework validates magic bytes |
291
389
 
292
390
  ## Related
293
391
 
@@ -295,3 +393,5 @@ if (import.meta.env.DEV) {
295
393
  - [Type Safety](./type-safety.md)
296
394
  - [Plugin Security](../core/plugin-system.md)
297
395
  - [Routes with Eden Treaty](../resources/routes-eden.md)
396
+ - [Live Components](../resources/live-components.md)
397
+ - [Live Upload](../resources/live-upload.md)
@@ -1,39 +1,39 @@
1
- # Routing (React Router v7)
2
-
3
- FluxStack uses **React Router v7** via the `react-router` package for web routing.
4
-
5
- ## Where It Lives
6
-
7
- - Router provider: `app/client/src/main.tsx`
8
- - Routes and pages: `app/client/src/App.tsx`
9
- - Pages: `app/client/src/pages/*`
10
- - Shared layout: `app/client/src/components/AppLayout.tsx`
11
-
12
- ## Why `react-router` (not `react-router-dom`)
13
-
14
- In v7, the React Router team recommends using the core `react-router` package
15
- directly for web apps. The `react-router-dom` package remains as a compatibility
16
- re-export for older apps, but new projects should import from `react-router`.
17
-
18
- ## Example: Adding a New Route
19
-
20
- 1. Create a page in `app/client/src/pages/MyPage.tsx`
21
- 2. Add a route in `app/client/src/App.tsx`:
22
-
23
- ```tsx
24
- import { MyPage } from './pages/MyPage'
25
-
26
- <Route path="/my-page" element={<MyPage />} />
27
- ```
28
-
29
- 3. Add a nav link in `app/client/src/components/AppLayout.tsx`
30
-
31
- ## Current Demo Routes
32
-
33
- - `/` Home
34
- - `/counter` Live Counter
35
- - `/form` Live Form
36
- - `/upload` Live Upload
37
- - `/chat` Live Chat
38
- - `/api-test` Eden Treaty API Test
39
-
1
+ # Routing (React Router v7)
2
+
3
+ FluxStack uses **React Router v7** via the `react-router` package for web routing.
4
+
5
+ ## Where It Lives
6
+
7
+ - Router provider: `app/client/src/main.tsx`
8
+ - Routes and pages: `app/client/src/App.tsx`
9
+ - Pages: `app/client/src/pages/*`
10
+ - Shared layout: `app/client/src/components/AppLayout.tsx`
11
+
12
+ ## Why `react-router` (not `react-router-dom`)
13
+
14
+ In v7, the React Router team recommends using the core `react-router` package
15
+ directly for web apps. The `react-router-dom` package remains as a compatibility
16
+ re-export for older apps, but new projects should import from `react-router`.
17
+
18
+ ## Example: Adding a New Route
19
+
20
+ 1. Create a page in `app/client/src/pages/MyPage.tsx`
21
+ 2. Add a route in `app/client/src/App.tsx`:
22
+
23
+ ```tsx
24
+ import { MyPage } from './pages/MyPage'
25
+
26
+ <Route path="/my-page" element={<MyPage />} />
27
+ ```
28
+
29
+ 3. Add a nav link in `app/client/src/components/AppLayout.tsx`
30
+
31
+ ## Current Demo Routes
32
+
33
+ - `/` Home
34
+ - `/counter` Live Counter
35
+ - `/form` Live Form
36
+ - `/upload` Live Upload
37
+ - `/chat` Live Chat
38
+ - `/api-test` Eden Treaty API Test
39
+
@@ -4,6 +4,7 @@
4
4
 
5
5
  ## Quick Facts
6
6
 
7
+ - **`publicActions` is the foundation** - Only whitelisted methods can be called remotely
7
8
  - Declarative auth configuration via `static auth` and `static actionAuth`
8
9
  - Role-based access control (RBAC) with OR logic
9
10
  - Permission-based access control with AND logic
@@ -22,6 +23,7 @@ import type { LiveComponentAuth } from '@core/server/live/auth/types'
22
23
 
23
24
  export class ProtectedChat extends LiveComponent<typeof ProtectedChat.defaultState> {
24
25
  static componentName = 'ProtectedChat'
26
+ static publicActions = ['sendMessage'] as const // 🔒 REQUIRED
25
27
  static defaultState = {
26
28
  messages: [] as string[]
27
29
  }
@@ -47,6 +49,7 @@ import type { LiveComponentAuth } from '@core/server/live/auth/types'
47
49
 
48
50
  export class AdminPanel extends LiveComponent<typeof AdminPanel.defaultState> {
49
51
  static componentName = 'AdminPanel'
52
+ static publicActions = ['deleteUser'] as const // 🔒 REQUIRED
50
53
  static defaultState = {
51
54
  users: [] as { id: string; name: string; role: string }[]
52
55
  }
@@ -74,6 +77,7 @@ import type { LiveComponentAuth } from '@core/server/live/auth/types'
74
77
 
75
78
  export class ContentEditor extends LiveComponent<typeof ContentEditor.defaultState> {
76
79
  static componentName = 'ContentEditor'
80
+ static publicActions = ['editContent', 'saveContent'] as const // 🔒 REQUIRED
77
81
  static defaultState = {
78
82
  content: ''
79
83
  }
@@ -95,6 +99,7 @@ import type { LiveComponentAuth, LiveActionAuthMap } from '@core/server/live/aut
95
99
 
96
100
  export class ModerationPanel extends LiveComponent<typeof ModerationPanel.defaultState> {
97
101
  static componentName = 'ModerationPanel'
102
+ static publicActions = ['getReports', 'deleteReport', 'banUser'] as const // 🔒 REQUIRED
98
103
  static defaultState = {
99
104
  reports: [] as any[]
100
105
  }
@@ -104,7 +109,7 @@ export class ModerationPanel extends LiveComponent<typeof ModerationPanel.defaul
104
109
  required: true
105
110
  }
106
111
 
107
- // Per-action auth
112
+ // Per-action auth (works together with publicActions)
108
113
  static actionAuth: LiveActionAuthMap = {
109
114
  deleteReport: { permissions: ['reports.delete'] },
110
115
  banUser: { roles: ['admin', 'moderator'] }
@@ -382,7 +387,7 @@ For testing, a `DevAuthProvider` with simple tokens is available:
382
387
  │ 1. WebSocket connect → store authContext on ws.data │
383
388
  │ 2. AUTH message → liveAuthManager.authenticate() │
384
389
  │ 3. COMPONENT_MOUNT → check static auth config │
385
- │ 4. CALL_ACTION → check static actionAuth config
390
+ │ 4. CALL_ACTION → check blocklist → publicActions → actionAuth │
386
391
  │ 5. Component has access to this.$auth │
387
392
  └─────────────────────────────────────────────────────────────┘
388
393
  ```
@@ -416,9 +421,21 @@ interface LiveAuthCredentials {
416
421
  }
417
422
  ```
418
423
 
424
+ ## Security Layers (Action Execution Order)
425
+
426
+ When a client calls an action, the server checks in this order:
427
+
428
+ 1. **Blocklist** - Internal methods (destroy, setState, emit, etc.) are always blocked
429
+ 2. **Private methods** - Methods starting with `_` or `#` are blocked
430
+ 3. **publicActions** - Action must be in the whitelist (mandatory, no fallback)
431
+ 4. **actionAuth** - Per-action role/permission check (if defined)
432
+ 5. **Method exists** - Action must exist on the component instance
433
+ 6. **Object.prototype** - Blocks toString, valueOf, hasOwnProperty
434
+
419
435
  ## Critical Rules
420
436
 
421
437
  **ALWAYS:**
438
+ - Define `static publicActions` listing all client-callable methods (MANDATORY)
422
439
  - Define `static auth` for protected components
423
440
  - Define `static actionAuth` for protected actions
424
441
  - Use `$auth.hasRole()` / `$auth.hasPermission()` in action logic
@@ -426,6 +443,7 @@ interface LiveAuthCredentials {
426
443
  - Handle `AUTH_DENIED` errors in client UI
427
444
 
428
445
  **NEVER:**
446
+ - Omit `publicActions` (component will deny ALL remote actions)
429
447
  - Store sensitive data in component state
430
448
  - Trust client-side auth checks alone (always verify server-side)
431
449
  - Expose tokens in error messages