create-fluxstack 1.19.0 → 1.20.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 (37) hide show
  1. package/LLMD/INDEX.md +1 -1
  2. package/LLMD/MAINTENANCE.md +197 -197
  3. package/LLMD/MIGRATION.md +44 -1
  4. package/LLMD/agent.md +20 -7
  5. package/LLMD/config/declarative-system.md +268 -268
  6. package/LLMD/config/environment-vars.md +3 -6
  7. package/LLMD/config/runtime-reload.md +401 -401
  8. package/LLMD/core/build-system.md +599 -599
  9. package/LLMD/core/framework-lifecycle.md +249 -229
  10. package/LLMD/core/plugin-system.md +154 -100
  11. package/LLMD/patterns/anti-patterns.md +397 -397
  12. package/LLMD/patterns/project-structure.md +264 -264
  13. package/LLMD/patterns/type-safety.md +61 -5
  14. package/LLMD/reference/cli-commands.md +31 -7
  15. package/LLMD/reference/plugin-hooks.md +4 -2
  16. package/LLMD/reference/troubleshooting.md +364 -364
  17. package/LLMD/resources/controllers.md +465 -465
  18. package/LLMD/resources/live-auth.md +178 -1
  19. package/LLMD/resources/live-binary-delta.md +3 -1
  20. package/LLMD/resources/live-components.md +1192 -1041
  21. package/LLMD/resources/live-logging.md +3 -1
  22. package/LLMD/resources/live-rooms.md +1 -1
  23. package/LLMD/resources/live-upload.md +228 -181
  24. package/LLMD/resources/plugins-external.md +8 -7
  25. package/LLMD/resources/rest-auth.md +290 -290
  26. package/LLMD/resources/routes-eden.md +254 -254
  27. package/app/client/.live-stubs/LiveAdminPanel.js +15 -0
  28. package/app/client/.live-stubs/LiveCounter.js +9 -0
  29. package/app/client/.live-stubs/LiveForm.js +11 -0
  30. package/app/client/.live-stubs/LiveLocalCounter.js +8 -0
  31. package/app/client/.live-stubs/LivePingPong.js +10 -0
  32. package/app/client/.live-stubs/LiveRoomChat.js +11 -0
  33. package/app/client/.live-stubs/LiveSharedCounter.js +10 -0
  34. package/app/client/.live-stubs/LiveUpload.js +15 -0
  35. package/app/server/live/auto-generated-components.ts +1 -1
  36. package/core/utils/version.ts +6 -6
  37. package/package.json +108 -108
@@ -1,6 +1,8 @@
1
1
  # Live Logging
2
2
 
3
- **Version:** 1.12.1 | **Updated:** 2025-02-22
3
+ **Version:** 1.19.0 | **Updated:** 2026-04-14
4
+
5
+ > **Nota:** O recurso `static logging` \u00e9 uma feature do `@fluxstack/live`, dispon\u00edvel mas n\u00e3o usada nos componentes de exemplo do FluxStack app.
4
6
 
5
7
  ## Quick Facts
6
8
 
@@ -1,6 +1,6 @@
1
1
  # Live Room System
2
2
 
3
- **Version:** 2.0.0 | **Updated:** 2025-03-11
3
+ **Version:** 0.7.2 | **Updated:** 2026-04-14
4
4
 
5
5
  ## Quick Facts
6
6
 
@@ -1,181 +1,228 @@
1
- # Live Upload (Chunked Upload via WebSocket)
2
-
3
- **Version:** 1.14.0 | **Updated:** 2025-02-21
4
-
5
- ## Overview
6
-
7
- FluxStack supports chunked file upload over the Live Components WebSocket. The
8
- server tracks progress and assembles the file in `uploads/`. The client streams
9
- chunks without loading the entire file into memory.
10
-
11
- ## Security Features
12
-
13
- The upload system includes multiple layers of security:
14
-
15
- - **MIME type allowlist** - Only safe file types accepted (images, PDF, text, JSON, archives)
16
- - **Extension blocklist** - 31 dangerous extensions blocked (.exe, .bat, .sh, .dll, etc.)
17
- - **Double extension prevention** - Detects `malware.exe.jpg` style attacks
18
- - **Magic bytes validation** - Verifies actual file content matches claimed MIME type
19
- - **Per-user upload quota** - 500MB/day per user to prevent disk exhaustion
20
- - **File size limit** - 50MB max per file
21
- - **Filename sanitization** - Path traversal prevention via `path.basename()`
22
- - **Stale upload cleanup** - Abandoned uploads removed after 60 seconds
23
-
24
- ## Server: LiveUpload Component
25
-
26
- ```typescript
27
- // app/server/live/LiveUpload.ts
28
- import { LiveComponent } from '@core/types/types'
29
- import { liveUploadDefaultState, type LiveUploadState } from '@app/shared'
30
-
31
- export const defaultState: LiveUploadState = liveUploadDefaultState
32
-
33
- export class LiveUpload extends LiveComponent<LiveUploadState> {
34
- static componentName = 'LiveUpload'
35
- static publicActions = ['startUpload', 'updateProgress', 'completeUpload', 'failUpload', 'reset'] as const
36
- static defaultState = defaultState
37
-
38
- constructor(initialState: Partial<typeof defaultState>, ws: any, options?: { room?: string; userId?: string }) {
39
- super({ ...defaultState, ...initialState }, ws, options)
40
- }
41
-
42
- async startUpload(payload: { fileName: string; fileSize: number; fileType: string }) {
43
- const normalized = payload.fileName.toLowerCase()
44
- if (normalized.includes('..') || normalized.includes('/') || normalized.includes('\\')) {
45
- throw new Error('Invalid file name')
46
- }
47
-
48
- const ext = normalized.includes('.') ? normalized.split('.').pop() || '' : ''
49
- const blocked = ['exe', 'bat', 'cmd', 'sh', 'ps1', 'msi', 'jar']
50
- if (ext && blocked.includes(ext)) {
51
- throw new Error(`File extension not allowed: .${ext}`)
52
- }
53
-
54
- this.setState({
55
- status: 'uploading',
56
- progress: 0,
57
- fileName: payload.fileName,
58
- fileSize: payload.fileSize,
59
- fileType: payload.fileType,
60
- fileUrl: '',
61
- bytesUploaded: 0,
62
- totalBytes: payload.fileSize,
63
- error: null
64
- })
65
-
66
- return { success: true }
67
- }
68
-
69
- async updateProgress(payload: { progress: number; bytesUploaded: number; totalBytes: number }) {
70
- const progress = Math.max(0, Math.min(100, payload.progress))
71
- this.setState({
72
- progress,
73
- bytesUploaded: payload.bytesUploaded,
74
- totalBytes: payload.totalBytes
75
- })
76
-
77
- return { success: true, progress }
78
- }
79
-
80
- async completeUpload(payload: { fileUrl: string }) {
81
- this.setState({
82
- status: 'complete',
83
- progress: 100,
84
- fileUrl: payload.fileUrl,
85
- error: null
86
- })
87
-
88
- return { success: true }
89
- }
90
-
91
- async failUpload(payload: { error: string }) {
92
- this.setState({
93
- status: 'error',
94
- error: payload.error || 'Upload failed'
95
- })
96
-
97
- return { success: true }
98
- }
99
-
100
- async reset() {
101
- this.setState({ ...defaultState })
102
- return { success: true }
103
- }
104
- }
105
- ```
106
-
107
- ## Client: useLiveUpload + Widget
108
-
109
- ```typescript
110
- // app/client/src/live/UploadDemo.tsx
111
- import { useLiveUpload } from './useLiveUpload'
112
- import { LiveUploadWidget } from '../components/LiveUploadWidget'
113
-
114
- export function UploadDemo() {
115
- const { live } = useLiveUpload()
116
-
117
- return (
118
- <LiveUploadWidget live={live} />
119
- )
120
- }
121
- ```
122
-
123
- ## Chunked Upload Flow
124
-
125
- 1. Client calls `startUpload()` (Live Component action).
126
- 2. Client streams file chunks over WebSocket with `useChunkedUpload`.
127
- 3. Server receives chunks and validates size/count.
128
- 4. On completion, server validates **magic bytes** against claimed MIME type.
129
- 5. Server assembles file in `uploads/` with UUID filename and returns `/uploads/...`.
130
- 6. Client maps to `/api/uploads/...` for access.
131
-
132
- ## Magic Bytes Validation
133
-
134
- The server validates actual file content against known magic byte signatures before assembling:
135
-
136
- | MIME Type | Magic Bytes |
137
- |-----------|-------------|
138
- | `image/jpeg` | `FF D8 FF` |
139
- | `image/png` | `89 50 4E 47 0D 0A 1A 0A` |
140
- | `image/gif` | `47 49 46 38 37 61` or `47 49 46 38 39 61` |
141
- | `image/webp` | `52 49 46 46` (RIFF header) |
142
- | `application/pdf` | `25 50 44 46` (%PDF) |
143
- | `application/zip` | `50 4B 03 04` or `50 4B 05 06` |
144
- | `application/gzip` | `1F 8B` |
145
-
146
- Text-based types (text/plain, text/csv, application/json, image/svg+xml) skip binary validation.
147
-
148
- ## Per-User Upload Quotas
149
-
150
- Each authenticated user has a daily upload quota (default: 500MB/day):
151
-
152
- - Quota is checked before upload starts
153
- - Quota is reserved when upload begins (even if upload doesn't complete)
154
- - Quotas reset daily
155
- - Anonymous uploads (no userId) bypass quota checks
156
-
157
- ```typescript
158
- // Check user's remaining quota
159
- const usage = fileUploadManager.getUserUploadUsage(userId)
160
- // { used: 104857600, limit: 524288000, remaining: 419430400 }
161
- ```
162
-
163
- ## Error Handling
164
-
165
- - If an action throws, the error surfaces in `live.$error` on the client.
166
- - The widget shows `localError || state.error || $error`.
167
- - Magic bytes validation failure: `"File content does not match claimed type 'image/jpeg'"`
168
- - Quota exceeded: `"Upload quota exceeded for user"`
169
- - Double extension: `"Suspicious double extension detected: .exe in malware.exe.jpg"`
170
-
171
- ## Files Involved
172
-
173
- **Server**
174
- - `app/server/live/LiveUpload.ts`
175
- - `core/server/live/FileUploadManager.ts` (chunk handling, magic bytes, quotas, file assembly)
176
- - `core/server/live/websocket-plugin.ts` (upload message routing, userId passthrough)
177
-
178
- **Client**
179
- - `core/client/hooks/useChunkedUpload.ts` (streaming chunks)
180
- - `core/client/hooks/useLiveUpload.ts` (Live Component wrapper)
181
- - `app/client/src/components/LiveUploadWidget.tsx` (UI)
1
+ # Live Upload (Chunked Upload via WebSocket)
2
+
3
+ **Version:** @fluxstack/live 0.7.2 | **Updated:** 2026-04-14
4
+
5
+ ## Overview
6
+
7
+ FluxStack supports chunked file upload over the Live Components WebSocket. The
8
+ server tracks progress and assembles the file in `uploads/`. The client streams
9
+ chunks without loading the entire file into memory.
10
+
11
+ ## Security Features
12
+
13
+ ### Implemented in LiveUpload.ts (app example)
14
+
15
+ The `LiveUpload` component in `app/server/live/LiveUpload.ts` implements these validations directly:
16
+
17
+ - **Filename length validation** - Must be 1-255 characters
18
+ - **Path traversal prevention** - Blocks `..`, `/`, `\` in filenames
19
+ - **Control character blocking** - Rejects null bytes and control chars (`\x00-\x1f`)
20
+ - **Windows reserved name blocking** - Rejects CON, PRN, AUX, NUL, COM1-4, LPT1-3
21
+ - **No extension blocking** - The app example does NOT block file extensions by default (comment says "configure per your application needs")
22
+
23
+ ### Available in @fluxstack/live framework (FileUploadManager)
24
+
25
+ The `FileUploadManager` in `@fluxstack/live` core provides additional security features that applications can leverage. These are **available in the framework** but are handled at the framework transport layer, not in the `LiveUpload` component itself:
26
+
27
+ - **MIME type allowlist** - Configurable list of safe file types (default: images, PDF, text, JSON, archives)
28
+ - **Extension blocklist** - 31 dangerous extensions blocked (.exe, .bat, .sh, .dll, .ps1, .vbs, etc.)
29
+ - **Double extension prevention** - Detects `malware.exe.jpg` style attacks
30
+ - **Magic bytes validation** - Verifies actual file content matches claimed MIME type on completion
31
+ - **Per-user upload quota** - Configurable daily limit per user (default: 500MB/day)
32
+ - **File size limit** - Configurable max per file (default: 50MB)
33
+ - **Filename sanitization** - Path traversal prevention via `path.basename()`
34
+ - **Stale upload cleanup** - Abandoned uploads removed automatically (chunk timeout x2)
35
+ - **Custom file assembly** - Pluggable `assembleFile` handler for custom storage backends
36
+
37
+ ### FileUploadManager Configuration
38
+
39
+ ```typescript
40
+ // Configuration options available in @fluxstack/live
41
+ interface FileUploadConfig {
42
+ maxUploadSize?: number // Default: 50MB
43
+ chunkTimeout?: number // Default: 30s
44
+ maxBytesPerUser?: number // Default: 500MB/day
45
+ quotaResetInterval?: number // Default: 24h
46
+ allowedTypes?: string[] // Default: images, PDF, text, JSON, archives
47
+ blockedExtensions?: string[] // Default: 31 dangerous extensions
48
+ uploadsDir?: string // Default: './uploads'
49
+ assembleFile?: (upload: ActiveUpload) => Promise<string> // Custom handler
50
+ }
51
+ ```
52
+
53
+ ## Server: LiveUpload Component
54
+
55
+ ```typescript
56
+ // app/server/live/LiveUpload.ts
57
+ import { LiveComponent } from '@core/types/types'
58
+
59
+ export class LiveUpload extends LiveComponent<typeof LiveUpload.defaultState> {
60
+ static componentName = 'LiveUpload'
61
+ static publicActions = ['startUpload', 'updateProgress', 'completeUpload', 'failUpload', 'reset'] as const
62
+ static defaultState = {
63
+ status: 'idle' as 'idle' | 'uploading' | 'complete' | 'error',
64
+ progress: 0,
65
+ fileName: '',
66
+ fileSize: 0,
67
+ fileType: '',
68
+ fileUrl: '',
69
+ bytesUploaded: 0,
70
+ totalBytes: 0,
71
+ error: null as string | null
72
+ }
73
+
74
+ async startUpload(payload: { fileName: string; fileSize: number; fileType: string }) {
75
+ const fileName = payload.fileName
76
+
77
+ // Validate filename length
78
+ if (!fileName || fileName.length > 255) {
79
+ throw new Error('Invalid file name: must be 1-255 characters')
80
+ }
81
+
82
+ // Block path traversal, null bytes, and control characters
83
+ if (/[\x00-\x1f]/.test(fileName) || fileName.includes('..') || fileName.includes('/') || fileName.includes('\\')) {
84
+ throw new Error('Invalid file name: contains forbidden characters')
85
+ }
86
+
87
+ // Block Windows reserved names
88
+ const baseName = fileName.split('.')[0].toUpperCase()
89
+ const reserved = ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'LPT1', 'LPT2', 'LPT3']
90
+ if (reserved.includes(baseName)) {
91
+ throw new Error('Invalid file name: reserved name')
92
+ }
93
+
94
+ // All file types allowed - no extension blocking in this example
95
+ // Security note: Configure allowed extensions per your application needs
96
+
97
+ this.setState({
98
+ status: 'uploading',
99
+ progress: 0,
100
+ fileName: payload.fileName,
101
+ fileSize: payload.fileSize,
102
+ fileType: payload.fileType,
103
+ fileUrl: '',
104
+ bytesUploaded: 0,
105
+ totalBytes: payload.fileSize,
106
+ error: null
107
+ })
108
+
109
+ return { success: true }
110
+ }
111
+
112
+ async updateProgress(payload: { progress: number; bytesUploaded: number; totalBytes: number }) {
113
+ const progress = Math.max(0, Math.min(100, payload.progress))
114
+ this.setState({
115
+ progress,
116
+ bytesUploaded: payload.bytesUploaded,
117
+ totalBytes: payload.totalBytes
118
+ })
119
+
120
+ return { success: true, progress }
121
+ }
122
+
123
+ async completeUpload(payload: { fileUrl: string }) {
124
+ this.setState({
125
+ status: 'complete',
126
+ progress: 100,
127
+ fileUrl: payload.fileUrl,
128
+ error: null
129
+ })
130
+
131
+ return { success: true }
132
+ }
133
+
134
+ async failUpload(payload: { error: string }) {
135
+ this.setState({
136
+ status: 'error',
137
+ error: payload.error || 'Upload failed'
138
+ })
139
+
140
+ return { success: true }
141
+ }
142
+
143
+ async reset() {
144
+ this.setState({ ...LiveUpload.defaultState })
145
+ return { success: true }
146
+ }
147
+ }
148
+ ```
149
+
150
+ ## Client: useLiveUpload + Widget
151
+
152
+ ```typescript
153
+ // app/client/src/live/UploadDemo.tsx
154
+ import { useLiveUpload } from './useLiveUpload'
155
+ import { LiveUploadWidget } from '../components/LiveUploadWidget'
156
+
157
+ export function UploadDemo() {
158
+ const { live } = useLiveUpload()
159
+
160
+ return (
161
+ <LiveUploadWidget live={live} />
162
+ )
163
+ }
164
+ ```
165
+
166
+ ## Chunked Upload Flow
167
+
168
+ 1. Client calls `startUpload()` (Live Component action) -- validates filename.
169
+ 2. Client streams file chunks over WebSocket with `useChunkedUpload`.
170
+ 3. Server (`FileUploadManager`) receives chunks and validates size/count.
171
+ 4. On completion, `FileUploadManager` validates **magic bytes** against claimed MIME type.
172
+ 5. Server assembles file in `uploads/` with UUID filename and returns `/uploads/...`.
173
+ 6. Client maps to `/api/uploads/...` for access.
174
+
175
+ ## Magic Bytes Validation (framework level)
176
+
177
+ The `FileUploadManager` in `@fluxstack/live` validates actual file content against known magic byte signatures before assembling. This happens at the framework transport layer, not in the `LiveUpload` component:
178
+
179
+ | MIME Type | Magic Bytes |
180
+ |-----------|-------------|
181
+ | `image/jpeg` | `FF D8 FF` |
182
+ | `image/png` | `89 50 4E 47 0D 0A 1A 0A` |
183
+ | `image/gif` | `47 49 46 38 37 61` or `47 49 46 38 39 61` |
184
+ | `image/webp` | `52 49 46 46` (RIFF header) |
185
+ | `application/pdf` | `25 50 44 46` (%PDF) |
186
+ | `application/zip` | `50 4B 03 04` or `50 4B 05 06` |
187
+ | `application/gzip` | `1F 8B` |
188
+
189
+ Text-based types (text/plain, text/csv, application/json, image/svg+xml) skip binary validation.
190
+
191
+ ## Per-User Upload Quotas (framework level)
192
+
193
+ Each authenticated user has a daily upload quota (default: 500MB/day), managed by `FileUploadManager`:
194
+
195
+ - Quota is checked before upload starts
196
+ - Quota is reserved when upload begins (even if upload doesn't complete)
197
+ - Quotas reset daily (configurable interval)
198
+ - Anonymous uploads (no userId) bypass quota checks
199
+
200
+ ```typescript
201
+ // Check user's remaining quota (framework API)
202
+ const usage = fileUploadManager.getUserUploadUsage(userId)
203
+ // { used: 104857600, limit: 524288000, remaining: 419430400 }
204
+ ```
205
+
206
+ > **Note:** Quota enforcement happens in `FileUploadManager.startUpload()`, which is called by the framework's WebSocket plugin before reaching the `LiveUpload` component.
207
+
208
+ ## Error Handling
209
+
210
+ - If an action throws, the error surfaces in `live.$error` on the client.
211
+ - The widget shows `localError || state.error || $error`.
212
+ - Magic bytes validation failure: `"File content does not match claimed type 'image/jpeg'"`
213
+ - Quota exceeded: `"Upload quota exceeded for user"`
214
+ - Double extension: `"Suspicious double extension detected: .exe in malware.exe.jpg"`
215
+ - Path traversal: `"Invalid file name: contains forbidden characters"`
216
+ - Reserved name: `"Invalid file name: reserved name"`
217
+
218
+ ## Files Involved
219
+
220
+ **Server**
221
+ - `app/server/live/LiveUpload.ts` -- Component with state tracking and filename validation
222
+ - `core/server/live/FileUploadManager.ts` (from `@fluxstack/live`) -- chunk handling, magic bytes, quotas, extension blocking, file assembly
223
+ - `core/server/live/websocket-plugin.ts` -- upload message routing, userId passthrough
224
+
225
+ **Client**
226
+ - `core/client/hooks/useChunkedUpload.ts` -- streaming chunks over WebSocket
227
+ - `core/client/hooks/useLiveUpload.ts` -- Live Component wrapper hook
228
+ - `app/client/src/components/LiveUploadWidget.tsx` -- UI widget
@@ -1,6 +1,6 @@
1
1
  # External Plugins
2
2
 
3
- **Version:** 1.11.0 | **Updated:** 2025-02-08
3
+ **Version:** 1.19.0 | **Updated:** 2026-04-14
4
4
 
5
5
  ## Quick Facts
6
6
 
@@ -9,7 +9,8 @@
9
9
  - Use lifecycle hooks for integration
10
10
  - Support declarative configuration system
11
11
  - Can add CLI commands, routes, and middleware
12
- - Auto-discovered and loaded at startup
12
+ - **Registration is via `.use()` (no auto-discovery for built-in plugins)**
13
+ - Plugin types come from **`@fluxstack/plugin-kit` 0.4.0** (external package); `core/plugins/types.ts` re-exports with specialized generics
13
14
 
14
15
  ## Plugin Structure
15
16
 
@@ -584,13 +585,13 @@ export const cryptoAuthPlugin: FluxStack.Plugin = {
584
585
  }
585
586
  ```
586
587
 
587
- ## Plugin Discovery
588
+ ## Plugin Registration
588
589
 
589
- Plugins are auto-discovered from:
590
+ As of v1.19.0, **built-in plugins are registered explicitly via `.use()`** — there is no auto-discovery for them. Project and NPM plugins follow a layered model:
590
591
 
591
- 1. `plugins/` directory (project plugins)
592
- 2. `node_modules/@fluxstack/*-plugin` (npm plugins)
593
- 3. Whitelisted in `config/system/plugins.config.ts`
592
+ 1. **Built-in plugins** (`core/plugins/built-in`) manual registration via `.use()`
593
+ 2. **Project plugins** (`plugins/`) — auto-discovery enabled by default (`PLUGINS_DISCOVER_PROJECT=true`)
594
+ 3. **NPM plugins** (`node_modules/`) — auto-discovery disabled by default; requires whitelist in `PLUGINS_ALLOWED`
594
595
 
595
596
  ## Critical Rules
596
597