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.
- package/LLMD/INDEX.md +1 -1
- package/LLMD/MAINTENANCE.md +197 -197
- package/LLMD/MIGRATION.md +44 -1
- package/LLMD/agent.md +20 -7
- package/LLMD/config/declarative-system.md +268 -268
- package/LLMD/config/environment-vars.md +3 -6
- package/LLMD/config/runtime-reload.md +401 -401
- package/LLMD/core/build-system.md +599 -599
- package/LLMD/core/framework-lifecycle.md +249 -229
- package/LLMD/core/plugin-system.md +154 -100
- package/LLMD/patterns/anti-patterns.md +397 -397
- package/LLMD/patterns/project-structure.md +264 -264
- package/LLMD/patterns/type-safety.md +61 -5
- package/LLMD/reference/cli-commands.md +31 -7
- package/LLMD/reference/plugin-hooks.md +4 -2
- package/LLMD/reference/troubleshooting.md +364 -364
- package/LLMD/resources/controllers.md +465 -465
- package/LLMD/resources/live-auth.md +178 -1
- package/LLMD/resources/live-binary-delta.md +3 -1
- package/LLMD/resources/live-components.md +1192 -1041
- package/LLMD/resources/live-logging.md +3 -1
- package/LLMD/resources/live-rooms.md +1 -1
- package/LLMD/resources/live-upload.md +228 -181
- package/LLMD/resources/plugins-external.md +8 -7
- package/LLMD/resources/rest-auth.md +290 -290
- package/LLMD/resources/routes-eden.md +254 -254
- package/app/client/.live-stubs/LiveAdminPanel.js +15 -0
- package/app/client/.live-stubs/LiveCounter.js +9 -0
- package/app/client/.live-stubs/LiveForm.js +11 -0
- package/app/client/.live-stubs/LiveLocalCounter.js +8 -0
- package/app/client/.live-stubs/LivePingPong.js +10 -0
- package/app/client/.live-stubs/LiveRoomChat.js +11 -0
- package/app/client/.live-stubs/LiveSharedCounter.js +10 -0
- package/app/client/.live-stubs/LiveUpload.js +15 -0
- package/app/server/live/auto-generated-components.ts +1 -1
- package/core/utils/version.ts +6 -6
- package/package.json +108 -108
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Live Logging
|
|
2
2
|
|
|
3
|
-
**Version:** 1.
|
|
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,181 +1,228 @@
|
|
|
1
|
-
# Live Upload (Chunked Upload via WebSocket)
|
|
2
|
-
|
|
3
|
-
**Version:**
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
20
|
-
- **
|
|
21
|
-
- **
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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.
|
|
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
|
-
-
|
|
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
|
|
588
|
+
## Plugin Registration
|
|
588
589
|
|
|
589
|
-
|
|
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
|
|
592
|
-
2.
|
|
593
|
-
3.
|
|
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
|
|