create-fluxstack 1.10.1 → 1.12.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 (257) hide show
  1. package/.dockerignore +1 -2
  2. package/Dockerfile +8 -8
  3. package/LLMD/INDEX.md +64 -0
  4. package/LLMD/MAINTENANCE.md +197 -0
  5. package/LLMD/MIGRATION.md +156 -0
  6. package/LLMD/config/.gitkeep +1 -0
  7. package/LLMD/config/declarative-system.md +268 -0
  8. package/LLMD/config/environment-vars.md +327 -0
  9. package/LLMD/config/runtime-reload.md +401 -0
  10. package/LLMD/core/.gitkeep +1 -0
  11. package/LLMD/core/build-system.md +599 -0
  12. package/LLMD/core/framework-lifecycle.md +229 -0
  13. package/LLMD/core/plugin-system.md +451 -0
  14. package/LLMD/patterns/.gitkeep +1 -0
  15. package/LLMD/patterns/anti-patterns.md +297 -0
  16. package/LLMD/patterns/project-structure.md +264 -0
  17. package/LLMD/patterns/type-safety.md +440 -0
  18. package/LLMD/reference/.gitkeep +1 -0
  19. package/LLMD/reference/cli-commands.md +250 -0
  20. package/LLMD/reference/plugin-hooks.md +357 -0
  21. package/LLMD/reference/routing.md +39 -0
  22. package/LLMD/reference/troubleshooting.md +364 -0
  23. package/LLMD/resources/.gitkeep +1 -0
  24. package/LLMD/resources/controllers.md +465 -0
  25. package/LLMD/resources/live-components.md +703 -0
  26. package/LLMD/resources/live-rooms.md +482 -0
  27. package/LLMD/resources/live-upload.md +130 -0
  28. package/LLMD/resources/plugins-external.md +617 -0
  29. package/LLMD/resources/routes-eden.md +254 -0
  30. package/README.md +37 -17
  31. package/app/client/index.html +0 -1
  32. package/app/client/src/App.tsx +107 -150
  33. package/app/client/src/components/AppLayout.tsx +68 -0
  34. package/app/client/src/components/BackButton.tsx +13 -0
  35. package/app/client/src/components/DemoPage.tsx +20 -0
  36. package/app/client/src/components/LiveUploadWidget.tsx +204 -0
  37. package/app/client/src/lib/eden-api.ts +85 -60
  38. package/app/client/src/live/ChatDemo.tsx +107 -0
  39. package/app/client/src/live/CounterDemo.tsx +206 -0
  40. package/app/client/src/live/FormDemo.tsx +119 -0
  41. package/app/client/src/live/RoomChatDemo.tsx +242 -0
  42. package/app/client/src/live/UploadDemo.tsx +21 -0
  43. package/app/client/src/main.tsx +4 -1
  44. package/app/client/src/pages/ApiTestPage.tsx +108 -0
  45. package/app/client/src/pages/HomePage.tsx +76 -0
  46. package/app/server/app.ts +1 -4
  47. package/app/server/controllers/users.controller.ts +36 -44
  48. package/app/server/index.ts +25 -35
  49. package/app/server/live/LiveChat.ts +77 -0
  50. package/app/server/live/LiveCounter.ts +67 -0
  51. package/app/server/live/LiveForm.ts +63 -0
  52. package/app/server/live/LiveLocalCounter.ts +32 -0
  53. package/app/server/live/LiveRoomChat.ts +285 -0
  54. package/app/server/live/LiveUpload.ts +81 -0
  55. package/app/server/routes/index.ts +3 -1
  56. package/app/server/routes/room.routes.ts +117 -0
  57. package/app/server/routes/users.routes.ts +35 -27
  58. package/app/shared/types/index.ts +14 -2
  59. package/config/app.config.ts +2 -62
  60. package/config/client.config.ts +2 -95
  61. package/config/database.config.ts +2 -99
  62. package/config/fluxstack.config.ts +25 -45
  63. package/config/index.ts +57 -38
  64. package/config/monitoring.config.ts +2 -114
  65. package/config/plugins.config.ts +2 -80
  66. package/config/server.config.ts +2 -68
  67. package/config/services.config.ts +2 -130
  68. package/config/system/app.config.ts +29 -0
  69. package/config/system/build.config.ts +49 -0
  70. package/config/system/client.config.ts +68 -0
  71. package/config/system/database.config.ts +17 -0
  72. package/config/system/fluxstack.config.ts +114 -0
  73. package/config/{logger.config.ts → system/logger.config.ts} +3 -1
  74. package/config/system/monitoring.config.ts +114 -0
  75. package/config/system/plugins.config.ts +84 -0
  76. package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
  77. package/config/system/server.config.ts +68 -0
  78. package/config/system/services.config.ts +46 -0
  79. package/config/{system.config.ts → system/system.config.ts} +1 -1
  80. package/core/build/flux-plugins-generator.ts +325 -325
  81. package/core/build/index.ts +39 -27
  82. package/core/build/live-components-generator.ts +3 -3
  83. package/core/build/optimizer.ts +235 -235
  84. package/core/cli/command-registry.ts +6 -4
  85. package/core/cli/commands/build.ts +79 -0
  86. package/core/cli/commands/create.ts +54 -0
  87. package/core/cli/commands/dev.ts +101 -0
  88. package/core/cli/commands/help.ts +34 -0
  89. package/core/cli/commands/index.ts +34 -0
  90. package/core/cli/commands/make-plugin.ts +90 -0
  91. package/core/cli/commands/plugin-add.ts +197 -0
  92. package/core/cli/commands/plugin-deps.ts +2 -2
  93. package/core/cli/commands/plugin-list.ts +208 -0
  94. package/core/cli/commands/plugin-remove.ts +170 -0
  95. package/core/cli/generators/component.ts +769 -769
  96. package/core/cli/generators/controller.ts +1 -1
  97. package/core/cli/generators/index.ts +146 -146
  98. package/core/cli/generators/interactive.ts +227 -227
  99. package/core/cli/generators/plugin.ts +2 -2
  100. package/core/cli/generators/prompts.ts +82 -82
  101. package/core/cli/generators/route.ts +6 -6
  102. package/core/cli/generators/service.ts +2 -2
  103. package/core/cli/generators/template-engine.ts +4 -3
  104. package/core/cli/generators/types.ts +2 -2
  105. package/core/cli/generators/utils.ts +191 -191
  106. package/core/cli/index.ts +115 -686
  107. package/core/cli/plugin-discovery.ts +2 -2
  108. package/core/client/LiveComponentsProvider.tsx +60 -8
  109. package/core/client/api/eden.ts +183 -0
  110. package/core/client/api/index.ts +11 -0
  111. package/core/client/components/Live.tsx +104 -0
  112. package/core/client/fluxstack.ts +1 -9
  113. package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
  114. package/core/client/hooks/state-validator.ts +1 -1
  115. package/core/client/hooks/useAuth.ts +48 -48
  116. package/core/client/hooks/useChunkedUpload.ts +85 -35
  117. package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
  118. package/core/client/hooks/useLiveComponent.ts +800 -0
  119. package/core/client/hooks/useLiveUpload.ts +71 -0
  120. package/core/client/hooks/useRoom.ts +409 -0
  121. package/core/client/hooks/useRoomProxy.ts +382 -0
  122. package/core/client/index.ts +17 -68
  123. package/core/client/standalone-entry.ts +8 -0
  124. package/core/client/standalone.ts +74 -53
  125. package/core/client/state/createStore.ts +192 -192
  126. package/core/client/state/index.ts +14 -14
  127. package/core/config/index.ts +70 -291
  128. package/core/config/schema.ts +42 -723
  129. package/core/framework/client.ts +131 -131
  130. package/core/framework/index.ts +7 -7
  131. package/core/framework/server.ts +47 -40
  132. package/core/framework/types.ts +2 -2
  133. package/core/index.ts +23 -4
  134. package/core/live/ComponentRegistry.ts +3 -3
  135. package/core/live/types.ts +77 -0
  136. package/core/plugins/built-in/index.ts +134 -134
  137. package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
  138. package/core/plugins/built-in/live-components/index.ts +1 -1
  139. package/core/plugins/built-in/monitoring/index.ts +111 -47
  140. package/core/plugins/built-in/static/index.ts +1 -1
  141. package/core/plugins/built-in/swagger/index.ts +68 -265
  142. package/core/plugins/built-in/vite/index.ts +85 -185
  143. package/core/plugins/built-in/vite/vite-dev.ts +10 -16
  144. package/core/plugins/config.ts +9 -7
  145. package/core/plugins/dependency-manager.ts +31 -1
  146. package/core/plugins/discovery.ts +19 -7
  147. package/core/plugins/executor.ts +2 -2
  148. package/core/plugins/index.ts +203 -203
  149. package/core/plugins/manager.ts +27 -39
  150. package/core/plugins/module-resolver.ts +19 -8
  151. package/core/plugins/registry.ts +255 -19
  152. package/core/plugins/types.ts +20 -53
  153. package/core/server/framework.ts +66 -43
  154. package/core/server/index.ts +15 -15
  155. package/core/server/live/ComponentRegistry.ts +78 -71
  156. package/core/server/live/FileUploadManager.ts +23 -10
  157. package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
  158. package/core/server/live/LiveRoomManager.ts +261 -0
  159. package/core/server/live/RoomEventBus.ts +234 -0
  160. package/core/server/live/RoomStateManager.ts +172 -0
  161. package/core/server/live/StateSignature.ts +643 -643
  162. package/core/server/live/WebSocketConnectionManager.ts +30 -19
  163. package/core/server/live/auto-generated-components.ts +21 -9
  164. package/core/server/live/index.ts +14 -0
  165. package/core/server/live/websocket-plugin.ts +214 -67
  166. package/core/server/middleware/elysia-helpers.ts +7 -2
  167. package/core/server/middleware/errorHandling.ts +1 -1
  168. package/core/server/middleware/index.ts +31 -31
  169. package/core/server/plugins/database.ts +180 -180
  170. package/core/server/plugins/static-files-plugin.ts +69 -69
  171. package/core/server/plugins/swagger.ts +1 -1
  172. package/core/server/rooms/RoomBroadcaster.ts +357 -0
  173. package/core/server/rooms/RoomSystem.ts +463 -0
  174. package/core/server/rooms/index.ts +13 -0
  175. package/core/server/services/BaseService.ts +1 -1
  176. package/core/server/services/ServiceContainer.ts +1 -1
  177. package/core/server/services/index.ts +8 -8
  178. package/core/templates/create-project.ts +12 -12
  179. package/core/testing/index.ts +9 -9
  180. package/core/testing/setup.ts +73 -73
  181. package/core/types/api.ts +168 -168
  182. package/core/types/build.ts +219 -219
  183. package/core/types/config.ts +56 -26
  184. package/core/types/index.ts +4 -4
  185. package/core/types/plugin.ts +107 -107
  186. package/core/types/types.ts +353 -14
  187. package/core/utils/build-logger.ts +324 -324
  188. package/core/utils/config-schema.ts +480 -480
  189. package/core/utils/env.ts +2 -8
  190. package/core/utils/errors/codes.ts +114 -114
  191. package/core/utils/errors/handlers.ts +36 -1
  192. package/core/utils/errors/index.ts +49 -5
  193. package/core/utils/errors/middleware.ts +113 -113
  194. package/core/utils/helpers.ts +6 -16
  195. package/core/utils/index.ts +17 -17
  196. package/core/utils/logger/colors.ts +114 -114
  197. package/core/utils/logger/config.ts +13 -9
  198. package/core/utils/logger/formatter.ts +82 -82
  199. package/core/utils/logger/group-logger.ts +101 -101
  200. package/core/utils/logger/index.ts +6 -1
  201. package/core/utils/logger/stack-trace.ts +3 -1
  202. package/core/utils/logger/startup-banner.ts +82 -82
  203. package/core/utils/logger/winston-logger.ts +152 -152
  204. package/core/utils/monitoring/index.ts +211 -211
  205. package/core/utils/sync-version.ts +66 -66
  206. package/core/utils/version.ts +1 -1
  207. package/create-fluxstack.ts +8 -7
  208. package/package.json +12 -13
  209. package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
  210. package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
  211. package/plugins/crypto-auth/client/components/index.ts +11 -11
  212. package/plugins/crypto-auth/client/index.ts +11 -11
  213. package/plugins/crypto-auth/config/index.ts +1 -1
  214. package/plugins/crypto-auth/index.ts +4 -4
  215. package/plugins/crypto-auth/package.json +65 -65
  216. package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
  217. package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
  218. package/plugins/crypto-auth/server/index.ts +21 -21
  219. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
  220. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
  221. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
  222. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
  223. package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
  224. package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
  225. package/tsconfig.api-strict.json +16 -0
  226. package/tsconfig.json +48 -52
  227. package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
  228. package/types/global.d.ts +29 -29
  229. package/types/vitest.d.ts +8 -8
  230. package/vite.config.ts +38 -62
  231. package/vitest.config.live.ts +10 -9
  232. package/vitest.config.ts +29 -17
  233. package/app/client/README.md +0 -69
  234. package/app/client/SIMPLIFICATION.md +0 -140
  235. package/app/client/frontend-only.ts +0 -12
  236. package/app/client/src/live/FileUploadExample.tsx +0 -359
  237. package/app/client/src/live/MinimalLiveClock.tsx +0 -47
  238. package/app/client/src/live/QuickUploadTest.tsx +0 -193
  239. package/app/client/tsconfig.app.json +0 -45
  240. package/app/client/tsconfig.json +0 -7
  241. package/app/client/zustand-setup.md +0 -65
  242. package/app/server/backend-only.ts +0 -18
  243. package/app/server/live/LiveClockComponent.ts +0 -215
  244. package/app/server/live/LiveFileUploadComponent.ts +0 -77
  245. package/app/server/routes/env-test.ts +0 -110
  246. package/core/client/hooks/index.ts +0 -7
  247. package/core/client/hooks/useHybridLiveComponent.ts +0 -685
  248. package/core/client/hooks/useTypedLiveComponent.ts +0 -133
  249. package/core/client/hooks/useWebSocket.ts +0 -361
  250. package/core/config/env.ts +0 -546
  251. package/core/config/loader.ts +0 -522
  252. package/core/config/runtime-config.ts +0 -327
  253. package/core/config/validator.ts +0 -540
  254. package/core/server/backend-entry.ts +0 -51
  255. package/core/server/standalone.ts +0 -106
  256. package/core/utils/regenerate-files.ts +0 -69
  257. package/fluxstack.config.ts +0 -354
@@ -0,0 +1,465 @@
1
+ # Controllers & Services
2
+
3
+ **Version:** 1.11.0 | **Updated:** 2025-02-08
4
+
5
+ ## Quick Facts
6
+
7
+ - Controllers handle business logic, separate from routes
8
+ - Use static methods for stateless operations
9
+ - Return structured responses: `{ success: boolean, data?, error? }`
10
+ - Service layer optional for complex business logic
11
+ - Error handling uses FluxStackError classes
12
+ - Database integration via controllers or separate services
13
+
14
+ ## Controller Pattern
15
+
16
+ Controllers separate business logic from route definitions:
17
+
18
+ ```typescript
19
+ // app/server/controllers/users.controller.ts
20
+ import type { CreateUserRequest } from '@app/shared/types'
21
+
22
+ export class UsersController {
23
+ static async getUsers() {
24
+ return {
25
+ success: true as const,
26
+ users: this.users,
27
+ count: this.users.length
28
+ }
29
+ }
30
+
31
+ static async getUserById(id: number) {
32
+ const user = await findUser(id)
33
+ if (!user) {
34
+ return {
35
+ success: false as const,
36
+ error: 'User not found'
37
+ }
38
+ }
39
+ return {
40
+ success: true as const,
41
+ user
42
+ }
43
+ }
44
+
45
+ static async createUser(data: CreateUserRequest) {
46
+ // Validation
47
+ const existingUser = await findByEmail(data.email)
48
+ if (existingUser) {
49
+ return {
50
+ success: false as const,
51
+ error: 'Email already in use'
52
+ }
53
+ }
54
+
55
+ // Business logic
56
+ const newUser = await saveUser(data)
57
+
58
+ return {
59
+ success: true as const,
60
+ user: newUser,
61
+ message: 'User created successfully'
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## Route Integration
68
+
69
+ Routes call controllers, handle HTTP concerns:
70
+
71
+ ```typescript
72
+ // app/server/routes/users.routes.ts
73
+ import { Elysia, t } from 'elysia'
74
+ import { UsersController } from '@app/server/controllers/users.controller'
75
+
76
+ export const usersRoutes = new Elysia({ prefix: '/users' })
77
+ .get('/', async () => UsersController.getUsers(), {
78
+ response: GetUsersResponseSchema
79
+ })
80
+
81
+ .get('/:id', async ({ params, set }) => {
82
+ const id = Number(params.id)
83
+
84
+ if (!Number.isFinite(id)) {
85
+ set.status = 400
86
+ return { success: false, error: 'Invalid ID' }
87
+ }
88
+
89
+ const result = await UsersController.getUserById(id)
90
+
91
+ if (!result.success) {
92
+ set.status = 404
93
+ }
94
+
95
+ return result
96
+ }, {
97
+ params: t.Object({ id: t.String() }),
98
+ response: GetUserResponseSchema
99
+ })
100
+ ```
101
+
102
+ ## Response Structure
103
+
104
+ Consistent response format across all controllers:
105
+
106
+ ```typescript
107
+ // Success response
108
+ {
109
+ success: true,
110
+ data: any,
111
+ message?: string
112
+ }
113
+
114
+ // Error response
115
+ {
116
+ success: false,
117
+ error: string,
118
+ details?: any
119
+ }
120
+ ```
121
+
122
+ Use `as const` for literal types in responses:
123
+
124
+ ```typescript
125
+ return {
126
+ success: true as const, // Type: true (not boolean)
127
+ user: newUser
128
+ }
129
+ ```
130
+
131
+ ## Service Layer Pattern
132
+
133
+ For complex business logic, separate into services:
134
+
135
+ ```typescript
136
+ // app/server/services/user.service.ts
137
+ export class UserService {
138
+ static async validateUserData(data: CreateUserRequest) {
139
+ // Complex validation logic
140
+ if (!this.isValidEmail(data.email)) {
141
+ throw new ValidationError('Invalid email format')
142
+ }
143
+
144
+ const exists = await this.checkEmailExists(data.email)
145
+ if (exists) {
146
+ throw new ConflictError('Email already registered')
147
+ }
148
+ }
149
+
150
+ static async createUserWithProfile(data: CreateUserRequest) {
151
+ // Multi-step business logic
152
+ const user = await this.createUser(data)
153
+ const profile = await this.createProfile(user.id)
154
+ await this.sendWelcomeEmail(user.email)
155
+
156
+ return { user, profile }
157
+ }
158
+
159
+ private static isValidEmail(email: string): boolean {
160
+ // Validation logic
161
+ }
162
+ }
163
+ ```
164
+
165
+ Controller uses service:
166
+
167
+ ```typescript
168
+ // app/server/controllers/users.controller.ts
169
+ import { UserService } from '@app/server/services/user.service'
170
+
171
+ export class UsersController {
172
+ static async createUser(data: CreateUserRequest) {
173
+ try {
174
+ await UserService.validateUserData(data)
175
+ const result = await UserService.createUserWithProfile(data)
176
+
177
+ return {
178
+ success: true as const,
179
+ user: result.user,
180
+ profile: result.profile
181
+ }
182
+ } catch (error) {
183
+ if (error instanceof FluxStackError) {
184
+ return {
185
+ success: false as const,
186
+ error: error.message
187
+ }
188
+ }
189
+ throw error
190
+ }
191
+ }
192
+ }
193
+ ```
194
+
195
+ ## Error Handling
196
+
197
+ Use FluxStackError classes for structured errors:
198
+
199
+ ```typescript
200
+ import {
201
+ ValidationError,
202
+ NotFoundError,
203
+ ConflictError,
204
+ UnauthorizedError,
205
+ DatabaseError
206
+ } from '@core/utils/errors'
207
+
208
+ export class UsersController {
209
+ static async updateUser(id: number, data: UpdateUserRequest) {
210
+ // Validation errors (400)
211
+ if (!data.name || data.name.length < 2) {
212
+ throw new ValidationError('Name must be at least 2 characters')
213
+ }
214
+
215
+ // Not found errors (404)
216
+ const user = await findUser(id)
217
+ if (!user) {
218
+ throw new NotFoundError('User', { id })
219
+ }
220
+
221
+ // Conflict errors (409)
222
+ if (data.email !== user.email) {
223
+ const emailExists = await checkEmailExists(data.email)
224
+ if (emailExists) {
225
+ throw new ConflictError('Email already in use', { email: data.email })
226
+ }
227
+ }
228
+
229
+ // Database errors (500)
230
+ try {
231
+ const updated = await updateUserInDb(id, data)
232
+ return { success: true, user: updated }
233
+ } catch (error) {
234
+ throw new DatabaseError('update', { id, error })
235
+ }
236
+ }
237
+ }
238
+ ```
239
+
240
+ ## Available Error Classes
241
+
242
+ ```typescript
243
+ // Validation (400)
244
+ ValidationError
245
+ InvalidInputError
246
+ MissingRequiredFieldError
247
+
248
+ // Authentication (401)
249
+ UnauthorizedError
250
+ InvalidTokenError
251
+ TokenExpiredError
252
+
253
+ // Authorization (403)
254
+ ForbiddenError
255
+ InsufficientPermissionsError
256
+
257
+ // Not Found (404)
258
+ NotFoundError
259
+ ResourceNotFoundError
260
+ EndpointNotFoundError
261
+
262
+ // Conflict (409)
263
+ ConflictError
264
+ ResourceAlreadyExistsError
265
+
266
+ // Rate Limiting (429)
267
+ RateLimitExceededError
268
+
269
+ // Server Errors (500)
270
+ InternalServerError
271
+ DatabaseError
272
+ ExternalServiceError
273
+
274
+ // Service Unavailable (503)
275
+ ServiceUnavailableError
276
+ MaintenanceModeError
277
+ ```
278
+
279
+ ## Database Integration
280
+
281
+ Example with in-memory store (replace with real database):
282
+
283
+ ```typescript
284
+ export class UsersController {
285
+ private static users: User[] = []
286
+ private static nextId = 1
287
+
288
+ static async getUsers() {
289
+ // In production: const users = await db.select().from(usersTable)
290
+ return {
291
+ success: true as const,
292
+ users: this.users,
293
+ count: this.users.length
294
+ }
295
+ }
296
+
297
+ static async createUser(data: CreateUserRequest) {
298
+ // In production: const user = await db.insert(usersTable).values(data)
299
+ const newUser: User = {
300
+ id: this.nextId++,
301
+ ...data,
302
+ createdAt: new Date()
303
+ }
304
+
305
+ this.users.push(newUser)
306
+
307
+ return {
308
+ success: true as const,
309
+ user: newUser
310
+ }
311
+ }
312
+ }
313
+ ```
314
+
315
+ With Drizzle ORM:
316
+
317
+ ```typescript
318
+ import { db } from '@app/server/db'
319
+ import { users } from '@app/server/db/schema'
320
+ import { eq } from 'drizzle-orm'
321
+
322
+ export class UsersController {
323
+ static async getUsers() {
324
+ const userList = await db.select().from(users)
325
+
326
+ return {
327
+ success: true as const,
328
+ users: userList,
329
+ count: userList.length
330
+ }
331
+ }
332
+
333
+ static async getUserById(id: number) {
334
+ const [user] = await db
335
+ .select()
336
+ .from(users)
337
+ .where(eq(users.id, id))
338
+
339
+ if (!user) {
340
+ return {
341
+ success: false as const,
342
+ error: 'User not found'
343
+ }
344
+ }
345
+
346
+ return {
347
+ success: true as const,
348
+ user
349
+ }
350
+ }
351
+
352
+ static async createUser(data: CreateUserRequest) {
353
+ try {
354
+ const [newUser] = await db
355
+ .insert(users)
356
+ .values(data)
357
+ .returning()
358
+
359
+ return {
360
+ success: true as const,
361
+ user: newUser
362
+ }
363
+ } catch (error) {
364
+ throw new DatabaseError('insert', { error })
365
+ }
366
+ }
367
+ }
368
+ ```
369
+
370
+ ## Organization Patterns
371
+
372
+ ### Simple Apps
373
+ ```
374
+ app/server/
375
+ ├── controllers/
376
+ │ ├── users.controller.ts
377
+ │ ├── posts.controller.ts
378
+ │ └── auth.controller.ts
379
+ └── routes/
380
+ ├── users.routes.ts
381
+ ├── posts.routes.ts
382
+ └── auth.routes.ts
383
+ ```
384
+
385
+ ### Complex Apps
386
+ ```
387
+ app/server/
388
+ ├── controllers/
389
+ │ ├── users.controller.ts
390
+ │ └── posts.controller.ts
391
+ ├── services/
392
+ │ ├── user.service.ts
393
+ │ ├── email.service.ts
394
+ │ └── storage.service.ts
395
+ ├── repositories/
396
+ │ ├── user.repository.ts
397
+ │ └── post.repository.ts
398
+ └── routes/
399
+ ├── users.routes.ts
400
+ └── posts.routes.ts
401
+ ```
402
+
403
+ ## Testing Controllers
404
+
405
+ ```typescript
406
+ // tests/unit/controllers/users.controller.test.ts
407
+ import { describe, it, expect, beforeEach } from 'vitest'
408
+ import { UsersController } from '@app/server/controllers/users.controller'
409
+
410
+ describe('UsersController', () => {
411
+ beforeEach(() => {
412
+ UsersController.resetForTesting()
413
+ })
414
+
415
+ it('should create a user', async () => {
416
+ const result = await UsersController.createUser({
417
+ name: 'John Doe',
418
+ email: 'john@example.com'
419
+ })
420
+
421
+ expect(result.success).toBe(true)
422
+ expect(result.user).toMatchObject({
423
+ name: 'John Doe',
424
+ email: 'john@example.com'
425
+ })
426
+ })
427
+
428
+ it('should return error for duplicate email', async () => {
429
+ await UsersController.createUser({
430
+ name: 'John',
431
+ email: 'john@example.com'
432
+ })
433
+
434
+ const result = await UsersController.createUser({
435
+ name: 'Jane',
436
+ email: 'john@example.com'
437
+ })
438
+
439
+ expect(result.success).toBe(false)
440
+ expect(result.error).toContain('already in use')
441
+ })
442
+ })
443
+ ```
444
+
445
+ ## Critical Rules
446
+
447
+ **ALWAYS:**
448
+ - Separate business logic from routes
449
+ - Return structured `{ success, data?, error? }` responses
450
+ - Use `as const` for literal types
451
+ - Handle errors with FluxStackError classes
452
+ - Validate input in controllers or services
453
+
454
+ **NEVER:**
455
+ - Put business logic directly in routes
456
+ - Return raw data without success/error structure
457
+ - Ignore error handling
458
+ - Mix database queries with route handlers
459
+ - Forget to set HTTP status codes in routes
460
+
461
+ ## Related
462
+
463
+ - [Routes with Eden Treaty](./routes-eden.md)
464
+ - [Project Structure](../patterns/project-structure.md)
465
+ - [Anti-Patterns](../patterns/anti-patterns.md)