create-fluxstack 1.19.0 → 1.20.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 (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,397 +1,397 @@
1
- # Anti-Patterns
2
-
3
- **Version:** 1.11.0 | **Updated:** 2025-02-08
4
-
5
- ## Quick Facts
6
-
7
- - FluxStack has strict rules to maintain type safety and stability
8
- - Violations break type inference, cause runtime errors, or introduce security issues
9
- - Most issues stem from ignoring the core/app separation
10
-
11
- ## Core Directory Violations
12
-
13
- ### Never Modify `core/`
14
-
15
- ```typescript
16
- // ❌ NEVER do this
17
- // Editing core/server/framework.ts
18
- // Editing core/plugins/manager.ts
19
- // Editing core/utils/config-schema.ts
20
-
21
- // ✅ Use extension points instead
22
- // Create plugins in plugins/
23
- // Override configs in config/
24
- // Add business logic in app/
25
- ```
26
-
27
- **Why**: `core/` is framework code. Changes break on updates and can't be merged upstream.
28
-
29
- ## Eden Treaty Anti-Patterns
30
-
31
- ### Never Wrap Eden Treaty
32
-
33
- ```typescript
34
- // ❌ WRONG - Wrapping breaks type inference
35
- async function apiCall<T>(fn: () => Promise<any>): Promise<T> {
36
- try {
37
- const result = await fn()
38
- return result.data as T // Type cast = lost inference
39
- } catch (error) {
40
- throw error
41
- }
42
- }
43
-
44
- const user = await apiCall<User>(() => api.users({ id: 1 }).get())
45
- // user type is manually cast, not inferred
46
-
47
- // ✅ CORRECT - Use Eden Treaty directly
48
- const { data, error } = await api.users({ id: 1 }).get()
49
- // data is automatically typed as UserResponse
50
- ```
51
-
52
- **Why**: Eden Treaty's power is automatic type inference. Wrappers destroy this.
53
-
54
- ### Never Omit Response Schemas
55
-
56
- ```typescript
57
- // ❌ WRONG - No response schema
58
- export const usersRoutes = new Elysia({ prefix: '/users' })
59
- .get('/', () => {
60
- return { users: [] } // Response type is 'unknown' in Eden
61
- })
62
-
63
- // ✅ CORRECT - Always define response schema
64
- export const usersRoutes = new Elysia({ prefix: '/users' })
65
- .get('/', () => {
66
- return { users: [] }
67
- }, {
68
- response: t.Object({
69
- users: t.Array(t.Object({
70
- id: t.Number(),
71
- name: t.String()
72
- }))
73
- })
74
- })
75
- ```
76
-
77
- **Why**: Response schemas enable type inference AND generate Swagger docs.
78
-
79
- ### Never Define Types Manually for API Responses
80
-
81
- ```typescript
82
- // ❌ WRONG - Manual type definitions
83
- interface UserResponse {
84
- id: number
85
- name: string
86
- }
87
- const { data } = await api.users.get()
88
- const users = data as UserResponse[] // Type assertion
89
-
90
- // ✅ CORRECT - Let Eden Treaty infer types
91
- const { data, error } = await api.users.get()
92
- // TypeScript automatically knows data.users is User[]
93
- ```
94
-
95
- ## Configuration Anti-Patterns
96
-
97
- ### Never Use process.env Directly
98
-
99
- ```typescript
100
- // ❌ WRONG - No validation, no type safety
101
- const port = process.env.PORT || 3000
102
- const debug = process.env.DEBUG === 'true'
103
-
104
- // ✅ CORRECT - Use config system
105
- import { appConfig } from '@config/app.config'
106
- const port = appConfig.port // number, validated
107
- const debug = appConfig.debug // boolean, validated
108
- ```
109
-
110
- ### Never Hardcode Configuration
111
-
112
- ```typescript
113
- // ❌ WRONG - Hardcoded values
114
- const corsOrigins = ['http://localhost:5173', 'https://myapp.com']
115
-
116
- // ✅ CORRECT - Use environment-based config
117
- import { serverConfig } from '@config/server.config'
118
- const corsOrigins = serverConfig.cors.origins
119
- ```
120
-
121
- ### Never Mix Config Layers
122
-
123
- ```typescript
124
- // ❌ WRONG - Accessing system config from app code
125
- import { systemConfig } from '@config/system.config'
126
- console.log(systemConfig.framework.name) // Framework details in app
127
-
128
- // ✅ CORRECT - Use appropriate config layer
129
- import { appConfig } from '@config/app.config'
130
- console.log(appConfig.name)
131
- ```
132
-
133
- ## Import Path Anti-Patterns
134
-
135
- ### Never Use Deep Relative Imports
136
-
137
- ```typescript
138
- // ❌ WRONG - Brittle, hard to refactor
139
- import { api } from '../../../lib/eden-api'
140
- import type { User } from '../../../../shared/types'
141
-
142
- // ✅ CORRECT - Use path aliases
143
- import { api } from '@client/lib/eden-api'
144
- import type { User } from '@shared/types'
145
- ```
146
-
147
- ### Never Import Core Internals
148
-
149
- ```typescript
150
- // ❌ WRONG - Internal implementation details
151
- import { internalHelper } from '@core/framework/internal/utils'
152
-
153
- // ✅ CORRECT - Use public exports only
154
- import { publicUtil } from '@core/utils'
155
- ```
156
-
157
- ## Plugin Security Anti-Patterns
158
-
159
- ### Never Enable NPM Discovery Without Whitelist
160
-
161
- ```bash
162
- # ❌ WRONG - All NPM plugins auto-loaded (dangerous!)
163
- PLUGINS_DISCOVER_NPM=true
164
- # No PLUGINS_ALLOWED set
165
-
166
- # ✅ CORRECT - Whitelist required packages
167
- PLUGINS_DISCOVER_NPM=true
168
- PLUGINS_ALLOWED=fluxstack-plugin-auth,@acme/fplugin-payments
169
- ```
170
-
171
- ### Never Skip Plugin Auditing
172
-
173
- ```bash
174
- # ❌ WRONG - Installing without audit
175
- bun add some-random-plugin
176
-
177
- # ✅ CORRECT - Use plugin:add with audit
178
- bun run fluxstack plugin:add some-random-plugin
179
- # Automatically audits before install
180
- ```
181
-
182
- ### Never Trust Plugin Config Blindly
183
-
184
- ```typescript
185
- // ❌ WRONG - Using unvalidated plugin config
186
- const pluginConfig = await loadPluginConfig(pluginName)
187
- database.connect(pluginConfig.connectionString) // Potential injection
188
-
189
- // ✅ CORRECT - Validate with schema
190
- const schema = {
191
- connectionString: config.string('DB_URL', '', true)
192
- }
193
- const validatedConfig = defineConfig(schema)
194
- ```
195
-
196
- ## Route Definition Anti-Patterns
197
-
198
- ### Never Mix Business Logic in Routes
199
-
200
- ```typescript
201
- // ❌ WRONG - Database logic in route
202
- export const usersRoutes = new Elysia({ prefix: '/users' })
203
- .get('/', async () => {
204
- const db = await connectDB()
205
- const users = await db.query('SELECT * FROM users')
206
- await db.close()
207
- return { users }
208
- })
209
-
210
- // ✅ CORRECT - Use controller/service pattern
211
- export const usersRoutes = new Elysia({ prefix: '/users' })
212
- .get('/', async () => {
213
- return await userController.list()
214
- })
215
- ```
216
-
217
- ### Never Forget Error Handling
218
-
219
- ```typescript
220
- // ❌ WRONG - Unhandled errors
221
- .post('/', async ({ body }) => {
222
- const user = await createUser(body) // May throw
223
- return { user }
224
- })
225
-
226
- // ✅ CORRECT - Handle errors properly
227
- .post('/', async ({ body, error }) => {
228
- try {
229
- const user = await createUser(body)
230
- return { success: true, user }
231
- } catch (e) {
232
- return error(400, { success: false, message: e.message })
233
- }
234
- })
235
- ```
236
-
237
- ## Testing Anti-Patterns
238
-
239
- ### Never Test Against Real API
240
-
241
- ```typescript
242
- // ❌ WRONG - Real API calls in tests
243
- it('should fetch users', async () => {
244
- const { data } = await api.users.get() // Hits real backend
245
- expect(data.users).toBeDefined()
246
- })
247
-
248
- // ✅ CORRECT - Mock Eden Treaty
249
- vi.mock('@client/lib/eden-api', () => ({
250
- api: {
251
- users: {
252
- get: vi.fn().mockResolvedValue({
253
- data: { users: [{ id: 1, name: 'Test' }] },
254
- error: undefined
255
- })
256
- }
257
- }
258
- }))
259
- ```
260
-
261
- ## Build Anti-Patterns
262
-
263
- ### Never Import Dev Dependencies in Production
264
-
265
- ```typescript
266
- // ❌ WRONG - Conditional import that still bundles
267
- import { DevTools } from 'react-devtools' // Always bundled
268
-
269
- if (process.env.NODE_ENV === 'development') {
270
- DevTools.init()
271
- }
272
-
273
- // ✅ CORRECT - Dynamic import for dev-only
274
- if (import.meta.env.DEV) {
275
- const { DevTools } = await import('react-devtools')
276
- DevTools.init()
277
- }
278
- ```
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
-
374
- ## Summary Table
375
-
376
- | Anti-Pattern | Impact | Solution |
377
- |-------------|--------|----------|
378
- | Modifying `core/` | Update conflicts | Use plugins/app |
379
- | Wrapping Eden Treaty | Lost type inference | Use directly |
380
- | Missing response schemas | Unknown types | Always define schemas |
381
- | Direct process.env | No validation | Use config system |
382
- | Deep relative imports | Fragile paths | Use aliases |
383
- | NPM plugins without whitelist | Security risk | Set PLUGINS_ALLOWED |
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 |
389
-
390
- ## Related
391
-
392
- - [Project Structure](./project-structure.md)
393
- - [Type Safety](./type-safety.md)
394
- - [Plugin Security](../core/plugin-system.md)
395
- - [Routes with Eden Treaty](../resources/routes-eden.md)
396
- - [Live Components](../resources/live-components.md)
397
- - [Live Upload](../resources/live-upload.md)
1
+ # Anti-Patterns
2
+
3
+ **Version:** 1.19.0 | **Updated:** 2026-04-14
4
+
5
+ ## Quick Facts
6
+
7
+ - FluxStack has strict rules to maintain type safety and stability
8
+ - Violations break type inference, cause runtime errors, or introduce security issues
9
+ - Most issues stem from ignoring the core/app separation
10
+
11
+ ## Core Directory Violations
12
+
13
+ ### Never Modify `core/`
14
+
15
+ ```typescript
16
+ // ❌ NEVER do this
17
+ // Editing core/server/framework.ts
18
+ // Editing core/plugins/manager.ts
19
+ // Editing core/utils/config-schema.ts
20
+
21
+ // ✅ Use extension points instead
22
+ // Create plugins in plugins/
23
+ // Override configs in config/
24
+ // Add business logic in app/
25
+ ```
26
+
27
+ **Why**: `core/` is framework code. Changes break on updates and can't be merged upstream.
28
+
29
+ ## Eden Treaty Anti-Patterns
30
+
31
+ ### Never Wrap Eden Treaty
32
+
33
+ ```typescript
34
+ // ❌ WRONG - Wrapping breaks type inference
35
+ async function apiCall<T>(fn: () => Promise<any>): Promise<T> {
36
+ try {
37
+ const result = await fn()
38
+ return result.data as T // Type cast = lost inference
39
+ } catch (error) {
40
+ throw error
41
+ }
42
+ }
43
+
44
+ const user = await apiCall<User>(() => api.users({ id: 1 }).get())
45
+ // user type is manually cast, not inferred
46
+
47
+ // ✅ CORRECT - Use Eden Treaty directly
48
+ const { data, error } = await api.users({ id: 1 }).get()
49
+ // data is automatically typed as UserResponse
50
+ ```
51
+
52
+ **Why**: Eden Treaty's power is automatic type inference. Wrappers destroy this.
53
+
54
+ ### Never Omit Response Schemas
55
+
56
+ ```typescript
57
+ // ❌ WRONG - No response schema
58
+ export const usersRoutes = new Elysia({ prefix: '/users' })
59
+ .get('/', () => {
60
+ return { users: [] } // Response type is 'unknown' in Eden
61
+ })
62
+
63
+ // ✅ CORRECT - Always define response schema
64
+ export const usersRoutes = new Elysia({ prefix: '/users' })
65
+ .get('/', () => {
66
+ return { users: [] }
67
+ }, {
68
+ response: t.Object({
69
+ users: t.Array(t.Object({
70
+ id: t.Number(),
71
+ name: t.String()
72
+ }))
73
+ })
74
+ })
75
+ ```
76
+
77
+ **Why**: Response schemas enable type inference AND generate Swagger docs.
78
+
79
+ ### Never Define Types Manually for API Responses
80
+
81
+ ```typescript
82
+ // ❌ WRONG - Manual type definitions
83
+ interface UserResponse {
84
+ id: number
85
+ name: string
86
+ }
87
+ const { data } = await api.users.get()
88
+ const users = data as UserResponse[] // Type assertion
89
+
90
+ // ✅ CORRECT - Let Eden Treaty infer types
91
+ const { data, error } = await api.users.get()
92
+ // TypeScript automatically knows data.users is User[]
93
+ ```
94
+
95
+ ## Configuration Anti-Patterns
96
+
97
+ ### Never Use process.env Directly
98
+
99
+ ```typescript
100
+ // ❌ WRONG - No validation, no type safety
101
+ const port = process.env.PORT || 3000
102
+ const debug = process.env.DEBUG === 'true'
103
+
104
+ // ✅ CORRECT - Use config system
105
+ import { appConfig } from '@config/app.config'
106
+ const port = appConfig.port // number, validated
107
+ const debug = appConfig.debug // boolean, validated
108
+ ```
109
+
110
+ ### Never Hardcode Configuration
111
+
112
+ ```typescript
113
+ // ❌ WRONG - Hardcoded values
114
+ const corsOrigins = ['http://localhost:5173', 'https://myapp.com']
115
+
116
+ // ✅ CORRECT - Use environment-based config
117
+ import { serverConfig } from '@config/server.config'
118
+ const corsOrigins = serverConfig.cors.origins
119
+ ```
120
+
121
+ ### Never Mix Config Layers
122
+
123
+ ```typescript
124
+ // ❌ WRONG - Accessing system config from app code
125
+ import { systemConfig } from '@config/system.config'
126
+ console.log(systemConfig.framework.name) // Framework details in app
127
+
128
+ // ✅ CORRECT - Use appropriate config layer
129
+ import { appConfig } from '@config/app.config'
130
+ console.log(appConfig.name)
131
+ ```
132
+
133
+ ## Import Path Anti-Patterns
134
+
135
+ ### Never Use Deep Relative Imports
136
+
137
+ ```typescript
138
+ // ❌ WRONG - Brittle, hard to refactor
139
+ import { api } from '../../../lib/eden-api'
140
+ import type { User } from '../../../../shared/types'
141
+
142
+ // ✅ CORRECT - Use path aliases
143
+ import { api } from '@client/lib/eden-api'
144
+ import type { User } from '@shared/types'
145
+ ```
146
+
147
+ ### Never Import Core Internals
148
+
149
+ ```typescript
150
+ // ❌ WRONG - Internal implementation details
151
+ import { internalHelper } from '@core/framework/internal/utils'
152
+
153
+ // ✅ CORRECT - Use public exports only
154
+ import { publicUtil } from '@core/utils'
155
+ ```
156
+
157
+ ## Plugin Security Anti-Patterns
158
+
159
+ ### Never Enable NPM Discovery Without Whitelist
160
+
161
+ ```bash
162
+ # ❌ WRONG - All NPM plugins auto-loaded (dangerous!)
163
+ PLUGINS_DISCOVER_NPM=true
164
+ # No PLUGINS_ALLOWED set
165
+
166
+ # ✅ CORRECT - Whitelist required packages
167
+ PLUGINS_DISCOVER_NPM=true
168
+ PLUGINS_ALLOWED=fluxstack-plugin-auth,@acme/fplugin-payments
169
+ ```
170
+
171
+ ### Never Skip Plugin Auditing
172
+
173
+ ```bash
174
+ # ❌ WRONG - Installing without audit
175
+ bun add some-random-plugin
176
+
177
+ # ✅ CORRECT - Use plugin:add with audit
178
+ bun run fluxstack plugin:add some-random-plugin
179
+ # Automatically audits before install
180
+ ```
181
+
182
+ ### Never Trust Plugin Config Blindly
183
+
184
+ ```typescript
185
+ // ❌ WRONG - Using unvalidated plugin config
186
+ const pluginConfig = await loadPluginConfig(pluginName)
187
+ database.connect(pluginConfig.connectionString) // Potential injection
188
+
189
+ // ✅ CORRECT - Validate with schema
190
+ const schema = {
191
+ connectionString: config.string('DB_URL', '', true)
192
+ }
193
+ const validatedConfig = defineConfig(schema)
194
+ ```
195
+
196
+ ## Route Definition Anti-Patterns
197
+
198
+ ### Never Mix Business Logic in Routes
199
+
200
+ ```typescript
201
+ // ❌ WRONG - Database logic in route
202
+ export const usersRoutes = new Elysia({ prefix: '/users' })
203
+ .get('/', async () => {
204
+ const db = await connectDB()
205
+ const users = await db.query('SELECT * FROM users')
206
+ await db.close()
207
+ return { users }
208
+ })
209
+
210
+ // ✅ CORRECT - Use controller/service pattern
211
+ export const usersRoutes = new Elysia({ prefix: '/users' })
212
+ .get('/', async () => {
213
+ return await userController.list()
214
+ })
215
+ ```
216
+
217
+ ### Never Forget Error Handling
218
+
219
+ ```typescript
220
+ // ❌ WRONG - Unhandled errors
221
+ .post('/', async ({ body }) => {
222
+ const user = await createUser(body) // May throw
223
+ return { user }
224
+ })
225
+
226
+ // ✅ CORRECT - Handle errors properly
227
+ .post('/', async ({ body, error }) => {
228
+ try {
229
+ const user = await createUser(body)
230
+ return { success: true, user }
231
+ } catch (e) {
232
+ return error(400, { success: false, message: e.message })
233
+ }
234
+ })
235
+ ```
236
+
237
+ ## Testing Anti-Patterns
238
+
239
+ ### Never Test Against Real API
240
+
241
+ ```typescript
242
+ // ❌ WRONG - Real API calls in tests
243
+ it('should fetch users', async () => {
244
+ const { data } = await api.users.get() // Hits real backend
245
+ expect(data.users).toBeDefined()
246
+ })
247
+
248
+ // ✅ CORRECT - Mock Eden Treaty
249
+ vi.mock('@client/lib/eden-api', () => ({
250
+ api: {
251
+ users: {
252
+ get: vi.fn().mockResolvedValue({
253
+ data: { users: [{ id: 1, name: 'Test' }] },
254
+ error: undefined
255
+ })
256
+ }
257
+ }
258
+ }))
259
+ ```
260
+
261
+ ## Build Anti-Patterns
262
+
263
+ ### Never Import Dev Dependencies in Production
264
+
265
+ ```typescript
266
+ // ❌ WRONG - Conditional import that still bundles
267
+ import { DevTools } from 'react-devtools' // Always bundled
268
+
269
+ if (process.env.NODE_ENV === 'development') {
270
+ DevTools.init()
271
+ }
272
+
273
+ // ✅ CORRECT - Dynamic import for dev-only
274
+ if (import.meta.env.DEV) {
275
+ const { DevTools } = await import('react-devtools')
276
+ DevTools.init()
277
+ }
278
+ ```
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
+
374
+ ## Summary Table
375
+
376
+ | Anti-Pattern | Impact | Solution |
377
+ |-------------|--------|----------|
378
+ | Modifying `core/` | Update conflicts | Use plugins/app |
379
+ | Wrapping Eden Treaty | Lost type inference | Use directly |
380
+ | Missing response schemas | Unknown types | Always define schemas |
381
+ | Direct process.env | No validation | Use config system |
382
+ | Deep relative imports | Fragile paths | Use aliases |
383
+ | NPM plugins without whitelist | Security risk | Set PLUGINS_ALLOWED |
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 |
389
+
390
+ ## Related
391
+
392
+ - [Project Structure](./project-structure.md)
393
+ - [Type Safety](./type-safety.md)
394
+ - [Plugin Security](../core/plugin-system.md)
395
+ - [Routes with Eden Treaty](../resources/routes-eden.md)
396
+ - [Live Components](../resources/live-components.md)
397
+ - [Live Upload](../resources/live-upload.md)