create-fluxstack 1.17.0 → 1.18.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 (93) hide show
  1. package/LLMD/resources/live-auth.md +462 -465
  2. package/app/client/.live-stubs/LiveAdminPanel.js +15 -0
  3. package/app/client/.live-stubs/LiveCounter.js +9 -0
  4. package/app/client/.live-stubs/LiveForm.js +11 -0
  5. package/app/client/.live-stubs/LiveLocalCounter.js +8 -0
  6. package/app/client/.live-stubs/LivePingPong.js +10 -0
  7. package/app/client/.live-stubs/LiveRoomChat.js +11 -0
  8. package/app/client/.live-stubs/LiveSharedCounter.js +10 -0
  9. package/app/client/.live-stubs/LiveUpload.js +15 -0
  10. package/app/client/src/App.tsx +45 -3
  11. package/app/client/src/components/AppLayout.tsx +10 -1
  12. package/app/client/src/components/ErrorBoundary.tsx +117 -0
  13. package/app/client/src/components/LiveErrorBoundary.tsx +87 -0
  14. package/app/client/src/components/LiveUploadWidget.tsx +10 -14
  15. package/app/client/src/lib/eden-api.ts +6 -0
  16. package/app/client/src/lib/plugin-hooks.ts +82 -0
  17. package/app/client/src/live/AuthDemo.tsx +0 -1
  18. package/app/client/src/live/FormDemo.tsx +1 -1
  19. package/app/client/src/live/PingPongDemo.tsx +4 -1
  20. package/app/client/src/live/RoomChatDemo.tsx +90 -50
  21. package/app/client/src/live/SharedCounterDemo.tsx +5 -0
  22. package/app/server/auth/AuthManager.ts +24 -0
  23. package/app/server/auth/contracts.ts +12 -1
  24. package/app/server/auth/errors.ts +84 -0
  25. package/app/server/auth/guards/TokenGuard.ts +5 -2
  26. package/app/server/auth/index.ts +1 -1
  27. package/app/server/auth/providers/InMemoryProvider.ts +1 -1
  28. package/app/server/index.ts +3 -4
  29. package/app/server/live/LiveAdminPanel.ts +8 -8
  30. package/app/server/live/LiveForm.ts +1 -1
  31. package/app/server/live/LiveProtectedChat.ts +5 -5
  32. package/app/server/live/LiveRoomChat.ts +50 -28
  33. package/app/server/live/LiveUpload.ts +17 -3
  34. package/app/server/live/auto-generated-components.ts +26 -0
  35. package/app/server/live/rooms/ChatRoom.ts +17 -2
  36. package/app/server/routes/auth.routes.ts +29 -20
  37. package/app/server/routes/index.ts +9 -0
  38. package/app/server/routes/room.routes.ts +6 -6
  39. package/config/index.ts +3 -3
  40. package/config/system/app.config.ts +1 -1
  41. package/config/system/auth.config.ts +1 -1
  42. package/config/system/build.config.ts +8 -6
  43. package/config/system/client.config.ts +6 -4
  44. package/config/system/database.config.ts +1 -1
  45. package/config/system/logger.config.ts +1 -1
  46. package/config/system/monitoring.config.ts +6 -4
  47. package/config/system/plugins.config.ts +1 -1
  48. package/config/system/runtime.config.ts +1 -1
  49. package/config/system/server.config.ts +1 -1
  50. package/config/system/services.config.ts +1 -1
  51. package/config/system/session.config.ts +3 -3
  52. package/config/system/system.config.ts +1 -1
  53. package/core/build/vite-plugins.ts +3 -2
  54. package/core/cli/generators/plugin.ts +1 -1
  55. package/core/config/index.ts +8 -1
  56. package/core/framework/server.ts +9 -5
  57. package/core/index.ts +1 -1
  58. package/core/plugins/index.ts +1 -1
  59. package/core/plugins/manager.ts +5 -1
  60. package/core/plugins/types.ts +17 -1
  61. package/core/server/index.ts +5 -2
  62. package/core/server/live/index.ts +8 -71
  63. package/core/server/plugin-client-hooks.ts +97 -0
  64. package/core/types/types.ts +1 -0
  65. package/core/utils/version.ts +1 -1
  66. package/create-fluxstack.ts +1 -1
  67. package/package.json +8 -5
  68. package/src/client/components/ui/StatusBadge.tsx +23 -0
  69. package/core/utils/config-schema.ts +0 -480
  70. package/core/utils/env.ts +0 -305
  71. package/plugins/crypto-auth/README.md +0 -788
  72. package/plugins/crypto-auth/ai-context.md +0 -1282
  73. package/plugins/crypto-auth/cli/make-protected-route.command.ts +0 -383
  74. package/plugins/crypto-auth/client/CryptoAuthClient.ts +0 -302
  75. package/plugins/crypto-auth/client/components/AuthProvider.tsx +0 -131
  76. package/plugins/crypto-auth/client/components/LoginButton.tsx +0 -138
  77. package/plugins/crypto-auth/client/components/ProtectedRoute.tsx +0 -89
  78. package/plugins/crypto-auth/client/components/index.ts +0 -12
  79. package/plugins/crypto-auth/client/index.ts +0 -12
  80. package/plugins/crypto-auth/config/index.ts +0 -34
  81. package/plugins/crypto-auth/index.ts +0 -173
  82. package/plugins/crypto-auth/package.json +0 -66
  83. package/plugins/crypto-auth/server/AuthMiddleware.ts +0 -181
  84. package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +0 -58
  85. package/plugins/crypto-auth/server/CryptoAuthService.ts +0 -186
  86. package/plugins/crypto-auth/server/index.ts +0 -25
  87. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +0 -66
  88. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +0 -26
  89. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +0 -77
  90. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +0 -45
  91. package/plugins/crypto-auth/server/middlewares/helpers.ts +0 -155
  92. package/plugins/crypto-auth/server/middlewares/index.ts +0 -22
  93. package/plugins/crypto-auth/server/middlewares.ts +0 -19
@@ -38,20 +38,28 @@ export class LiveRoomChat extends LiveComponent<typeof LiveRoomChat.defaultState
38
38
  // Load existing custom rooms from directory state
39
39
  this.setState({ customRooms: dir.state.rooms || [] })
40
40
 
41
- // Listen for new rooms being added
42
- const unsubAdd = dir.on('room:added', (entry: DirectoryEntry) => {
43
- const current = this.state.customRooms.filter(r => r.id !== entry.id)
44
- this.setState({ customRooms: [...current, entry] })
45
- })
46
-
47
- // Listen for rooms being removed
48
- const unsubRemove = dir.on('room:removed', (data: { id: string }) => {
49
- this.setState({
50
- customRooms: this.state.customRooms.filter(r => r.id !== data.id)
51
- })
52
- })
53
-
54
- this.directoryUnsubs = [unsubAdd, unsubRemove]
41
+ // Listen for directory events with safe cleanup on error
42
+ const unsubs: (() => void)[] = []
43
+ try {
44
+ // Listen for new rooms being added
45
+ unsubs.push(dir.on('room:added', (entry: DirectoryEntry) => {
46
+ const current = this.state.customRooms.filter(r => r.id !== entry.id)
47
+ this.setState({ customRooms: [...current, entry] })
48
+ }))
49
+
50
+ // Listen for rooms being removed
51
+ unsubs.push(dir.on('room:removed', (data: { id: string }) => {
52
+ this.setState({
53
+ customRooms: this.state.customRooms.filter(r => r.id !== data.id)
54
+ })
55
+ }))
56
+
57
+ this.directoryUnsubs = unsubs
58
+ } catch (error) {
59
+ // Cleanup any listeners that were successfully registered
60
+ unsubs.forEach(fn => fn())
61
+ throw error
62
+ }
55
63
  }
56
64
 
57
65
  async createRoom(payload: { roomId: string; roomName: string; password?: string }) {
@@ -83,14 +91,21 @@ export class LiveRoomChat extends LiveComponent<typeof LiveRoomChat.defaultState
83
91
  createdBy: this.state.username || 'Anonymous'
84
92
  })
85
93
 
86
- // Listen for messages
87
- const unsub = room.on('chat:message', (msg: ChatMessage) => {
88
- const msgs = this.state.messages[roomId] || []
89
- this.setState({
90
- messages: { ...this.state.messages, [roomId]: [...msgs, msg].slice(-100) }
94
+ // Listen for messages with safe cleanup
95
+ let unsub: (() => void) | undefined
96
+ try {
97
+ unsub = room.on('chat:message', (msg: ChatMessage) => {
98
+ const msgs = this.state.messages[roomId] || []
99
+ this.setState({
100
+ messages: { ...this.state.messages, [roomId]: [...msgs, msg].slice(-100) }
101
+ })
91
102
  })
92
- })
93
- this.roomListeners.set(roomId, [unsub])
103
+ this.roomListeners.set(roomId, [unsub])
104
+ } catch (error) {
105
+ unsub?.()
106
+ room.leave()
107
+ throw error
108
+ }
94
109
 
95
110
  this.setState({
96
111
  activeRoom: roomId,
@@ -118,14 +133,21 @@ export class LiveRoomChat extends LiveComponent<typeof LiveRoomChat.defaultState
118
133
  return { success: false, error: 'Senha incorreta' }
119
134
  }
120
135
 
121
- // Listen for chat messages from other members
122
- const unsub = room.on('chat:message', (msg: ChatMessage) => {
123
- const msgs = this.state.messages[roomId] || []
124
- this.setState({
125
- messages: { ...this.state.messages, [roomId]: [...msgs, msg].slice(-100) }
136
+ // Listen for chat messages from other members with safe cleanup
137
+ let unsub: (() => void) | undefined
138
+ try {
139
+ unsub = room.on('chat:message', (msg: ChatMessage) => {
140
+ const msgs = this.state.messages[roomId] || []
141
+ this.setState({
142
+ messages: { ...this.state.messages, [roomId]: [...msgs, msg].slice(-100) }
143
+ })
126
144
  })
127
- })
128
- this.roomListeners.set(roomId, [unsub])
145
+ this.roomListeners.set(roomId, [unsub])
146
+ } catch (error) {
147
+ unsub?.()
148
+ room.leave()
149
+ throw error
150
+ }
129
151
 
130
152
  // Update component state — load existing messages from room state
131
153
  this.setState({
@@ -21,9 +21,23 @@ export class LiveUpload extends LiveComponent<typeof LiveUpload.defaultState> {
21
21
  }
22
22
 
23
23
  async startUpload(payload: { fileName: string; fileSize: number; fileType: string }) {
24
- const normalized = payload.fileName.toLowerCase()
25
- if (normalized.includes('..') || normalized.includes('/') || normalized.includes('\\')) {
26
- throw new Error('Invalid file name')
24
+ const fileName = payload.fileName
25
+
26
+ // Validate filename length
27
+ if (!fileName || fileName.length > 255) {
28
+ throw new Error('Invalid file name: must be 1-255 characters')
29
+ }
30
+
31
+ // Block path traversal, null bytes, and control characters
32
+ if (/[\x00-\x1f]/.test(fileName) || fileName.includes('..') || fileName.includes('/') || fileName.includes('\\')) {
33
+ throw new Error('Invalid file name: contains forbidden characters')
34
+ }
35
+
36
+ // Block Windows reserved names
37
+ const baseName = fileName.split('.')[0].toUpperCase()
38
+ const reserved = ['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'LPT1', 'LPT2', 'LPT3']
39
+ if (reserved.includes(baseName)) {
40
+ throw new Error('Invalid file name: reserved name')
27
41
  }
28
42
 
29
43
  // All file types allowed - no extension blocking
@@ -0,0 +1,26 @@
1
+ // Auto-generated Live Components Registration
2
+ // Generated by @fluxstack/live — DO NOT EDIT MANUALLY
3
+ // Generated at: 2026-04-09T02:31:46.153Z
4
+
5
+ import { LiveAdminPanel } from "./LiveAdminPanel"
6
+ import { LiveCounter } from "./LiveCounter"
7
+ import { LiveForm } from "./LiveForm"
8
+ import { LiveLocalCounter } from "./LiveLocalCounter"
9
+ import { LivePingPong } from "./LivePingPong"
10
+ import { LiveProtectedChat } from "./LiveProtectedChat"
11
+ import { LiveRoomChat } from "./LiveRoomChat"
12
+ import { LiveSharedCounter } from "./LiveSharedCounter"
13
+ import { LiveUpload } from "./LiveUpload"
14
+
15
+ // Component classes array for LiveServer({ components }) option
16
+ export const liveComponentClasses = [
17
+ LiveAdminPanel,
18
+ LiveCounter,
19
+ LiveForm,
20
+ LiveLocalCounter,
21
+ LivePingPong,
22
+ LiveProtectedChat,
23
+ LiveRoomChat,
24
+ LiveSharedCounter,
25
+ LiveUpload,
26
+ ]
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { LiveRoom } from '@fluxstack/live'
4
4
  import type { RoomJoinContext, RoomLeaveContext } from '@fluxstack/live'
5
+ import { createHash, timingSafeEqual } from 'crypto'
5
6
 
6
7
  export interface ChatMessage {
7
8
  id: string
@@ -32,16 +33,30 @@ export class ChatRoom extends LiveRoom<ChatState, ChatMeta, ChatEvents> {
32
33
  static defaultMeta: ChatMeta = { password: null, createdBy: null }
33
34
  static $options = { maxMembers: 100 }
34
35
 
36
+ /** Hash a password using SHA-256. */
37
+ private static hashPassword(password: string): string {
38
+ return createHash('sha256').update(password).digest('hex')
39
+ }
40
+
41
+ /** Constant-time comparison of two hex strings. */
42
+ private static safeCompare(a: string, b: string): boolean {
43
+ const bufA = Buffer.from(a, 'hex')
44
+ const bufB = Buffer.from(b, 'hex')
45
+ if (bufA.length !== bufB.length) return false
46
+ return timingSafeEqual(bufA, bufB)
47
+ }
48
+
35
49
  /** Set a password for this room. Pass null to remove. */
36
50
  setPassword(password: string | null) {
37
- this.meta.password = password
51
+ this.meta.password = password ? ChatRoom.hashPassword(password) : null
38
52
  this.setState({ isPrivate: password !== null })
39
53
  }
40
54
 
41
55
  onJoin(ctx: RoomJoinContext) {
42
56
  // Validate password if room is protected
43
57
  if (this.meta.password) {
44
- if (ctx.payload?.password !== this.meta.password) {
58
+ const provided = ctx.payload?.password
59
+ if (!provided || !ChatRoom.safeCompare(ChatRoom.hashPassword(provided), this.meta.password)) {
45
60
  return false // Rejected — wrong or missing password
46
61
  }
47
62
  }
@@ -16,6 +16,8 @@ import {
16
16
  guest,
17
17
  buildRequestContext,
18
18
  } from '@server/auth'
19
+ import type { Guard, Authenticatable } from '@server/auth'
20
+ import { classifyAuthError } from '@server/auth/errors'
19
21
  import { authConfig } from '@config/system/auth.config'
20
22
 
21
23
  // ===== Schemas TypeBox (para validação + Swagger) =====
@@ -87,9 +89,8 @@ export const authRoutes = new Elysia({
87
89
  // Solução: usar o InMemoryProvider diretamente ou o guard.
88
90
  // Por segurança, vamos usar attempt após criar o user.
89
91
 
90
- // Buscar provider do auth manager config
91
- const providers = (authManager as any).providerInstances as Map<string, any>
92
- const provider = providers.get(providerName)
92
+ // Buscar provider via public API
93
+ const provider = authManager.getProvider(providerName)
93
94
 
94
95
  if (!provider) {
95
96
  set.status = 500
@@ -116,14 +117,15 @@ export const authRoutes = new Elysia({
116
117
  set.status = 201
117
118
  return {
118
119
  success: true as const,
119
- user: user.toJSON() as any,
120
+ user: user.toJSON(),
120
121
  }
121
- } catch (error: any) {
122
- set.status = 422
122
+ } catch (error: unknown) {
123
+ const classified = classifyAuthError(error)
124
+ set.status = classified.status as 422 | 500
123
125
  return {
124
126
  success: false as const,
125
- error: 'RegistrationFailed',
126
- message: error.message ?? 'Failed to register user',
127
+ error: classified.error,
128
+ message: classified.message,
127
129
  }
128
130
  }
129
131
  }, {
@@ -187,26 +189,32 @@ export const authRoutes = new Elysia({
187
189
  await rateLimiter.clear(throttleKey)
188
190
 
189
191
  // Montar response
190
- const response: any = {
192
+ const response: {
193
+ success: true
194
+ user: ReturnType<typeof user.toJSON>
195
+ token?: string
196
+ } = {
191
197
  success: true as const,
192
198
  user: user.toJSON(),
193
199
  }
194
200
 
195
201
  // Se for token guard, incluir token na response
196
202
  if ('getLastGeneratedToken' in guard) {
197
- const token = (guard as any).getLastGeneratedToken()
203
+ const tokenGuard = guard as Guard & { getLastGeneratedToken(): string | null }
204
+ const token = tokenGuard.getLastGeneratedToken()
198
205
  if (token) {
199
206
  response.token = token
200
207
  }
201
208
  }
202
209
 
203
210
  return response
204
- } catch (error: any) {
205
- set.status = 500
211
+ } catch (error: unknown) {
212
+ const classified = classifyAuthError(error)
213
+ set.status = classified.status as 500
206
214
  return {
207
215
  success: false as const,
208
- error: 'LoginFailed',
209
- message: error.message ?? 'An unexpected error occurred.',
216
+ error: classified.error,
217
+ message: classified.message,
210
218
  }
211
219
  }
212
220
  }, {
@@ -226,7 +234,7 @@ export const authRoutes = new Elysia({
226
234
  // ───── Logout ─────
227
235
  .use(auth())
228
236
  .post('/logout', async (ctx) => {
229
- const { auth: guard } = ctx as any
237
+ const guard = (ctx as unknown as { auth: Guard | null }).auth
230
238
 
231
239
  try {
232
240
  if (guard) {
@@ -237,12 +245,13 @@ export const authRoutes = new Elysia({
237
245
  success: true as const,
238
246
  message: 'Logged out successfully.',
239
247
  }
240
- } catch (error: any) {
241
- (ctx as any).set.status = 500
248
+ } catch (error: unknown) {
249
+ const classified = classifyAuthError(error)
250
+ ctx.set.status = classified.status as 500
242
251
  return {
243
252
  success: false as const,
244
- error: 'LogoutFailed',
245
- message: error.message ?? 'Failed to logout.',
253
+ error: classified.error,
254
+ message: classified.message,
246
255
  }
247
256
  }
248
257
  }, {
@@ -261,7 +270,7 @@ export const authRoutes = new Elysia({
261
270
 
262
271
  // ───── Me (current user) ─────
263
272
  .get('/me', async (ctx) => {
264
- const { user } = ctx as any
273
+ const user = (ctx as unknown as { user: Authenticatable }).user
265
274
 
266
275
  return {
267
276
  success: true as const,
@@ -2,6 +2,7 @@ import { Elysia, t } from "elysia"
2
2
  import { usersRoutes } from "./users.routes"
3
3
  import { roomRoutes } from "./room.routes"
4
4
  import { authRoutes } from "./auth.routes"
5
+ import { pluginClientHooks } from "@core/server/plugin-client-hooks"
5
6
 
6
7
  export const apiRoutes = new Elysia({ prefix: "/api" })
7
8
  .get("/", () => ({ message: "🔥 Hot Reload funcionando! FluxStack API v1.4.0 ⚡" }), {
@@ -34,6 +35,14 @@ export const apiRoutes = new Elysia({ prefix: "/api" })
34
35
  description: 'Returns the current health status of the API server'
35
36
  }
36
37
  })
38
+ // Plugin client hooks endpoint
39
+ .get("/__plugins/client-hooks", () => pluginClientHooks.getSignedResponse(), {
40
+ detail: {
41
+ tags: ['Plugins'],
42
+ summary: 'Get plugin client hooks',
43
+ description: 'Returns JavaScript code registered by server-side plugins to be executed on the client'
44
+ }
45
+ })
37
46
  // Register routes
38
47
  .use(authRoutes)
39
48
  .use(usersRoutes)
@@ -4,7 +4,7 @@
4
4
  // enviem mensagens para salas de chat via API REST
5
5
 
6
6
  import { Elysia, t } from 'elysia'
7
- import { liveRoomManager, roomEvents } from '@core/server/live'
7
+ import { liveServer } from '@core/server/live'
8
8
 
9
9
  export const roomRoutes = new Elysia({ prefix: '/rooms' })
10
10
 
@@ -22,9 +22,9 @@ export const roomRoutes = new Elysia({ prefix: '/rooms' })
22
22
 
23
23
  // Emitir evento para a sala
24
24
  // Isso vai:
25
- // 1. Notificar handlers server-side via roomEvents
25
+ // 1. Notificar handlers server-side via liveServer!.roomEvents
26
26
  // 2. Broadcast via WebSocket para frontends
27
- const notified = liveRoomManager.emitToRoom(roomId, 'message:new', message)
27
+ const notified = liveServer!.roomManager.emitToRoom(roomId, 'message:new', message)
28
28
 
29
29
  return {
30
30
  success: true,
@@ -63,7 +63,7 @@ export const roomRoutes = new Elysia({ prefix: '/rooms' })
63
63
  const { roomId } = params
64
64
  const { event, data } = body
65
65
 
66
- const notified = liveRoomManager.emitToRoom(roomId, event, data)
66
+ const notified = liveServer!.roomManager.emitToRoom(roomId, event, data)
67
67
 
68
68
  return {
69
69
  success: true,
@@ -94,8 +94,8 @@ export const roomRoutes = new Elysia({ prefix: '/rooms' })
94
94
 
95
95
  // Obter estatísticas das salas
96
96
  .get('/stats', () => {
97
- const roomStats = liveRoomManager.getStats()
98
- const eventStats = roomEvents.getStats()
97
+ const roomStats = liveServer!.roomManager.getStats()
98
+ const eventStats = liveServer!.roomEvents.getStats()
99
99
 
100
100
  return {
101
101
  success: true,
package/config/index.ts CHANGED
@@ -47,7 +47,7 @@ export { sessionConfig } from './system/session.config'
47
47
  export { fluxStackConfig, config as fluxConfig, type FluxStackConfig } from './fluxstack.config'
48
48
 
49
49
  // Plugin configs (re-exported for convenience)
50
- export { cryptoAuthConfig } from '../plugins/crypto-auth/config'
50
+ export { cryptoAuthConfig } from '@fluxstack/plugin-crypto-auth/config'
51
51
 
52
52
  // ============================================================================
53
53
  // 📝 TYPE EXPORTS
@@ -86,7 +86,7 @@ export type { AuthConfig } from './system/auth.config'
86
86
  export type { SessionConfig } from './system/session.config'
87
87
 
88
88
  // Plugin types
89
- export type { CryptoAuthConfig } from '../plugins/crypto-auth/config'
89
+ export type { CryptoAuthConfig } from '@fluxstack/plugin-crypto-auth/config'
90
90
 
91
91
  // ============================================================================
92
92
  // 🎯 UNIFIED CONFIG OBJECT
@@ -105,7 +105,7 @@ import { databaseConfig } from './system/database.config'
105
105
  import { servicesConfig } from './system/services.config'
106
106
  import { authConfig } from './system/auth.config'
107
107
  import { sessionConfig } from './system/session.config'
108
- import { cryptoAuthConfig } from '../plugins/crypto-auth/config'
108
+ import { cryptoAuthConfig } from '@fluxstack/plugin-crypto-auth/config'
109
109
 
110
110
  /**
111
111
  * All configs in one object
@@ -3,7 +3,7 @@
3
3
  * Core application settings and metadata
4
4
  */
5
5
 
6
- import { defineConfig, config } from '@core/utils/config-schema'
6
+ import { defineConfig, config } from '@fluxstack/config'
7
7
 
8
8
  /**
9
9
  * App configuration schema
@@ -8,7 +8,7 @@
8
8
  * Providers: ONDE buscar usuários (memory, database...)
9
9
  */
10
10
 
11
- import { defineConfig, defineNestedConfig, config } from '@core/utils/config-schema'
11
+ import { defineConfig, defineNestedConfig, config } from '@fluxstack/config'
12
12
 
13
13
  // ===== Defaults =====
14
14
 
@@ -3,16 +3,18 @@
3
3
  * Server build and optimization settings
4
4
  */
5
5
 
6
- import { defineConfig, defineNestedConfig, config } from '@core/utils/config-schema'
7
- import { helpers } from '@core/utils/env'
6
+ import { defineConfig, defineNestedConfig, config } from '@fluxstack/config'
7
+
8
+ const isProduction = process.env.NODE_ENV === 'production'
9
+ const isDevelopment = process.env.NODE_ENV !== 'production'
8
10
 
9
11
  /**
10
12
  * Build optimization schema
11
13
  */
12
14
  const optimizationSchema = {
13
- minify: config.boolean('BUILD_MINIFY', helpers.isProduction()),
15
+ minify: config.boolean('BUILD_MINIFY', isProduction),
14
16
  treeshake: config.boolean('BUILD_TREESHAKE', true),
15
- compress: config.boolean('BUILD_COMPRESS', helpers.isProduction()),
17
+ compress: config.boolean('BUILD_COMPRESS', isProduction),
16
18
  splitChunks: config.boolean('BUILD_SPLIT_CHUNKS', true),
17
19
  bundleAnalyzer: config.boolean('BUILD_BUNDLE_ANALYZER', false),
18
20
  removeUnusedCSS: config.boolean('BUILD_REMOVE_UNUSED_CSS', false),
@@ -25,9 +27,9 @@ const optimizationSchema = {
25
27
  const buildSchema = {
26
28
  target: config.enum('BUILD_TARGET', ['bun', 'node', 'docker'] as const, 'bun', true),
27
29
  outDir: config.string('BUILD_OUT_DIR', 'dist', true),
28
- sourceMaps: config.boolean('BUILD_SOURCE_MAPS', helpers.isDevelopment()),
30
+ sourceMaps: config.boolean('BUILD_SOURCE_MAPS', isDevelopment),
29
31
  clean: config.boolean('BUILD_CLEAN', true),
30
- mode: config.enum('BUILD_MODE', ['development', 'production'] as const, helpers.isProduction() ? 'production' : 'development'),
32
+ mode: config.enum('BUILD_MODE', ['development', 'production'] as const, isProduction ? 'production' : 'development'),
31
33
  external: config.array('BUILD_EXTERNAL', []),
32
34
  optimize: config.boolean('BUILD_OPTIMIZE', true)
33
35
  } as const
@@ -3,8 +3,10 @@
3
3
  * Declarative client, proxy and Vite dev server configuration
4
4
  */
5
5
 
6
- import { defineConfig, defineNestedConfig, config } from '../../core/utils/config-schema'
7
- import { env, helpers } from '../../core/utils/env'
6
+ import { defineConfig, defineNestedConfig, config } from '@fluxstack/config'
7
+
8
+ const isProduction = process.env.NODE_ENV === 'production'
9
+ const isDevelopment = process.env.NODE_ENV !== 'production'
8
10
 
9
11
  /**
10
12
  * Vite Dev Server Configuration
@@ -31,9 +33,9 @@ const viteSchema = {
31
33
  const buildSchema = {
32
34
  outDir: config.string('CLIENT_OUTDIR', 'dist/client'),
33
35
 
34
- sourceMaps: config.boolean('CLIENT_SOURCEMAPS', helpers.isDevelopment()),
36
+ sourceMaps: config.boolean('CLIENT_SOURCEMAPS', isDevelopment),
35
37
 
36
- minify: config.boolean('CLIENT_MINIFY', helpers.isProduction()),
38
+ minify: config.boolean('CLIENT_MINIFY', isProduction),
37
39
 
38
40
  target: config.string('CLIENT_TARGET', 'esnext'),
39
41
 
@@ -3,7 +3,7 @@
3
3
  * Optional database settings for backward compatibility
4
4
  */
5
5
 
6
- import { defineConfig, config } from '@core/utils/config-schema'
6
+ import { defineConfig, config } from '@fluxstack/config'
7
7
 
8
8
  export const databaseConfig = defineConfig({
9
9
  url: config.string('DATABASE_URL', ''),
@@ -3,7 +3,7 @@
3
3
  * Declarative logger config using FluxStack config system
4
4
  */
5
5
 
6
- import { defineConfig, config } from '@core/utils/config-schema'
6
+ import { defineConfig, config } from '@fluxstack/config'
7
7
 
8
8
  export const loggerConfig = defineConfig({
9
9
  // Log level
@@ -3,8 +3,10 @@
3
3
  * Declarative monitoring, metrics and profiling configuration
4
4
  */
5
5
 
6
- import { defineConfig, defineNestedConfig, config } from '@core/utils/config-schema'
7
- import { helpers } from '@core/utils/env'
6
+ import { defineConfig, defineNestedConfig, config } from '@fluxstack/config'
7
+
8
+ const isProduction = process.env.NODE_ENV === 'production'
9
+ const isDevelopment = process.env.NODE_ENV !== 'production'
8
10
 
9
11
  /**
10
12
  * Metrics Configuration Schema
@@ -31,7 +33,7 @@ const metricsSchema = {
31
33
  customMetrics: config.boolean('CUSTOM_METRICS', false),
32
34
 
33
35
  // Metric exporters
34
- exportToConsole: config.boolean('METRICS_EXPORT_CONSOLE', helpers.isDevelopment()),
36
+ exportToConsole: config.boolean('METRICS_EXPORT_CONSOLE', isDevelopment),
35
37
 
36
38
  exportToFile: config.boolean('METRICS_EXPORT_FILE', false),
37
39
 
@@ -54,7 +56,7 @@ const profilingSchema = {
54
56
  sampleRate: {
55
57
  type: 'number' as const,
56
58
  env: 'PROFILING_SAMPLE_RATE',
57
- default: helpers.isProduction() ? 0.01 : 0.1,
59
+ default: isProduction ? 0.01 : 0.1,
58
60
  validate: (value: number) => {
59
61
  if (value < 0 || value > 1) {
60
62
  return 'Sample rate must be between 0 and 1'
@@ -3,7 +3,7 @@
3
3
  * Declarative plugin management configuration
4
4
  */
5
5
 
6
- import { defineConfig, config } from '@core/utils/config-schema'
6
+ import { defineConfig, config } from '@fluxstack/config'
7
7
  import { FLUXSTACK_VERSION } from '@core/utils/version'
8
8
 
9
9
  const defaultPluginConfigs = {
@@ -3,7 +3,7 @@
3
3
  * Configs that can be reloaded without server restart
4
4
  */
5
5
 
6
- import { defineReactiveConfig, config } from '@core/utils/config-schema'
6
+ import { defineReactiveConfig, config } from '@fluxstack/config'
7
7
 
8
8
  /**
9
9
  * Runtime app configuration
@@ -3,7 +3,7 @@
3
3
  * Server-specific settings (port, host, CORS, middleware)
4
4
  */
5
5
 
6
- import { defineConfig, defineNestedConfig, config } from '@core/utils/config-schema'
6
+ import { defineConfig, defineNestedConfig, config } from '@fluxstack/config'
7
7
 
8
8
  /**
9
9
  * CORS configuration schema
@@ -3,7 +3,7 @@
3
3
  * Optional service settings (email, JWT, storage, redis) for compatibility
4
4
  */
5
5
 
6
- import { defineNestedConfig, config } from '@core/utils/config-schema'
6
+ import { defineNestedConfig, config } from '@fluxstack/config'
7
7
 
8
8
  const emailSchema = {
9
9
  host: config.string('MAIL_HOST', 'smtp.example.com'),
@@ -5,7 +5,7 @@
5
5
  * Inspirado no config/session.php do Laravel.
6
6
  */
7
7
 
8
- import { defineConfig, config } from '@core/utils/config-schema'
8
+ import { defineConfig, config } from '@fluxstack/config'
9
9
 
10
10
  const sessionConfigSchema = {
11
11
  /** Driver de sessão (memory = processo, extensível para redis, database) */
@@ -16,8 +16,8 @@ const sessionConfigSchema = {
16
16
  cookieName: config.string('SESSION_COOKIE', 'fluxstack_session'),
17
17
  /** Cookie httpOnly (default: true) */
18
18
  httpOnly: config.boolean('SESSION_HTTP_ONLY', true),
19
- /** Cookie secure - true em produção (default: false) */
20
- secure: config.boolean('SESSION_SECURE', false),
19
+ /** Cookie secure - auto-detects production (default: true in production, false in dev) */
20
+ secure: config.boolean('SESSION_SECURE', process.env.NODE_ENV === 'production'),
21
21
  /** Cookie sameSite */
22
22
  sameSite: config.enum('SESSION_SAME_SITE', ['strict', 'lax', 'none'] as const, 'lax'),
23
23
  /** Cookie path */
@@ -3,7 +3,7 @@
3
3
  * System information and environment variables
4
4
  */
5
5
 
6
- import { defineConfig, config } from '@core/utils/config-schema'
6
+ import { defineConfig, config } from '@fluxstack/config'
7
7
 
8
8
  /**
9
9
  * System environment variables config
@@ -11,7 +11,8 @@ import { resolve } from 'path'
11
11
  import tsconfigPaths from 'vite-tsconfig-paths'
12
12
  import checker from 'vite-plugin-checker'
13
13
  import { liveStripPlugin } from '@fluxstack/live/build'
14
- import { helpers } from '../utils/env'
14
+ import { env } from '@fluxstack/config'
15
+ const isDevelopment = env.get('NODE_ENV', 'development') === 'development'
15
16
 
16
17
  export function fluxstackVitePlugins(): Plugin[] {
17
18
  return [
@@ -20,7 +21,7 @@ export function fluxstackVitePlugins(): Plugin[] {
20
21
  projects: [resolve(import.meta.dirname, '..', '..', 'tsconfig.json')]
21
22
  }),
22
23
  // Only run type checker in development (saves ~5+ minutes in Docker builds)
23
- helpers.isDevelopment() && checker({
24
+ isDevelopment && checker({
24
25
  typescript: true,
25
26
  overlay: true
26
27
  }),
@@ -111,7 +111,7 @@ export class PluginGenerator implements Generator {
111
111
  * Declarative config using FluxStack config system
112
112
  */
113
113
 
114
- import { defineConfig, config } from '@core/utils/config-schema'
114
+ import { defineConfig, config } from '@fluxstack/config'
115
115
 
116
116
  const {{camelName}}ConfigSchema = {
117
117
  // Enable/disable plugin