create-fluxstack 1.12.1 → 1.14.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 (116) 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/patterns/anti-patterns.md +100 -0
  5. package/LLMD/reference/routing.md +39 -39
  6. package/LLMD/resources/live-auth.md +465 -0
  7. package/LLMD/resources/live-components.md +168 -26
  8. package/LLMD/resources/live-logging.md +220 -0
  9. package/LLMD/resources/live-upload.md +59 -8
  10. package/LLMD/resources/rest-auth.md +290 -0
  11. package/README.md +520 -340
  12. package/app/client/index.html +2 -2
  13. package/app/client/public/favicon.svg +46 -0
  14. package/app/client/src/App.tsx +13 -1
  15. package/app/client/src/assets/fluxstack-static.svg +46 -0
  16. package/app/client/src/assets/fluxstack.svg +183 -0
  17. package/app/client/src/components/AppLayout.tsx +139 -9
  18. package/app/client/src/components/BackButton.tsx +13 -13
  19. package/app/client/src/components/DemoPage.tsx +4 -4
  20. package/app/client/src/live/AuthDemo.tsx +334 -0
  21. package/app/client/src/live/ChatDemo.tsx +2 -2
  22. package/app/client/src/live/CounterDemo.tsx +12 -12
  23. package/app/client/src/live/FormDemo.tsx +2 -2
  24. package/app/client/src/live/LiveDebuggerPanel.tsx +779 -0
  25. package/app/client/src/live/RoomChatDemo.tsx +24 -16
  26. package/app/client/src/main.tsx +13 -13
  27. package/app/client/src/pages/ApiTestPage.tsx +6 -6
  28. package/app/client/src/pages/HomePage.tsx +80 -52
  29. package/app/server/auth/AuthManager.ts +213 -0
  30. package/app/server/auth/DevAuthProvider.ts +66 -0
  31. package/app/server/auth/HashManager.ts +123 -0
  32. package/app/server/auth/JWTAuthProvider.example.ts +101 -0
  33. package/app/server/auth/RateLimiter.ts +106 -0
  34. package/app/server/auth/contracts.ts +192 -0
  35. package/app/server/auth/guards/SessionGuard.ts +167 -0
  36. package/app/server/auth/guards/TokenGuard.ts +202 -0
  37. package/app/server/auth/index.ts +174 -0
  38. package/app/server/auth/middleware.ts +163 -0
  39. package/app/server/auth/providers/InMemoryProvider.ts +162 -0
  40. package/app/server/auth/sessions/SessionManager.ts +164 -0
  41. package/app/server/cache/CacheManager.ts +81 -0
  42. package/app/server/cache/MemoryDriver.ts +112 -0
  43. package/app/server/cache/contracts.ts +49 -0
  44. package/app/server/cache/index.ts +42 -0
  45. package/app/server/index.ts +14 -0
  46. package/app/server/live/LiveAdminPanel.ts +174 -0
  47. package/app/server/live/LiveChat.ts +78 -77
  48. package/app/server/live/LiveCounter.ts +1 -0
  49. package/app/server/live/LiveForm.ts +1 -0
  50. package/app/server/live/LiveLocalCounter.ts +38 -32
  51. package/app/server/live/LiveProtectedChat.ts +151 -0
  52. package/app/server/live/LiveRoomChat.ts +1 -0
  53. package/app/server/live/LiveUpload.ts +1 -0
  54. package/app/server/live/register-components.ts +19 -19
  55. package/app/server/routes/auth.routes.ts +278 -0
  56. package/app/server/routes/index.ts +2 -0
  57. package/config/index.ts +8 -0
  58. package/config/system/auth.config.ts +49 -0
  59. package/config/system/runtime.config.ts +4 -0
  60. package/config/system/session.config.ts +33 -0
  61. package/core/build/optimizer.ts +235 -235
  62. package/core/client/LiveComponentsProvider.tsx +76 -5
  63. package/core/client/components/Live.tsx +17 -10
  64. package/core/client/components/LiveDebugger.tsx +1324 -0
  65. package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
  66. package/core/client/hooks/useLiveComponent.ts +58 -5
  67. package/core/client/hooks/useLiveDebugger.ts +392 -0
  68. package/core/client/index.ts +16 -1
  69. package/core/framework/server.ts +36 -4
  70. package/core/plugins/built-in/index.ts +134 -134
  71. package/core/plugins/built-in/live-components/commands/create-live-component.ts +19 -8
  72. package/core/plugins/built-in/monitoring/index.ts +10 -3
  73. package/core/plugins/built-in/vite/index.ts +151 -20
  74. package/core/plugins/config.ts +5 -4
  75. package/core/plugins/discovery.ts +11 -2
  76. package/core/plugins/manager.ts +11 -5
  77. package/core/plugins/module-resolver.ts +1 -1
  78. package/core/plugins/registry.ts +53 -25
  79. package/core/server/index.ts +15 -15
  80. package/core/server/live/ComponentRegistry.ts +134 -50
  81. package/core/server/live/FileUploadManager.ts +188 -24
  82. package/core/server/live/LiveComponentPerformanceMonitor.ts +9 -8
  83. package/core/server/live/LiveDebugger.ts +462 -0
  84. package/core/server/live/LiveLogger.ts +144 -0
  85. package/core/server/live/LiveRoomManager.ts +22 -5
  86. package/core/server/live/StateSignature.ts +704 -643
  87. package/core/server/live/WebSocketConnectionManager.ts +11 -10
  88. package/core/server/live/auth/LiveAuthContext.ts +71 -0
  89. package/core/server/live/auth/LiveAuthManager.ts +304 -0
  90. package/core/server/live/auth/index.ts +19 -0
  91. package/core/server/live/auth/types.ts +179 -0
  92. package/core/server/live/auto-generated-components.ts +8 -2
  93. package/core/server/live/index.ts +16 -0
  94. package/core/server/live/websocket-plugin.ts +323 -22
  95. package/core/server/plugins/static-files-plugin.ts +179 -69
  96. package/core/templates/create-project.ts +0 -3
  97. package/core/types/build.ts +219 -219
  98. package/core/types/plugin.ts +107 -107
  99. package/core/types/types.ts +278 -22
  100. package/core/utils/index.ts +17 -17
  101. package/core/utils/logger/index.ts +5 -2
  102. package/core/utils/logger/startup-banner.ts +82 -82
  103. package/core/utils/version.ts +6 -6
  104. package/package.json +1 -8
  105. package/plugins/crypto-auth/index.ts +6 -0
  106. package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +58 -0
  107. package/plugins/crypto-auth/server/index.ts +24 -21
  108. package/rest-tests/README.md +57 -0
  109. package/rest-tests/auth-token.http +113 -0
  110. package/rest-tests/auth.http +112 -0
  111. package/rest-tests/rooms-token.http +69 -0
  112. package/rest-tests/users-token.http +62 -0
  113. package/.dockerignore +0 -81
  114. package/Dockerfile +0 -70
  115. package/LIVE_COMPONENTS_REVIEW.md +0 -781
  116. package/app/client/src/assets/react.svg +0 -1
@@ -1,77 +1,78 @@
1
- // LiveChat - Chat compartilhado por sala
2
-
3
- import { LiveComponent, type FluxStackWebSocket } from '@core/types/types'
4
-
5
- // Componente Cliente (Ctrl+Click para navegar)
6
- import type { ChatDemo as _Client } from '@client/src/live/ChatDemo'
7
-
8
- export type ChatMessage = {
9
- id: string
10
- user: string
11
- text: string
12
- timestamp: number
13
- }
14
-
15
- export class LiveChat extends LiveComponent<typeof LiveChat.defaultState> {
16
- static componentName = 'LiveChat'
17
- static defaultState = {
18
- messages: [] as ChatMessage[]
19
- }
20
- protected roomType = 'chat'
21
- private maxMessages = 50
22
- private static roomHistory = new Map<string, ChatMessage[]>()
23
-
24
- constructor(initialState: Partial<typeof LiveChat.defaultState> = {}, ws: FluxStackWebSocket, options?: { room?: string; userId?: string }) {
25
- super(initialState, ws, options)
26
-
27
- this.onRoomEvent<ChatMessage>('NEW_MESSAGE', (message) => {
28
- this.addMessage(message)
29
- })
30
-
31
- if (this.room) {
32
- const history = LiveChat.roomHistory.get(this.room) || []
33
- if (history.length > 0) {
34
- this.setState({ messages: history })
35
- }
36
- }
37
- }
38
-
39
- private addMessage(message: ChatMessage) {
40
- const next = [...this.state.messages, message].slice(-this.maxMessages)
41
- if (this.room) {
42
- LiveChat.roomHistory.set(this.room, next)
43
- }
44
- this.setState({ messages: next })
45
- }
46
-
47
- async sendMessage(payload: { user: string; text: string }) {
48
- const text = payload.text?.trim()
49
- const user = payload.user?.trim() || 'anonymous'
50
-
51
- if (!text) {
52
- throw new Error('Message cannot be empty')
53
- }
54
-
55
- if (text.length > 500) {
56
- throw new Error('Message too long')
57
- }
58
-
59
- const message: ChatMessage = {
60
- id: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
61
- user,
62
- text,
63
- timestamp: Date.now()
64
- }
65
-
66
- const next = [...this.state.messages, message].slice(-this.maxMessages)
67
- if (this.room) {
68
- LiveChat.roomHistory.set(this.room, next)
69
- }
70
-
71
- this.emitRoomEventWithState('NEW_MESSAGE', message, {
72
- messages: next
73
- })
74
-
75
- return { success: true }
76
- }
77
- }
1
+ // LiveChat - Chat compartilhado por sala
2
+
3
+ import { LiveComponent, type FluxStackWebSocket } from '@core/types/types'
4
+
5
+ // Componente Cliente (Ctrl+Click para navegar)
6
+ import type { ChatDemo as _Client } from '@client/src/live/ChatDemo'
7
+
8
+ export type ChatMessage = {
9
+ id: string
10
+ user: string
11
+ text: string
12
+ timestamp: number
13
+ }
14
+
15
+ export class LiveChat extends LiveComponent<typeof LiveChat.defaultState> {
16
+ static componentName = 'LiveChat'
17
+ static publicActions = ['sendMessage'] as const
18
+ static defaultState = {
19
+ messages: [] as ChatMessage[]
20
+ }
21
+ protected roomType = 'chat'
22
+ private maxMessages = 50
23
+ private static roomHistory = new Map<string, ChatMessage[]>()
24
+
25
+ constructor(initialState: Partial<typeof LiveChat.defaultState> = {}, ws: FluxStackWebSocket, options?: { room?: string; userId?: string }) {
26
+ super(initialState, ws, options)
27
+
28
+ this.onRoomEvent<ChatMessage>('NEW_MESSAGE', (message) => {
29
+ this.addMessage(message)
30
+ })
31
+
32
+ if (this.room) {
33
+ const history = LiveChat.roomHistory.get(this.room) || []
34
+ if (history.length > 0) {
35
+ this.setState({ messages: history })
36
+ }
37
+ }
38
+ }
39
+
40
+ private addMessage(message: ChatMessage) {
41
+ const next = [...this.state.messages, message].slice(-this.maxMessages)
42
+ if (this.room) {
43
+ LiveChat.roomHistory.set(this.room, next)
44
+ }
45
+ this.setState({ messages: next })
46
+ }
47
+
48
+ async sendMessage(payload: { user: string; text: string }) {
49
+ const text = payload.text?.trim()
50
+ const user = payload.user?.trim() || 'anonymous'
51
+
52
+ if (!text) {
53
+ throw new Error('Message cannot be empty')
54
+ }
55
+
56
+ if (text.length > 500) {
57
+ throw new Error('Message too long')
58
+ }
59
+
60
+ const message: ChatMessage = {
61
+ id: `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
62
+ user,
63
+ text,
64
+ timestamp: Date.now()
65
+ }
66
+
67
+ const next = [...this.state.messages, message].slice(-this.maxMessages)
68
+ if (this.room) {
69
+ LiveChat.roomHistory.set(this.room, next)
70
+ }
71
+
72
+ this.emitRoomEventWithState('NEW_MESSAGE', message, {
73
+ messages: next
74
+ })
75
+
76
+ return { success: true }
77
+ }
78
+ }
@@ -7,6 +7,7 @@ import type { CounterDemo as _Client } from '@client/src/live/CounterDemo'
7
7
 
8
8
  export class LiveCounter extends LiveComponent<typeof LiveCounter.defaultState> {
9
9
  static componentName = 'LiveCounter'
10
+ static publicActions = ['increment', 'decrement', 'reset'] as const
10
11
  static defaultState = {
11
12
  count: 0,
12
13
  lastUpdatedBy: null as string | null,
@@ -7,6 +7,7 @@ import type { FormDemo as _Client } from '@client/src/live/FormDemo'
7
7
 
8
8
  export class LiveForm extends LiveComponent<typeof LiveForm.defaultState> {
9
9
  static componentName = 'LiveForm'
10
+ static publicActions = ['submit', 'reset', 'validate', 'setValue'] as const
10
11
  static defaultState = {
11
12
  name: '',
12
13
  email: '',
@@ -1,32 +1,38 @@
1
- // LiveLocalCounter - Contador sem eventos de sala
2
-
3
- import { LiveComponent } from '@core/types/types'
4
-
5
- // Componente Cliente (Ctrl+Click para navegar)
6
- import type { CounterDemo as _Client } from '@client/src/live/CounterDemo'
7
-
8
- export class LiveLocalCounter extends LiveComponent<typeof LiveLocalCounter.defaultState> {
9
- static componentName = 'LiveLocalCounter'
10
- static defaultState = {
11
- count: 0,
12
- clicks: 0
13
- }
14
-
15
- async increment() {
16
- this.state.count++
17
- this.state.clicks++
18
- return { success: true, count: this.state.count }
19
- }
20
-
21
- async decrement() {
22
- this.state.count--
23
- this.state.clicks++
24
- return { success: true, count: this.state.count }
25
- }
26
-
27
- async reset() {
28
- this.state.count = 0
29
- this.state.clicks++
30
- return { success: true, count: 0 }
31
- }
32
- }
1
+ // LiveLocalCounter - Contador sem eventos de sala
2
+
3
+ import { LiveComponent } from '@core/types/types'
4
+
5
+ // Componente Cliente (Ctrl+Click para navegar)
6
+ import type { CounterDemo as _Client } from '@client/src/live/CounterDemo'
7
+
8
+ export class LiveLocalCounter extends LiveComponent<typeof LiveLocalCounter.defaultState> {
9
+ static componentName = 'LiveLocalCounter'
10
+ static publicActions = ['increment', 'decrement', 'reset'] as const
11
+ static defaultState = {
12
+ count: 0,
13
+ clicks: 0
14
+ }
15
+
16
+ // Declarar propriedades (criadas dinamicamente pelo LiveComponent)
17
+ declare count: number
18
+ declare clicks: number
19
+
20
+ // 🔥 Agora usa this.count diretamente!
21
+ async increment() {
22
+ this.count++
23
+ this.clicks++
24
+ return { success: true, count: this.count }
25
+ }
26
+
27
+ async decrement() {
28
+ this.count--
29
+ this.clicks++
30
+ return { success: true, count: this.count }
31
+ }
32
+
33
+ async reset() {
34
+ this.count = 0
35
+ this.clicks++
36
+ return { success: true, count: 0 }
37
+ }
38
+ }
@@ -0,0 +1,151 @@
1
+ // 🔒 LiveProtectedChat - Exemplo de Live Component com autenticação
2
+ //
3
+ // Demonstra como usar o sistema de auth em Live Components:
4
+ // - static auth: define que o componente requer autenticação
5
+ // - static actionAuth: define permissões por action
6
+ // - this.$auth: acessa o contexto de auth dentro do componente
7
+ //
8
+ // Client usage:
9
+ // import type { LiveProtectedChat as _Client } from '@client/src/live/ProtectedChat'
10
+
11
+ import { LiveComponent } from '@core/types/types'
12
+ import type { LiveComponentAuth, LiveActionAuthMap } from '@core/server/live/auth/types'
13
+
14
+ interface ChatMessage {
15
+ id: number
16
+ userId: string
17
+ text: string
18
+ timestamp: number
19
+ isAdmin: boolean
20
+ }
21
+
22
+ interface ProtectedChatState {
23
+ messages: ChatMessage[]
24
+ userCount: number
25
+ currentUser: string | null
26
+ isAdmin: boolean
27
+ }
28
+
29
+ export class LiveProtectedChat extends LiveComponent<ProtectedChatState> {
30
+ static componentName = 'LiveProtectedChat'
31
+ static publicActions = ['join', 'sendMessage', 'deleteMessage', 'clearMessages', 'getAuthInfo'] as const
32
+
33
+ static defaultState: ProtectedChatState = {
34
+ messages: [],
35
+ userCount: 0,
36
+ currentUser: null,
37
+ isAdmin: false,
38
+ }
39
+
40
+ // 🔒 Auth: componente requer autenticação
41
+ static auth: LiveComponentAuth = {
42
+ required: true,
43
+ }
44
+
45
+ // 🔒 Auth por action: deleteMessage requer permissão 'chat.admin'
46
+ static actionAuth: LiveActionAuthMap = {
47
+ deleteMessage: { permissions: ['chat.admin'] },
48
+ clearMessages: { roles: ['admin'] },
49
+ }
50
+
51
+ protected roomType = 'protected-chat'
52
+
53
+ constructor(
54
+ initialState: Partial<ProtectedChatState>,
55
+ ws: any,
56
+ options?: { room?: string; userId?: string }
57
+ ) {
58
+ super(initialState, ws, options)
59
+
60
+ // Escutar mensagens de outros usuários na sala
61
+ this.onRoomEvent<ChatMessage>('NEW_MESSAGE', (msg) => {
62
+ const messages = [...this.state.messages, msg].slice(-50)
63
+ this.setState({ messages })
64
+ })
65
+
66
+ this.onRoomEvent<{ count: number }>('USER_COUNT', (data) => {
67
+ this.setState({ userCount: data.count })
68
+ })
69
+ }
70
+
71
+ /**
72
+ * Entra na sala e configura info do usuário autenticado
73
+ */
74
+ async join(payload: { room: string }) {
75
+ this.$room(payload.room).join()
76
+
77
+ // 🔒 Usar $auth para identificar o usuário
78
+ const userId = this.$auth.user?.id || this.userId || 'anonymous'
79
+ const isAdmin = this.$auth.hasRole('admin')
80
+
81
+ this.setState({
82
+ currentUser: userId,
83
+ isAdmin,
84
+ })
85
+
86
+ return { success: true, userId, isAdmin }
87
+ }
88
+
89
+ /**
90
+ * Envia mensagem - qualquer usuário autenticado pode enviar
91
+ */
92
+ async sendMessage(payload: { text: string }) {
93
+ if (!payload.text?.trim()) {
94
+ throw new Error('Message cannot be empty')
95
+ }
96
+
97
+ const message: ChatMessage = {
98
+ id: Date.now(),
99
+ userId: this.$auth.user?.id || this.userId || 'unknown',
100
+ text: payload.text.trim(),
101
+ timestamp: Date.now(),
102
+ isAdmin: this.$auth.hasRole('admin'),
103
+ }
104
+
105
+ // Atualiza estado local + notifica outros na sala
106
+ this.emitRoomEventWithState(
107
+ 'NEW_MESSAGE',
108
+ message,
109
+ { messages: [...this.state.messages, message].slice(-50) }
110
+ )
111
+
112
+ return { success: true, messageId: message.id }
113
+ }
114
+
115
+ /**
116
+ * Deleta uma mensagem - requer permissão 'chat.admin'
117
+ * (protegido via static actionAuth)
118
+ */
119
+ async deleteMessage(payload: { messageId: number }) {
120
+ const messages = this.state.messages.filter(m => m.id !== payload.messageId)
121
+ this.setState({ messages })
122
+
123
+ // Notificar outros na sala
124
+ this.emitRoomEvent('MESSAGE_DELETED', { messageId: payload.messageId })
125
+
126
+ return { success: true }
127
+ }
128
+
129
+ /**
130
+ * Limpa todas as mensagens - requer role 'admin'
131
+ * (protegido via static actionAuth)
132
+ */
133
+ async clearMessages() {
134
+ this.setState({ messages: [] })
135
+ this.emitRoomEvent('MESSAGES_CLEARED', {})
136
+ return { success: true }
137
+ }
138
+
139
+ /**
140
+ * Retorna info do usuário autenticado (sem restrição extra)
141
+ */
142
+ async getAuthInfo() {
143
+ return {
144
+ authenticated: this.$auth.authenticated,
145
+ userId: this.$auth.user?.id,
146
+ roles: this.$auth.user?.roles,
147
+ permissions: this.$auth.user?.permissions,
148
+ isAdmin: this.$auth.hasRole('admin'),
149
+ }
150
+ }
151
+ }
@@ -14,6 +14,7 @@ export interface ChatMessage {
14
14
 
15
15
  export class LiveRoomChat extends LiveComponent<typeof LiveRoomChat.defaultState> {
16
16
  static componentName = 'LiveRoomChat'
17
+ static publicActions = ['joinRoom', 'leaveRoom', 'switchRoom', 'sendMessage', 'setUsername'] as const
17
18
  static defaultState = {
18
19
  username: '',
19
20
  activeRoom: null as string | null,
@@ -7,6 +7,7 @@ import type { UploadDemo as _Client } from '@client/src/live/UploadDemo'
7
7
 
8
8
  export class LiveUpload extends LiveComponent<typeof LiveUpload.defaultState> {
9
9
  static componentName = 'LiveUpload'
10
+ static publicActions = ['startUpload', 'updateProgress', 'completeUpload', 'failUpload', 'reset'] as const
10
11
  static defaultState = {
11
12
  status: 'idle' as 'idle' | 'uploading' | 'complete' | 'error',
12
13
  progress: 0,
@@ -1,19 +1,19 @@
1
- // ⚠️ DEPRECATION NOTICE
2
- // This file has been moved to: core/server/live/auto-generated-components.ts
3
- //
4
- // The auto-generated component registration is now located in the core/ directory
5
- // to prevent accidental user modifications and keep framework code separate from
6
- // application code.
7
- //
8
- // If you're looking for component registration logic:
9
- // - Generated file: core/server/live/auto-generated-components.ts (auto-generated during build)
10
- // - Generator: core/build/live-components-generator.ts
11
- // - Import location: app/server/index.ts
12
- //
13
- // To add new Live Components:
14
- // 1. Create your component class in this directory (app/server/live/)
15
- // 2. Extend LiveComponent class
16
- // 3. Run 'bun run build' to regenerate the registration file
17
-
18
- // This file intentionally left empty - do not import
19
- export {}
1
+ // ⚠️ DEPRECATION NOTICE
2
+ // This file has been moved to: core/server/live/auto-generated-components.ts
3
+ //
4
+ // The auto-generated component registration is now located in the core/ directory
5
+ // to prevent accidental user modifications and keep framework code separate from
6
+ // application code.
7
+ //
8
+ // If you're looking for component registration logic:
9
+ // - Generated file: core/server/live/auto-generated-components.ts (auto-generated during build)
10
+ // - Generator: core/build/live-components-generator.ts
11
+ // - Import location: app/server/index.ts
12
+ //
13
+ // To add new Live Components:
14
+ // 1. Create your component class in this directory (app/server/live/)
15
+ // 2. Extend LiveComponent class
16
+ // 3. Run 'bun run build' to regenerate the registration file
17
+
18
+ // This file intentionally left empty - do not import
19
+ export {}