create-fluxstack 1.12.0 → 1.13.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 (82) hide show
  1. package/LLMD/INDEX.md +8 -1
  2. package/LLMD/agent.md +867 -0
  3. package/LLMD/config/environment-vars.md +30 -0
  4. package/LLMD/resources/live-auth.md +447 -0
  5. package/LLMD/resources/live-components.md +79 -21
  6. package/LLMD/resources/live-logging.md +158 -0
  7. package/LLMD/resources/live-upload.md +1 -1
  8. package/LLMD/resources/rest-auth.md +290 -0
  9. package/README.md +520 -340
  10. package/app/client/src/App.tsx +11 -0
  11. package/app/client/src/components/AppLayout.tsx +1 -0
  12. package/app/client/src/live/AuthDemo.tsx +332 -0
  13. package/app/client/src/live/RoomChatDemo.tsx +24 -105
  14. package/app/server/auth/AuthManager.ts +213 -0
  15. package/app/server/auth/DevAuthProvider.ts +66 -0
  16. package/app/server/auth/HashManager.ts +123 -0
  17. package/app/server/auth/JWTAuthProvider.example.ts +101 -0
  18. package/app/server/auth/RateLimiter.ts +106 -0
  19. package/app/server/auth/contracts.ts +192 -0
  20. package/app/server/auth/guards/SessionGuard.ts +167 -0
  21. package/app/server/auth/guards/TokenGuard.ts +202 -0
  22. package/app/server/auth/index.ts +174 -0
  23. package/app/server/auth/middleware.ts +163 -0
  24. package/app/server/auth/providers/InMemoryProvider.ts +162 -0
  25. package/app/server/auth/sessions/SessionManager.ts +164 -0
  26. package/app/server/cache/CacheManager.ts +81 -0
  27. package/app/server/cache/MemoryDriver.ts +112 -0
  28. package/app/server/cache/contracts.ts +49 -0
  29. package/app/server/cache/index.ts +42 -0
  30. package/app/server/index.ts +14 -0
  31. package/app/server/live/LiveAdminPanel.ts +173 -0
  32. package/app/server/live/LiveCounter.ts +1 -0
  33. package/app/server/live/LiveLocalCounter.ts +13 -8
  34. package/app/server/live/LiveProtectedChat.ts +150 -0
  35. package/app/server/live/LiveRoomChat.ts +45 -203
  36. package/app/server/routes/auth.routes.ts +278 -0
  37. package/app/server/routes/index.ts +2 -0
  38. package/config/index.ts +8 -0
  39. package/config/system/auth.config.ts +49 -0
  40. package/config/system/session.config.ts +33 -0
  41. package/core/client/LiveComponentsProvider.tsx +76 -5
  42. package/core/client/components/Live.tsx +2 -1
  43. package/core/client/hooks/useLiveComponent.ts +47 -4
  44. package/core/client/index.ts +2 -1
  45. package/core/framework/server.ts +36 -4
  46. package/core/plugins/built-in/live-components/commands/create-live-component.ts +15 -8
  47. package/core/plugins/built-in/monitoring/index.ts +10 -3
  48. package/core/plugins/built-in/vite/index.ts +95 -18
  49. package/core/plugins/config.ts +5 -4
  50. package/core/plugins/discovery.ts +11 -2
  51. package/core/plugins/manager.ts +11 -5
  52. package/core/plugins/module-resolver.ts +1 -1
  53. package/core/plugins/registry.ts +53 -25
  54. package/core/server/live/ComponentRegistry.ts +79 -24
  55. package/core/server/live/LiveComponentPerformanceMonitor.ts +9 -8
  56. package/core/server/live/LiveLogger.ts +111 -0
  57. package/core/server/live/LiveRoomManager.ts +5 -4
  58. package/core/server/live/StateSignature.ts +644 -643
  59. package/core/server/live/auth/LiveAuthContext.ts +71 -0
  60. package/core/server/live/auth/LiveAuthManager.ts +304 -0
  61. package/core/server/live/auth/index.ts +19 -0
  62. package/core/server/live/auth/types.ts +179 -0
  63. package/core/server/live/auto-generated-components.ts +8 -2
  64. package/core/server/live/index.ts +16 -0
  65. package/core/server/live/websocket-plugin.ts +92 -16
  66. package/core/templates/create-project.ts +0 -3
  67. package/core/types/types.ts +133 -13
  68. package/core/utils/index.ts +17 -17
  69. package/core/utils/logger/index.ts +5 -2
  70. package/core/utils/version.ts +1 -1
  71. package/package.json +1 -8
  72. package/plugins/crypto-auth/index.ts +6 -0
  73. package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +58 -0
  74. package/plugins/crypto-auth/server/index.ts +24 -21
  75. package/rest-tests/README.md +57 -0
  76. package/rest-tests/auth-token.http +113 -0
  77. package/rest-tests/auth.http +112 -0
  78. package/rest-tests/rooms-token.http +69 -0
  79. package/rest-tests/users-token.http +62 -0
  80. package/.dockerignore +0 -81
  81. package/Dockerfile +0 -70
  82. package/LIVE_COMPONENTS_REVIEW.md +0 -781
package/LLMD/agent.md ADDED
@@ -0,0 +1,867 @@
1
+ # FluxStack Agent Guide
2
+
3
+ **Version:** 1.13.0 | **Updated:** 2025-02-14
4
+
5
+ > Guia completo para agentes de IA que auxiliam desenvolvedores a configurar, construir e manter aplicacoes FluxStack.
6
+
7
+ ## Identidade do Agente
8
+
9
+ Voce e um agente especialista em FluxStack, um framework full-stack TypeScript moderno. Seu papel e:
10
+
11
+ 1. **Guiar** desenvolvedores na configuracao inicial e estruturacao do projeto
12
+ 2. **Gerar** codigo que segue os padroes do framework (routes, controllers, live components, configs)
13
+ 3. **Diagnosticar** problemas seguindo a arvore de troubleshooting
14
+ 4. **Ensinar** boas praticas e prevenir anti-patterns
15
+
16
+ Sempre priorize type safety, separacao de responsabilidades e as convencoes do framework.
17
+
18
+ ---
19
+
20
+ ## Stack Tecnologica
21
+
22
+ | Camada | Tecnologia | Versao |
23
+ |--------|-----------|--------|
24
+ | Runtime | Bun | >= 1.2.0 |
25
+ | Backend | Elysia.js | 1.4.6 |
26
+ | Frontend | React | 19.1.0 |
27
+ | Bundler | Vite | 7.1.7 |
28
+ | Linguagem | TypeScript | 5.8.3 |
29
+ | Styling | Tailwind CSS | 4.1.13 |
30
+ | Type-safe API | Eden Treaty | 1.3.2 |
31
+ | Testes | Vitest | 3.2.4 |
32
+
33
+ ---
34
+
35
+ ## Arquitetura do Projeto
36
+
37
+ ```
38
+ FluxStack/
39
+ ├── core/ # FRAMEWORK (SOMENTE LEITURA - nunca modificar)
40
+ ├── app/ # CODIGO DA APLICACAO (area de trabalho principal)
41
+ │ ├── server/ # Backend: routes, controllers, live components
42
+ │ ├── client/ # Frontend: React components, pages, hooks
43
+ │ └── shared/ # Types compartilhados entre client e server
44
+ ├── config/ # CONFIGURACOES (declarativas, com validacao)
45
+ │ ├── system/ # Defaults do framework (base)
46
+ │ └── *.config.ts # Overrides do usuario
47
+ ├── plugins/ # PLUGINS DO PROJETO (auto-discovered, confiaveis)
48
+ ├── tests/ # TESTES (Vitest)
49
+ └── LLMD/ # DOCUMENTACAO LLM-optimizada
50
+ ```
51
+
52
+ ### Regra Fundamental
53
+
54
+ ```
55
+ core/ = READ-ONLY (framework)
56
+ app/ = READ-WRITE (seu codigo)
57
+ config/ = READ-WRITE (suas configuracoes)
58
+ plugins/ = READ-WRITE (seus plugins)
59
+ ```
60
+
61
+ ---
62
+
63
+ ## Fluxos de Trabalho
64
+
65
+ ### 1. Setup Inicial do Projeto
66
+
67
+ Quando o desenvolvedor pedir para configurar um novo projeto ou iniciar do zero:
68
+
69
+ ```bash
70
+ # 1. Verificar se Bun esta instalado
71
+ bun --version || curl -fsSL https://bun.sh/install | bash
72
+
73
+ # 2. Instalar dependencias
74
+ bun install
75
+
76
+ # 3. Copiar .env de exemplo (se existir)
77
+ cp .env.example .env # ajustar variaveis
78
+
79
+ # 4. Iniciar em modo desenvolvimento
80
+ bun run dev
81
+ ```
82
+
83
+ **Validar que tudo funciona:**
84
+ - Backend: http://localhost:3000/api/health
85
+ - Frontend: http://localhost:5173
86
+ - Swagger: http://localhost:3000/swagger
87
+
88
+ ---
89
+
90
+ ### 2. Criar uma Nova Rota API
91
+
92
+ **Passo 1 - Definir schemas e rota** em `app/server/routes/`:
93
+
94
+ ```typescript
95
+ // app/server/routes/{recurso}.routes.ts
96
+ import { Elysia, t } from 'elysia'
97
+
98
+ // 1. Definir schemas (reusaveis)
99
+ const ItemSchema = t.Object({
100
+ id: t.Number(),
101
+ name: t.String(),
102
+ status: t.Union([t.Literal('active'), t.Literal('inactive')])
103
+ }, { description: 'Item object' })
104
+
105
+ const CreateItemSchema = t.Object({
106
+ name: t.String({ minLength: 2, description: 'Item name' }),
107
+ status: t.Optional(t.Union([t.Literal('active'), t.Literal('inactive')]))
108
+ }, { description: 'Create item request' })
109
+
110
+ // 2. Definir rotas com response schemas (OBRIGATORIO)
111
+ export const itemsRoutes = new Elysia({ prefix: '/items', tags: ['Items'] })
112
+ .get('/', async () => {
113
+ return ItemsController.getAll()
114
+ }, {
115
+ detail: { summary: 'List Items', tags: ['Items'] },
116
+ response: t.Object({
117
+ success: t.Boolean(),
118
+ items: t.Array(ItemSchema),
119
+ count: t.Number()
120
+ })
121
+ })
122
+
123
+ .get('/:id', async ({ params, set }) => {
124
+ const result = await ItemsController.getById(Number(params.id))
125
+ if (!result.success) set.status = 404
126
+ return result
127
+ }, {
128
+ params: t.Object({ id: t.String() }),
129
+ response: {
130
+ 200: t.Object({ success: t.Literal(true), item: ItemSchema }),
131
+ 404: t.Object({ success: t.Literal(false), error: t.String() })
132
+ }
133
+ })
134
+
135
+ .post('/', async ({ body, set }) => {
136
+ const result = await ItemsController.create(body)
137
+ if (result.success) set.status = 201
138
+ return result
139
+ }, {
140
+ body: CreateItemSchema,
141
+ response: {
142
+ 201: t.Object({ success: t.Literal(true), item: ItemSchema }),
143
+ 400: t.Object({ success: t.Literal(false), error: t.String() })
144
+ }
145
+ })
146
+ ```
147
+
148
+ **Passo 2 - Criar controller** em `app/server/controllers/`:
149
+
150
+ ```typescript
151
+ // app/server/controllers/{recurso}.controller.ts
152
+ export class ItemsController {
153
+ private static items: Item[] = []
154
+ private static nextId = 1
155
+
156
+ static async getAll() {
157
+ return {
158
+ success: true as const,
159
+ items: this.items,
160
+ count: this.items.length
161
+ }
162
+ }
163
+
164
+ static async getById(id: number) {
165
+ const item = this.items.find(i => i.id === id)
166
+ if (!item) {
167
+ return { success: false as const, error: 'Item not found' }
168
+ }
169
+ return { success: true as const, item }
170
+ }
171
+
172
+ static async create(data: { name: string; status?: 'active' | 'inactive' }) {
173
+ const item: Item = {
174
+ id: this.nextId++,
175
+ name: data.name,
176
+ status: data.status ?? 'active'
177
+ }
178
+ this.items.push(item)
179
+ return { success: true as const, item }
180
+ }
181
+ }
182
+ ```
183
+
184
+ **Passo 3 - Registrar a rota** em `app/server/app.ts`:
185
+
186
+ ```typescript
187
+ import { itemsRoutes } from './routes/items.routes'
188
+
189
+ // Dentro da configuracao da app Elysia
190
+ app.use(itemsRoutes)
191
+ ```
192
+
193
+ **Passo 4 - Usar no frontend** (types automaticos via Eden Treaty):
194
+
195
+ ```typescript
196
+ // No componente React - SEM tipos manuais!
197
+ import { api } from '@/lib/eden-api'
198
+
199
+ const { data, error } = await api.items.get()
200
+ // data.items e automaticamente tipado como Item[]
201
+
202
+ const { data: created } = await api.items.post({
203
+ name: 'Novo Item',
204
+ status: 'active'
205
+ })
206
+ // created.item e automaticamente tipado como Item
207
+ ```
208
+
209
+ ---
210
+
211
+ ### 3. Criar um Live Component (WebSocket Real-Time)
212
+
213
+ **Passo 1 - Componente server-side** em `app/server/live/`:
214
+
215
+ ```typescript
216
+ // app/server/live/Live{Nome}.ts
217
+ import { LiveComponent } from '@core/types/types'
218
+
219
+ // Link para o componente client (Ctrl+Click no VSCode)
220
+ import type { {Nome}Demo as _Client } from '@client/src/live/{Nome}Demo'
221
+
222
+ export class Live{Nome} extends LiveComponent<typeof Live{Nome}.defaultState> {
223
+ static componentName = 'Live{Nome}'
224
+ static logging = ['lifecycle', 'messages'] as const // opcional
225
+
226
+ static defaultState = {
227
+ // Definir estado inicial aqui
228
+ count: 0,
229
+ items: [] as string[],
230
+ lastUpdated: null as string | null
231
+ }
232
+
233
+ // Declarar propriedades para TypeScript
234
+ declare count: number
235
+ declare items: string[]
236
+ declare lastUpdated: string | null
237
+
238
+ // Acoes chamadas pelo client
239
+ async increment() {
240
+ this.count++ // Auto-sync via Proxy
241
+ this.lastUpdated = new Date().toISOString()
242
+ return { success: true, count: this.count }
243
+ }
244
+
245
+ async addItem(payload: { text: string }) {
246
+ // Batch update (single STATE_DELTA)
247
+ this.setState({
248
+ items: [...this.items, payload.text],
249
+ lastUpdated: new Date().toISOString()
250
+ })
251
+ return { success: true }
252
+ }
253
+
254
+ async reset() {
255
+ this.setState({ ...Live{Nome}.defaultState })
256
+ return { success: true }
257
+ }
258
+ }
259
+ ```
260
+
261
+ **Passo 2 - Componente client-side** em `app/client/src/live/`:
262
+
263
+ ```typescript
264
+ // app/client/src/live/{Nome}Demo.tsx
265
+ import { Live } from '@/core/client'
266
+ import { Live{Nome} } from '@server/live/Live{Nome}'
267
+
268
+ export function {Nome}Demo() {
269
+ const component = Live.use(Live{Nome}, {
270
+ room: 'default-room', // opcional: para multi-user sync
271
+ initialState: Live{Nome}.defaultState
272
+ })
273
+
274
+ const { count, items, lastUpdated } = component.$state
275
+ const isConnected = component.$connected
276
+ const isLoading = component.$loading
277
+
278
+ return (
279
+ <div>
280
+ <p>Status: {isConnected ? 'Conectado' : 'Desconectado'}</p>
281
+ <p>Count: {count}</p>
282
+ <button onClick={() => component.increment()} disabled={isLoading}>
283
+ Incrementar
284
+ </button>
285
+ <button onClick={() => component.addItem({ text: 'Novo' })}>
286
+ Adicionar Item
287
+ </button>
288
+ <ul>
289
+ {items.map((item, i) => <li key={i}>{item}</li>)}
290
+ </ul>
291
+ {lastUpdated && <small>Atualizado: {lastUpdated}</small>}
292
+ </div>
293
+ )
294
+ }
295
+ ```
296
+
297
+ **Com Room Events (multi-usuario):**
298
+
299
+ ```typescript
300
+ // Server: sincronizar entre usuarios
301
+ export class LiveChat extends LiveComponent<typeof LiveChat.defaultState> {
302
+ static componentName = 'LiveChat'
303
+ static defaultState = {
304
+ messages: [] as { user: string; text: string; ts: number }[]
305
+ }
306
+
307
+ constructor(initialState: any, ws: any, options?: any) {
308
+ super(initialState, ws, options)
309
+
310
+ // Escutar eventos de OUTROS usuarios
311
+ this.onRoomEvent<{ user: string; text: string; ts: number }>('NEW_MSG', (msg) => {
312
+ this.setState({
313
+ messages: [...this.state.messages, msg]
314
+ })
315
+ })
316
+ }
317
+
318
+ async sendMessage(payload: { user: string; text: string }) {
319
+ const msg = { ...payload, ts: Date.now() }
320
+
321
+ // 1. Atualizar MEU estado
322
+ this.setState({ messages: [...this.state.messages, msg] })
323
+
324
+ // 2. Notificar OUTROS na sala
325
+ this.emitRoomEvent('NEW_MSG', msg)
326
+
327
+ return { success: true }
328
+ }
329
+
330
+ destroy() {
331
+ super.destroy()
332
+ }
333
+ }
334
+ ```
335
+
336
+ ---
337
+
338
+ ### 4. Criar Configuracao
339
+
340
+ **Passo 1 - Definir schema** em `config/`:
341
+
342
+ ```typescript
343
+ // config/{nome}.config.ts
344
+ import { defineConfig, config } from '@core/utils/config-schema'
345
+
346
+ const myConfigSchema = {
347
+ apiKey: config.string('MY_API_KEY', '', true),
348
+ maxRetries: config.number('MY_MAX_RETRIES', 3),
349
+ environment: config.enum(
350
+ 'MY_ENV',
351
+ ['sandbox', 'production'] as const,
352
+ 'sandbox',
353
+ true
354
+ ),
355
+ enableCache: config.boolean('MY_ENABLE_CACHE', true),
356
+ allowedOrigins: config.array('MY_ALLOWED_ORIGINS', ['localhost']),
357
+ } as const // IMPORTANTE: as const para preservar tipos literais
358
+
359
+ export const myConfig = defineConfig(myConfigSchema)
360
+ ```
361
+
362
+ **Passo 2 - Adicionar variaveis** no `.env`:
363
+
364
+ ```env
365
+ MY_API_KEY=sk-123456
366
+ MY_MAX_RETRIES=5
367
+ MY_ENV=sandbox
368
+ MY_ENABLE_CACHE=true
369
+ MY_ALLOWED_ORIGINS=localhost,myapp.com
370
+ ```
371
+
372
+ **Passo 3 - Usar com type safety total:**
373
+
374
+ ```typescript
375
+ import { myConfig } from '@config/my.config'
376
+
377
+ // TypeScript infere automaticamente:
378
+ // myConfig.apiKey → string
379
+ // myConfig.maxRetries → number
380
+ // myConfig.environment → "sandbox" | "production"
381
+ // myConfig.enableCache → boolean
382
+ ```
383
+
384
+ ---
385
+
386
+ ### 5. Criar Plugin do Projeto
387
+
388
+ **Passo 1 - Gerar scaffold:**
389
+
390
+ ```bash
391
+ bun run flux make:plugin meu-plugin
392
+ ```
393
+
394
+ **Passo 2 - Implementar o plugin** em `plugins/meu-plugin/index.ts`:
395
+
396
+ ```typescript
397
+ import type { FluxStackPlugin } from '@core/types/plugin'
398
+
399
+ const meuPlugin: FluxStackPlugin = {
400
+ name: 'meu-plugin',
401
+ version: '1.0.0',
402
+
403
+ // Hooks do ciclo de vida
404
+ async setup(app) {
405
+ // Registrar rotas, middleware, etc.
406
+ app.get('/api/meu-plugin/status', () => ({ active: true }))
407
+ },
408
+
409
+ hooks: {
410
+ onServerStart: async (app) => {
411
+ console.log('[meu-plugin] Servidor iniciado')
412
+ },
413
+
414
+ onRequest: async (ctx) => {
415
+ // Middleware em cada request
416
+ }
417
+ }
418
+ }
419
+
420
+ export default meuPlugin
421
+ ```
422
+
423
+ Plugins em `plugins/` sao auto-discovered e confiaveis. Nao precisam de whitelist.
424
+
425
+ ---
426
+
427
+ ### 6. Adicionar Autenticacao
428
+
429
+ #### REST API (Session ou Token Guard)
430
+
431
+ ```typescript
432
+ // app/server/routes/protected.routes.ts
433
+ import { Elysia, t } from 'elysia'
434
+ import { authMiddleware } from '@app/server/auth'
435
+
436
+ export const protectedRoutes = new Elysia({ prefix: '/protected' })
437
+ .use(authMiddleware) // Aplica auth em todas as rotas deste grupo
438
+
439
+ .get('/profile', async ({ user }) => {
440
+ return { success: true, user }
441
+ }, {
442
+ response: t.Object({
443
+ success: t.Boolean(),
444
+ user: t.Object({ id: t.String(), name: t.String() })
445
+ })
446
+ })
447
+ ```
448
+
449
+ #### Live Components (Declarativo RBAC)
450
+
451
+ ```typescript
452
+ export class AdminPanel extends LiveComponent<typeof AdminPanel.defaultState> {
453
+ static componentName = 'AdminPanel'
454
+ static defaultState = { users: [] as any[] }
455
+
456
+ // Auth declarativo na classe
457
+ static auth = {
458
+ required: true,
459
+ roles: ['admin']
460
+ }
461
+
462
+ // Auth por acao
463
+ static actionAuth = {
464
+ deleteUser: { permissions: ['users.delete'] }
465
+ }
466
+
467
+ async deleteUser(payload: { userId: string }) {
468
+ // $auth disponivel automaticamente
469
+ console.log(`${this.$auth.user?.id} deletando usuario`)
470
+ return { success: true }
471
+ }
472
+ }
473
+ ```
474
+
475
+ ---
476
+
477
+ ## Templates de Codigo por Tipo
478
+
479
+ ### Template: Rota CRUD Completa
480
+
481
+ ```typescript
482
+ // app/server/routes/{recurso}.routes.ts
483
+ import { Elysia, t } from 'elysia'
484
+ import { {Recurso}Controller } from '../controllers/{recurso}.controller'
485
+
486
+ const {Recurso}Schema = t.Object({
487
+ id: t.Number(),
488
+ name: t.String(),
489
+ createdAt: t.String()
490
+ })
491
+
492
+ const Create{Recurso}Schema = t.Object({
493
+ name: t.String({ minLength: 2 })
494
+ })
495
+
496
+ const Update{Recurso}Schema = t.Object({
497
+ name: t.Optional(t.String({ minLength: 2 }))
498
+ })
499
+
500
+ const SuccessResponse = (data: any) => t.Object({
501
+ success: t.Literal(true),
502
+ ...data
503
+ })
504
+
505
+ const ErrorResponse = t.Object({
506
+ success: t.Literal(false),
507
+ error: t.String()
508
+ })
509
+
510
+ export const {recurso}Routes = new Elysia({ prefix: '/{recurso}s', tags: ['{Recurso}s'] })
511
+
512
+ // LIST
513
+ .get('/', () => {Recurso}Controller.getAll(), {
514
+ detail: { summary: 'List {Recurso}s' },
515
+ response: t.Object({
516
+ success: t.Boolean(),
517
+ {recurso}s: t.Array({Recurso}Schema),
518
+ count: t.Number()
519
+ })
520
+ })
521
+
522
+ // GET BY ID
523
+ .get('/:id', async ({ params, set }) => {
524
+ const result = await {Recurso}Controller.getById(Number(params.id))
525
+ if (!result.success) set.status = 404
526
+ return result
527
+ }, {
528
+ params: t.Object({ id: t.String() }),
529
+ response: {
530
+ 200: t.Object({ success: t.Literal(true), {recurso}: {Recurso}Schema }),
531
+ 404: ErrorResponse
532
+ }
533
+ })
534
+
535
+ // CREATE
536
+ .post('/', async ({ body, set }) => {
537
+ const result = await {Recurso}Controller.create(body)
538
+ if (result.success) set.status = 201
539
+ return result
540
+ }, {
541
+ body: Create{Recurso}Schema,
542
+ response: {
543
+ 201: t.Object({ success: t.Literal(true), {recurso}: {Recurso}Schema }),
544
+ 400: ErrorResponse
545
+ }
546
+ })
547
+
548
+ // UPDATE
549
+ .put('/:id', async ({ params, body, set }) => {
550
+ const result = await {Recurso}Controller.update(Number(params.id), body)
551
+ if (!result.success) set.status = 404
552
+ return result
553
+ }, {
554
+ params: t.Object({ id: t.String() }),
555
+ body: Update{Recurso}Schema,
556
+ response: {
557
+ 200: t.Object({ success: t.Literal(true), {recurso}: {Recurso}Schema }),
558
+ 404: ErrorResponse
559
+ }
560
+ })
561
+
562
+ // DELETE
563
+ .delete('/:id', async ({ params, set }) => {
564
+ const result = await {Recurso}Controller.delete(Number(params.id))
565
+ if (!result.success) set.status = 404
566
+ return result
567
+ }, {
568
+ params: t.Object({ id: t.String() }),
569
+ response: {
570
+ 200: t.Object({ success: t.Literal(true), message: t.String() }),
571
+ 404: ErrorResponse
572
+ }
573
+ })
574
+ ```
575
+
576
+ ### Template: Controller Padrao
577
+
578
+ ```typescript
579
+ // app/server/controllers/{recurso}.controller.ts
580
+ interface {Recurso} {
581
+ id: number
582
+ name: string
583
+ createdAt: string
584
+ }
585
+
586
+ export class {Recurso}Controller {
587
+ private static items: {Recurso}[] = []
588
+ private static nextId = 1
589
+
590
+ static async getAll() {
591
+ return {
592
+ success: true as const,
593
+ {recurso}s: this.items,
594
+ count: this.items.length
595
+ }
596
+ }
597
+
598
+ static async getById(id: number) {
599
+ const item = this.items.find(i => i.id === id)
600
+ if (!item) return { success: false as const, error: '{Recurso} not found' }
601
+ return { success: true as const, {recurso}: item }
602
+ }
603
+
604
+ static async create(data: Omit<{Recurso}, 'id' | 'createdAt'>) {
605
+ const item: {Recurso} = {
606
+ id: this.nextId++,
607
+ ...data,
608
+ createdAt: new Date().toISOString()
609
+ }
610
+ this.items.push(item)
611
+ return { success: true as const, {recurso}: item }
612
+ }
613
+
614
+ static async update(id: number, data: Partial<Omit<{Recurso}, 'id' | 'createdAt'>>) {
615
+ const index = this.items.findIndex(i => i.id === id)
616
+ if (index === -1) return { success: false as const, error: '{Recurso} not found' }
617
+ this.items[index] = { ...this.items[index], ...data }
618
+ return { success: true as const, {recurso}: this.items[index] }
619
+ }
620
+
621
+ static async delete(id: number) {
622
+ const index = this.items.findIndex(i => i.id === id)
623
+ if (index === -1) return { success: false as const, error: '{Recurso} not found' }
624
+ this.items.splice(index, 1)
625
+ return { success: true as const, message: '{Recurso} deleted' }
626
+ }
627
+ }
628
+ ```
629
+
630
+ ### Template: Teste Unitario
631
+
632
+ ```typescript
633
+ // tests/unit/{recurso}.test.ts
634
+ import { describe, it, expect, beforeEach } from 'vitest'
635
+ import { {Recurso}Controller } from '@app/server/controllers/{recurso}.controller'
636
+
637
+ describe('{Recurso}Controller', () => {
638
+ beforeEach(() => {
639
+ // Reset do estado para cada teste
640
+ })
641
+
642
+ describe('create', () => {
643
+ it('deve criar um {recurso} com sucesso', async () => {
644
+ const result = await {Recurso}Controller.create({ name: 'Test' })
645
+ expect(result.success).toBe(true)
646
+ if (result.success) {
647
+ expect(result.{recurso}.name).toBe('Test')
648
+ expect(result.{recurso}.id).toBeDefined()
649
+ }
650
+ })
651
+ })
652
+
653
+ describe('getById', () => {
654
+ it('deve retornar erro para id inexistente', async () => {
655
+ const result = await {Recurso}Controller.getById(999)
656
+ expect(result.success).toBe(false)
657
+ })
658
+ })
659
+ })
660
+ ```
661
+
662
+ ---
663
+
664
+ ## Aliases de Import
665
+
666
+ Sempre use aliases em vez de caminhos relativos profundos:
667
+
668
+ ```typescript
669
+ // Aliases disponiveis (tsconfig.json)
670
+ import { ... } from '@core/...' // core/*
671
+ import { ... } from '@app/...' // app/*
672
+ import { ... } from '@server/...' // app/server/*
673
+ import { ... } from '@client/...' // app/client/*
674
+ import { ... } from '@shared/...' // app/shared/*
675
+ import { ... } from '@config' // config/index.ts
676
+ import { ... } from '@config/...' // config/*
677
+ ```
678
+
679
+ ---
680
+
681
+ ## Comandos CLI Essenciais
682
+
683
+ ```bash
684
+ # Desenvolvimento
685
+ bun run dev # Full-stack (backend 3000 + frontend 5173)
686
+ bun run dev --backend-only # Somente backend
687
+ bun run dev --frontend-only # Somente frontend
688
+
689
+ # Build
690
+ bun run build # Build de producao
691
+ bun run start # Executar build
692
+
693
+ # Testes
694
+ bun run test # Vitest
695
+ bun run typecheck # tsc --noEmit
696
+
697
+ # Geradores
698
+ bun run flux g controller NomeController
699
+ bun run flux g route nome-rota
700
+ bun run flux g component NomeComponente
701
+ bun run flux g service NomeService
702
+ bun run flux g plugin nome-plugin
703
+
704
+ # Plugins
705
+ bun run flux plugin:add nome-plugin # Instalar com auditoria
706
+ bun run flux plugin:list # Listar plugins
707
+ bun run flux plugin:remove nome-plugin # Remover plugin
708
+ ```
709
+
710
+ ---
711
+
712
+ ## Regras Criticas
713
+
714
+ ### NUNCA faca
715
+
716
+ 1. **Modificar `core/`** - Framework e read-only. Use plugins, app ou config
717
+ 2. **Envolver Eden Treaty em wrappers** - Quebra type inference
718
+ 3. **Omitir response schemas** - Eden Treaty perde a tipagem no frontend
719
+ 4. **Usar `process.env` diretamente** - Use o sistema de config declarativo
720
+ 5. **Colocar logica de negocio em rotas** - Use controllers/services
721
+ 6. **Habilitar NPM discovery sem whitelist** - Risco de supply chain attack
722
+ 7. **Criar tipos manuais para respostas de API** - Eden Treaty infere automaticamente
723
+ 8. **Usar imports relativos profundos** - Use path aliases (@server, @client, etc.)
724
+ 9. **Armazenar dados nao-serializaveis no state de Live Components**
725
+ 10. **Exportar `defaultState` separado** - Use `static defaultState` dentro da classe
726
+
727
+ ### SEMPRE faca
728
+
729
+ 1. **Trabalhar em `app/`** para codigo da aplicacao
730
+ 2. **Definir response schema** em toda rota (`t.Object()`)
731
+ 3. **Separar rotas e controllers** - Rotas lidam com HTTP, controllers com logica
732
+ 4. **Usar `as const`** em schemas e respostas para preservar tipos literais
733
+ 5. **Definir `static componentName`** em todo Live Component
734
+ 6. **Definir `static defaultState`** dentro da classe do Live Component
735
+ 7. **Usar `declare`** para propriedades de state (TypeScript hint)
736
+ 8. **Adicionar client link** nos Live Components server-side
737
+ 9. **Validar input** com schemas `t.Object()` do Elysia
738
+ 10. **Retornar `{ success, data?, error? }`** como padrao de resposta
739
+
740
+ ---
741
+
742
+ ## Diagnostico de Problemas
743
+
744
+ ### Arvore de Decisao
745
+
746
+ ```
747
+ Problema com tipos no frontend?
748
+ ├── Response schema definido na rota?
749
+ │ ├── NAO → Adicionar response: t.Object({...})
750
+ │ └── SIM → Eden Treaty importado corretamente?
751
+ │ ├── NAO → import { api } from '@/lib/eden-api'
752
+ │ └── SIM → Verificar app.ts exporta o tipo da app
753
+
754
+ "bun: command not found"?
755
+ └── Instalar: curl -fsSL https://bun.sh/install | bash
756
+
757
+ Erro de CORS?
758
+ └── Verificar config/server.config.ts → cors.origins
759
+
760
+ Live Component nao sincroniza?
761
+ ├── $connected e true?
762
+ │ ├── NAO → Verificar WebSocket URL e LiveComponentsProvider
763
+ │ └── SIM → componentName definido corretamente?
764
+ │ ├── NAO → Adicionar static componentName = 'NomeClasse'
765
+ │ └── SIM → State e serializavel (sem functions, Date, etc.)?
766
+
767
+ Plugin nao carrega?
768
+ ├── E plugin NPM?
769
+ │ ├── SIM → PLUGINS_DISCOVER_NPM=true e PLUGINS_ALLOWED configurado?
770
+ │ └── NAO → Esta em plugins/ com export default?
771
+ └── Verificar logs de seguranca no console
772
+
773
+ Config nao carrega?
774
+ ├── Arquivo .env existe?
775
+ ├── Variavel de ambiente esta correta?
776
+ └── Schema usa 'as const' no final?
777
+
778
+ Build falha?
779
+ ├── bunx tsc --noEmit → Erros de TypeScript?
780
+ ├── Imports circulares?
781
+ └── Dependencia faltando? → bun install
782
+ ```
783
+
784
+ ---
785
+
786
+ ## Contexto para Decisoes
787
+
788
+ ### Quando usar Live Components vs REST API
789
+
790
+ | Cenario | Usar |
791
+ |---------|------|
792
+ | CRUD simples | REST API (routes + controllers) |
793
+ | Dados que atualizam em tempo real | Live Components |
794
+ | Chat, colaboracao | Live Components + Rooms |
795
+ | Dashboard com metricas ao vivo | Live Components |
796
+ | Formularios com validacao | Live Components ($field) |
797
+ | Upload de arquivos | Live Upload (chunked WebSocket) |
798
+ | API publica/integracao | REST API |
799
+ | Webhook/bot externo | REST API + Room HTTP API |
800
+
801
+ ### Quando usar setState vs acesso direto
802
+
803
+ ```typescript
804
+ // 1 propriedade → acesso direto (1 STATE_DELTA)
805
+ this.count++
806
+
807
+ // Multiplas propriedades → setState (1 STATE_DELTA total)
808
+ this.setState({ count: newCount, lastUpdated: now })
809
+
810
+ // State anterior necessario → setState com funcao
811
+ this.setState(prev => ({ count: prev.count + 1 }))
812
+ ```
813
+
814
+ ### Organizacao de pastas por complexidade
815
+
816
+ **App simples:**
817
+ ```
818
+ app/server/
819
+ ├── controllers/ # Logica de negocio
820
+ └── routes/ # Endpoints HTTP
821
+ ```
822
+
823
+ **App complexa:**
824
+ ```
825
+ app/server/
826
+ ├── controllers/ # Orquestram services
827
+ ├── services/ # Logica de negocio complexa
828
+ ├── repositories/ # Acesso a dados
829
+ ├── routes/ # Endpoints HTTP
830
+ └── live/ # Componentes real-time
831
+ ```
832
+
833
+ ---
834
+
835
+ ## Checklist de Qualidade
836
+
837
+ Antes de finalizar qualquer implementacao, verificar:
838
+
839
+ - [ ] Todas as rotas tem `response` schema definido
840
+ - [ ] Controllers retornam `{ success: true/false, ... }`
841
+ - [ ] Tipos usam `as const` onde necessario
842
+ - [ ] Nenhum arquivo em `core/` foi modificado
843
+ - [ ] Path aliases usados (sem `../../../`)
844
+ - [ ] Live Components tem `static componentName` e `static defaultState`
845
+ - [ ] `declare` usado para propriedades de state
846
+ - [ ] Erros tratados com classes de erro do framework
847
+ - [ ] `bun run dev` funciona apos as mudancas
848
+ - [ ] `bunx tsc --noEmit` passa sem erros
849
+
850
+ ---
851
+
852
+ ## Documentacao Complementar
853
+
854
+ Para detalhes especificos, consultar:
855
+
856
+ - **[INDEX.md](./INDEX.md)** - Hub de navegacao
857
+ - **[Routes & Eden Treaty](./resources/routes-eden.md)** - APIs type-safe
858
+ - **[Controllers](./resources/controllers.md)** - Logica de negocio
859
+ - **[Live Components](./resources/live-components.md)** - WebSocket real-time
860
+ - **[Live Rooms](./resources/live-rooms.md)** - Multi-usuario
861
+ - **[Live Auth](./resources/live-auth.md)** - Autenticacao em componentes
862
+ - **[REST Auth](./resources/rest-auth.md)** - Autenticacao REST
863
+ - **[Config System](./config/declarative-system.md)** - Configuracao declarativa
864
+ - **[Anti-Patterns](./patterns/anti-patterns.md)** - O que NAO fazer
865
+ - **[CLI Commands](./reference/cli-commands.md)** - Todos os comandos
866
+ - **[Plugin Hooks](./reference/plugin-hooks.md)** - Hooks disponiveis
867
+ - **[Troubleshooting](./reference/troubleshooting.md)** - Problemas comuns