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
package/core/cli/index.ts CHANGED
@@ -1,25 +1,115 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
- import { FluxStackBuilder } from "@/core/build"
4
- import { ProjectCreator } from "@/core/templates/create-project"
5
- import { getConfigSync } from "@/core/config"
6
- import { serverConfig } from "@/config/server.config"
7
- import { clientConfig } from "@/config/client.config"
3
+ /**
4
+ * FluxStack CLI Entry Point
5
+ * Modular command structure with each command in its own file
6
+ */
7
+
8
8
  import { cliRegistry } from "./command-registry"
9
9
  import { pluginDiscovery } from "./plugin-discovery"
10
10
  import { generateCommand, interactiveGenerateCommand } from "./generators/index"
11
- import { startGroup, endGroup, logBox, logInGroup } from "@/core/utils/logger/group-logger"
11
+
12
+ // Import modular commands
13
+ import { builtInCommands } from "./commands"
12
14
 
13
15
  const command = process.argv[2]
14
16
  const args = process.argv.slice(3)
15
17
 
16
18
  // Register built-in commands
17
19
  async function registerBuiltInCommands() {
18
- // Register generate commands
20
+ // Register modular commands from commands/
21
+ for (const cmd of builtInCommands) {
22
+ cliRegistry.register(cmd)
23
+ }
24
+
25
+ // Register generate commands (still in generators/)
19
26
  cliRegistry.register(generateCommand)
20
27
  cliRegistry.register(interactiveGenerateCommand)
21
-
22
- // Register plugin dependency commands
28
+
29
+ // Register plugin commands that use Commander.js (dynamic imports)
30
+ cliRegistry.register({
31
+ name: 'plugin:add',
32
+ description: 'Install and whitelist an NPM plugin securely',
33
+ category: 'Plugins',
34
+ arguments: [
35
+ {
36
+ name: 'plugin-name',
37
+ description: 'Name of the plugin to install (e.g., fluxstack-plugin-auth)',
38
+ required: true
39
+ }
40
+ ],
41
+ options: [
42
+ {
43
+ name: '--skip-audit',
44
+ description: 'Skip npm audit check'
45
+ },
46
+ {
47
+ name: '--skip-confirmation',
48
+ description: 'Skip confirmation prompt'
49
+ }
50
+ ],
51
+ handler: async (args, options, context) => {
52
+ const { createPluginAddCommand } = await import('./commands/plugin-add')
53
+ const cmd = createPluginAddCommand()
54
+ await cmd.parseAsync(['node', 'cli', ...args], { from: 'user' })
55
+ }
56
+ })
57
+
58
+ cliRegistry.register({
59
+ name: 'plugin:remove',
60
+ description: 'Remove plugin from whitelist and optionally uninstall',
61
+ category: 'Plugins',
62
+ aliases: ['plugin:rm'],
63
+ arguments: [
64
+ {
65
+ name: 'plugin-name',
66
+ description: 'Name of the plugin to remove',
67
+ required: true
68
+ }
69
+ ],
70
+ options: [
71
+ {
72
+ name: '--skip-confirmation',
73
+ description: 'Skip confirmation prompt'
74
+ },
75
+ {
76
+ name: '--keep-installed',
77
+ description: 'Keep plugin installed, only remove from whitelist'
78
+ }
79
+ ],
80
+ handler: async (args, options, context) => {
81
+ const { createPluginRemoveCommand } = await import('./commands/plugin-remove')
82
+ const cmd = createPluginRemoveCommand()
83
+ await cmd.parseAsync(['node', 'cli', ...args], { from: 'user' })
84
+ }
85
+ })
86
+
87
+ cliRegistry.register({
88
+ name: 'plugin:list',
89
+ description: 'List all plugins (installed, whitelisted, and discovered)',
90
+ category: 'Plugins',
91
+ aliases: ['plugin:ls'],
92
+ options: [
93
+ {
94
+ name: '--installed',
95
+ description: 'Show only installed NPM plugins'
96
+ },
97
+ {
98
+ name: '--whitelisted',
99
+ description: 'Show only whitelisted plugins'
100
+ },
101
+ {
102
+ name: '--json',
103
+ description: 'Output as JSON'
104
+ }
105
+ ],
106
+ handler: async (args, options, context) => {
107
+ const { createPluginListCommand } = await import('./commands/plugin-list')
108
+ const cmd = createPluginListCommand()
109
+ await cmd.parseAsync(['node', 'cli', ...args], { from: 'user' })
110
+ }
111
+ })
112
+
23
113
  cliRegistry.register({
24
114
  name: 'plugin:deps',
25
115
  description: 'Gerenciar dependências de plugins',
@@ -31,7 +121,7 @@ async function registerBuiltInCommands() {
31
121
 
32
122
  Usage:
33
123
  flux plugin:deps install Install plugin dependencies
34
- flux plugin:deps list List plugin dependencies
124
+ flux plugin:deps list List plugin dependencies
35
125
  flux plugin:deps check Check for dependency conflicts
36
126
  flux plugin:deps clean Clean unused dependencies
37
127
 
@@ -42,15 +132,13 @@ Examples:
42
132
  `)
43
133
  return
44
134
  }
45
-
46
- // Handle subcommands
135
+
47
136
  const subcommand = args[0]
48
137
  const subArgs = args.slice(1)
49
-
50
- // Import dinamicamente para evitar problemas de inicialização
138
+
51
139
  const { createPluginDepsCommand } = await import('./commands/plugin-deps')
52
140
  const cmd = createPluginDepsCommand()
53
-
141
+
54
142
  switch (subcommand) {
55
143
  case 'install':
56
144
  const installCmd = cmd.commands.find(c => c.name() === 'install')
@@ -82,682 +170,23 @@ Examples:
82
170
  }
83
171
  }
84
172
  })
85
-
86
- // Help command
87
- cliRegistry.register({
88
- name: 'help',
89
- description: 'Show help information',
90
- category: 'General',
91
- aliases: ['h', '--help', '-h'],
92
- arguments: [
93
- {
94
- name: 'command',
95
- description: 'Command to show help for',
96
- required: false
97
- }
98
- ],
99
- handler: async (args, options, context) => {
100
- if (args[0]) {
101
- const targetCommand = cliRegistry.get(args[0])
102
- if (targetCommand) {
103
- cliRegistry.showCommandHelp(targetCommand)
104
- } else {
105
- console.error(`❌ Unknown command: ${args[0]}`)
106
- cliRegistry.showHelp()
107
- }
108
- } else {
109
- cliRegistry.showHelp()
110
- }
111
- }
112
- })
113
-
114
- // Dev command
115
- cliRegistry.register({
116
- name: 'dev',
117
- description: 'Start full-stack development server',
118
- category: 'Development',
119
- usage: 'flux dev [options]',
120
- examples: [
121
- 'flux dev # Start development server',
122
- 'flux dev --port 4000 # Start on custom port'
123
- ],
124
- options: [
125
- {
126
- name: 'port',
127
- short: 'p',
128
- description: 'Port for backend server',
129
- type: 'number',
130
- default: serverConfig.server.port
131
- },
132
- {
133
- name: 'frontend-port',
134
- description: 'Port for frontend server',
135
- type: 'number',
136
- default: clientConfig.vite.port
137
- }
138
- ],
139
- handler: async (args, options, context) => {
140
- // Grouped startup messages
141
- startGroup({
142
- title: 'FluxStack Development Server',
143
- icon: '',
144
- color: 'cyan'
145
- })
146
-
147
- logInGroup(`Server: http://localhost:${options.port}`, '')
148
- logInGroup(`API: http://localhost:${options.port}/api`, '')
149
- logInGroup(`Swagger: http://localhost:${options.port}/swagger`, '')
150
- logInGroup('Starting with hot reload...', '')
151
-
152
- endGroup()
153
- console.log('') // Separator line
154
-
155
- const { spawn } = await import("child_process")
156
- const devProcess = spawn("bun", ["--watch", "app/server/index.ts"], {
157
- stdio: "inherit",
158
- cwd: process.cwd(),
159
- env: {
160
- ...process.env,
161
- FRONTEND_PORT: options['frontend-port'].toString(),
162
- BACKEND_PORT: options.port.toString()
163
- }
164
- })
165
-
166
- process.on('SIGINT', () => {
167
- console.log('\n🛑 Shutting down gracefully...')
168
- devProcess.kill('SIGTERM')
169
- setTimeout(() => {
170
- devProcess.kill('SIGKILL')
171
- process.exit(0)
172
- }, 5000)
173
- })
174
-
175
- devProcess.on('close', (code) => {
176
- process.exit(code || 0)
177
- })
178
-
179
- // Keep the CLI running until the child process exits
180
- return new Promise((resolve) => {
181
- devProcess.on('exit', resolve)
182
- })
183
- }
184
- })
185
-
186
- // Build command
187
- cliRegistry.register({
188
- name: 'build',
189
- description: 'Build the application for production',
190
- category: 'Build',
191
- usage: 'flux build [options]',
192
- examples: [
193
- 'flux build # Build both frontend and backend',
194
- 'flux build --frontend-only # Build only frontend',
195
- 'flux build --backend-only # Build only backend'
196
- ],
197
- options: [
198
- {
199
- name: 'frontend-only',
200
- description: 'Build only frontend',
201
- type: 'boolean'
202
- },
203
- {
204
- name: 'backend-only',
205
- description: 'Build only backend',
206
- type: 'boolean'
207
- },
208
- {
209
- name: 'production',
210
- description: 'Build for production (minified)',
211
- type: 'boolean',
212
- default: true
213
- }
214
- ],
215
- handler: async (args, options, context) => {
216
- const config = getConfigSync()
217
-
218
- // Load plugins for build hooks
219
- const { PluginRegistry } = await import('../plugins/registry')
220
- const { PluginManager } = await import('../plugins/manager')
221
- const pluginRegistry = new PluginRegistry({ config, logger: context.logger })
222
- const pluginManager = new PluginManager({ config, logger: context.logger })
223
-
224
- try {
225
- await pluginManager.initialize()
226
- // Sync plugins to registry (same as framework does)
227
- const discoveredPlugins = pluginManager.getRegistry().getAll()
228
- for (const plugin of discoveredPlugins) {
229
- if (!pluginRegistry.has(plugin.name)) {
230
- (pluginRegistry as any).plugins.set(plugin.name, plugin)
231
- if (plugin.dependencies) {
232
- (pluginRegistry as any).dependencies.set(plugin.name, plugin.dependencies)
233
- }
234
- }
235
- }
236
- try {
237
- (pluginRegistry as any).updateLoadOrder()
238
- } catch (error) {
239
- const plugins = (pluginRegistry as any).plugins as Map<string, any>
240
- ;(pluginRegistry as any).loadOrder = Array.from(plugins.keys())
241
- }
242
- } catch (error) {
243
- context.logger.warn('Failed to load plugins for build hooks', { error })
244
- }
245
-
246
- const builder = new FluxStackBuilder(config, pluginRegistry)
247
-
248
- if (options['frontend-only']) {
249
- await builder.buildClient()
250
- } else if (options['backend-only']) {
251
- await builder.buildServer()
252
- } else {
253
- await builder.build()
254
- }
255
- }
256
- })
257
-
258
- // Create command
259
- cliRegistry.register({
260
- name: 'create',
261
- description: 'Create a new FluxStack project',
262
- category: 'Project',
263
- usage: 'flux create <project-name> [template]',
264
- examples: [
265
- 'flux create my-app # Create basic project',
266
- 'flux create my-app full # Create full-featured project'
267
- ],
268
- arguments: [
269
- {
270
- name: 'project-name',
271
- description: 'Name of the project to create',
272
- required: true,
273
- type: 'string'
274
- },
275
- {
276
- name: 'template',
277
- description: 'Project template to use',
278
- required: false,
279
- type: 'string',
280
- default: 'basic',
281
- choices: ['basic', 'full']
282
- }
283
- ],
284
- handler: async (args, options, context) => {
285
- const [projectName, template] = args
286
-
287
- if (!/^[a-zA-Z0-9-_]+$/.test(projectName)) {
288
- console.error("❌ Project name can only contain letters, numbers, hyphens, and underscores")
289
- return
290
- }
291
-
292
- try {
293
- const creator = new ProjectCreator({
294
- name: projectName,
295
- template: template as 'basic' | 'full' || 'basic'
296
- })
297
-
298
- await creator.create()
299
- } catch (error) {
300
- console.error("❌ Failed to create project:", error instanceof Error ? error.message : String(error))
301
- throw error
302
- }
303
- }
304
- })
305
-
306
- // Make:plugin command (shortcut for generate plugin)
307
- cliRegistry.register({
308
- name: 'make:plugin',
309
- description: 'Create a new FluxStack plugin',
310
- category: 'Plugins',
311
- usage: 'flux make:plugin <name> [options]',
312
- aliases: ['create:plugin'],
313
- examples: [
314
- 'flux make:plugin my-plugin # Create basic plugin',
315
- 'flux make:plugin my-plugin --template full # Create full plugin with server/client',
316
- 'flux make:plugin auth --template server # Create server-only plugin'
317
- ],
318
- arguments: [
319
- {
320
- name: 'name',
321
- description: 'Name of the plugin to create',
322
- required: true,
323
- type: 'string'
324
- }
325
- ],
326
- options: [
327
- {
328
- name: 'template',
329
- short: 't',
330
- description: 'Plugin template to use',
331
- type: 'string',
332
- choices: ['basic', 'full', 'server', 'client'],
333
- default: 'basic'
334
- },
335
- {
336
- name: 'description',
337
- short: 'd',
338
- description: 'Plugin description',
339
- type: 'string',
340
- default: 'A FluxStack plugin'
341
- },
342
- {
343
- name: 'force',
344
- short: 'f',
345
- description: 'Overwrite existing plugin',
346
- type: 'boolean',
347
- default: false
348
- }
349
- ],
350
- handler: async (args, options, context) => {
351
- const [name] = args
352
-
353
- if (!/^[a-zA-Z0-9-_]+$/.test(name)) {
354
- console.error("❌ Plugin name can only contain letters, numbers, hyphens, and underscores")
355
- return
356
- }
357
-
358
- // Use the plugin generator
359
- const { generatorRegistry } = await import('./generators/index')
360
- const pluginGenerator = generatorRegistry.get('plugin')
361
-
362
- if (!pluginGenerator) {
363
- console.error("❌ Plugin generator not found")
364
- return
365
- }
366
-
367
- const generatorContext = {
368
- workingDir: context.workingDir,
369
- config: context.config,
370
- logger: context.logger,
371
- utils: context.utils
372
- }
373
-
374
- const generatorOptions = {
375
- name,
376
- template: options.template,
377
- force: options.force,
378
- dryRun: false,
379
- description: options.description
380
- }
381
-
382
- try {
383
- await pluginGenerator.generate(generatorContext, generatorOptions)
384
- } catch (error) {
385
- console.error("❌ Failed to create plugin:", error instanceof Error ? error.message : String(error))
386
- throw error
387
- }
388
- }
389
- })
390
-
391
- // Frontend command (frontend-only development)
392
- cliRegistry.register({
393
- name: 'frontend',
394
- description: 'Start frontend development server only',
395
- category: 'Development',
396
- usage: 'flux frontend [options]',
397
- examples: [
398
- 'flux frontend # Start Vite dev server on port 5173',
399
- 'flux frontend --port 3000 # Start on custom port'
400
- ],
401
- options: [
402
- {
403
- name: 'port',
404
- short: 'p',
405
- description: 'Port for frontend server',
406
- type: 'number',
407
- default: 5173
408
- }
409
- ],
410
- handler: async (args, options, context) => {
411
- console.log("🎨 FluxStack Frontend Development")
412
- console.log(`🌐 Frontend: http://localhost:${options.port}`)
413
- console.log("📦 Starting Vite dev server...")
414
- console.log()
415
-
416
- const { spawn } = await import("child_process")
417
- const frontendProcess = spawn("vite", ["--config", "vite.config.ts", "--port", options.port.toString()], {
418
- stdio: "inherit",
419
- cwd: process.cwd()
420
- })
421
173
 
422
- process.on('SIGINT', () => {
423
- frontendProcess.kill('SIGINT')
424
- process.exit(0)
425
- })
426
-
427
- return new Promise((resolve) => {
428
- frontendProcess.on('exit', resolve)
429
- })
430
- }
431
- })
432
-
433
- // Backend command (backend-only development)
434
- cliRegistry.register({
435
- name: 'backend',
436
- description: 'Start backend development server only',
437
- category: 'Development',
438
- usage: 'flux backend [options]',
439
- examples: [
440
- 'flux backend # Start backend on port 3001',
441
- 'flux backend --port 4000 # Start on custom port'
442
- ],
443
- options: [
444
- {
445
- name: 'port',
446
- short: 'p',
447
- description: 'Port for backend server',
448
- type: 'number',
449
- default: 3001
450
- }
451
- ],
452
- handler: async (args, options, context) => {
453
- console.log("⚡ FluxStack Backend Development")
454
- console.log(`🚀 API Server: http://localhost:${options.port}`)
455
- console.log("📦 Starting backend with hot reload...")
456
- console.log()
457
-
458
- // Ensure backend-only.ts exists
459
- const { ensureBackendEntry } = await import("../utils/regenerate-files")
460
- await ensureBackendEntry()
461
-
462
- // Start backend with Bun watch for hot reload
463
- const { spawn } = await import("child_process")
464
- const backendProcess = spawn("bun", ["--watch", "app/server/backend-only.ts"], {
465
- stdio: "inherit",
466
- cwd: process.cwd(),
467
- env: {
468
- ...process.env,
469
- BACKEND_PORT: options.port.toString()
470
- }
471
- })
472
-
473
- // Handle process cleanup
474
- process.on('SIGINT', () => {
475
- backendProcess.kill('SIGINT')
476
- process.exit(0)
477
- })
478
-
479
- return new Promise((resolve) => {
480
- backendProcess.on('exit', resolve)
481
- })
482
- }
483
- })
484
-
485
- // Start command (production server)
486
- cliRegistry.register({
487
- name: 'start',
488
- description: 'Start production server',
489
- category: 'Production',
490
- usage: 'flux start',
491
- examples: [
492
- 'flux start # Start production server from dist/'
493
- ],
494
- handler: async (args, options, context) => {
495
- console.log("🚀 Starting FluxStack production server...")
496
- const { join } = await import("path")
497
- await import(join(process.cwd(), "dist", "index.js"))
498
- }
499
- })
500
-
501
- // Build:frontend command (shortcut)
502
- cliRegistry.register({
503
- name: 'build:frontend',
504
- description: 'Build frontend only (shortcut for build --frontend-only)',
505
- category: 'Build',
506
- usage: 'flux build:frontend',
507
- examples: [
508
- 'flux build:frontend # Build only frontend'
509
- ],
510
- handler: async (args, options, context) => {
511
- const config = getConfigSync()
512
- const builder = new FluxStackBuilder(config)
513
- await builder.buildClient()
514
- }
515
- })
516
-
517
- // Build:backend command (shortcut)
518
- cliRegistry.register({
519
- name: 'build:backend',
520
- description: 'Build backend only (shortcut for build --backend-only)',
521
- category: 'Build',
522
- usage: 'flux build:backend',
523
- examples: [
524
- 'flux build:backend # Build only backend'
525
- ],
526
- handler: async (args, options, context) => {
527
- const config = getConfigSync()
528
- const builder = new FluxStackBuilder(config)
529
- await builder.buildServer()
530
- }
531
- })
532
-
533
- // Build:exe command (compile to executable)
534
- cliRegistry.register({
535
- name: 'build:exe',
536
- description: 'Compile application to standalone executable',
537
- category: 'Build',
538
- usage: 'flux build:exe [options]',
539
- examples: [
540
- 'flux build:exe # Compile for current platform (auto-detected)',
541
- 'flux build:exe --target bun-linux-x64 # Compile for specific target',
542
- 'flux build:exe --name MyApp # Compile with custom name',
543
- 'flux build:exe --windows-hide-console # Hide console window on Windows'
544
- ],
545
- options: [
546
- {
547
- name: 'name',
548
- short: 'n',
549
- description: 'Name for the executable file',
550
- type: 'string',
551
- default: 'CLauncher'
552
- },
553
- {
554
- name: 'target',
555
- short: 't',
556
- description: 'Target platform (if not specified, builds all Windows targets)',
557
- type: 'string',
558
- choices: [
559
- 'bun-windows-x64',
560
- 'bun-windows-x64-baseline',
561
- 'bun-linux-x64',
562
- 'bun-linux-x64-baseline',
563
- 'bun-linux-arm64',
564
- 'bun-darwin-x64',
565
- 'bun-darwin-arm64'
566
- ]
567
- },
568
- {
569
- name: 'windows-hide-console',
570
- description: 'Hide console window on Windows',
571
- type: 'boolean',
572
- default: false
573
- },
574
- {
575
- name: 'windows-icon',
576
- description: 'Path to .ico file for Windows executable',
577
- type: 'string'
578
- },
579
- {
580
- name: 'windows-title',
581
- description: 'Product name for Windows executable',
582
- type: 'string'
583
- },
584
- {
585
- name: 'windows-publisher',
586
- description: 'Company name for Windows executable',
587
- type: 'string'
588
- },
589
- {
590
- name: 'windows-version',
591
- description: 'Version string for Windows executable (e.g. 1.2.3.4)',
592
- type: 'string'
593
- },
594
- {
595
- name: 'windows-description',
596
- description: 'Description for Windows executable',
597
- type: 'string'
598
- },
599
- {
600
- name: 'windows-copyright',
601
- description: 'Copyright string for Windows executable',
602
- type: 'string'
603
- }
604
- ],
605
- handler: async (args, options, context) => {
606
- const config = getConfigSync()
607
- const builder = new FluxStackBuilder(config)
608
-
609
- // Build executable options from CLI args with smart defaults
610
- const appName = config.app.name || 'CLauncher'
611
- const appVersion = config.app.version || '1.0.0'
612
- const currentYear = new Date().getFullYear()
613
-
614
- // Convert semver to Windows version format (1.0.0 -> 1.0.0.0)
615
- const windowsVersion = options['windows-version'] ||
616
- (appVersion.split('.').length === 3 ? `${appVersion}.0` : appVersion)
617
-
618
- // Determine targets to build based on current platform
619
- const getDefaultTargets = (): string[] => {
620
- const platform = process.platform
621
- const arch = process.arch
622
-
623
- if (platform === 'win32') {
624
- return ['bun-windows-x64', 'bun-windows-x64-baseline']
625
- } else if (platform === 'linux') {
626
- if (arch === 'arm64') {
627
- return ['bun-linux-arm64']
628
- }
629
- return ['bun-linux-x64', 'bun-linux-x64-baseline']
630
- } else if (platform === 'darwin') {
631
- if (arch === 'arm64') {
632
- return ['bun-darwin-arm64']
633
- }
634
- return ['bun-darwin-x64']
635
- }
636
-
637
- // Fallback to current platform
638
- return [`bun-${platform}-${arch}`]
639
- }
640
-
641
- const targets: string[] = options.target
642
- ? [options.target]
643
- : getDefaultTargets()
644
-
645
- const results: Array<{ target: string; success: boolean; outputPath?: string; error?: string }> = []
646
-
647
- for (const target of targets) {
648
- // Determine if this is a Windows target
649
- const isWindowsTarget = target.includes('windows')
650
-
651
- // Build executable options for this target
652
- const executableOptions: import("../types/build").BundleOptions = {
653
- target,
654
- executable: isWindowsTarget ? {
655
- windows: {
656
- hideConsole: options['windows-hide-console'],
657
- icon: options['windows-icon'],
658
- title: options['windows-title'] || appName,
659
- publisher: options['windows-publisher'] || appName,
660
- version: windowsVersion,
661
- description: options['windows-description'] || `${appName} Application`,
662
- copyright: options['windows-copyright'] || `Copyright © ${currentYear} ${appName}`
663
- }
664
- } : {}
665
- }
666
-
667
- // Generate output name with target suffix
668
- const baseName = options.name || appName
669
- const targetSuffix = target.replace('bun-', '').replace(/-/g, '_')
670
- const outputName = targets.length > 1 ? `${baseName}_${targetSuffix}` : baseName
671
-
672
- console.log(`\n🔨 Building for ${target}...`)
673
- const result = await builder.buildExecutable(outputName, executableOptions)
674
-
675
- results.push({
676
- target,
677
- success: result.success,
678
- outputPath: result.outputPath,
679
- error: result.error
680
- })
681
-
682
- if (result.success) {
683
- console.log(`✅ ${target}: ${result.outputPath}`)
684
- } else {
685
- console.error(`❌ ${target}: ${result.error}`)
686
- }
687
- }
688
-
689
- // Summary
690
- const successful = results.filter(r => r.success)
691
- const failed = results.filter(r => !r.success)
692
-
693
- console.log('\n' + '═'.repeat(50))
694
- console.log('📊 Build Summary')
695
- console.log('═'.repeat(50))
696
-
697
- if (successful.length > 0) {
698
- console.log(`\n✅ Successful (${successful.length}):`)
699
- for (const r of successful) {
700
- console.log(` • ${r.target} → ${r.outputPath}`)
701
- }
702
- }
703
-
704
- if (failed.length > 0) {
705
- console.log(`\n❌ Failed (${failed.length}):`)
706
- for (const r of failed) {
707
- console.log(` • ${r.target}: ${r.error}`)
708
- }
709
- }
710
-
711
- // Show Windows metadata if any Windows target was built
712
- const hasWindowsTarget = targets.some(t => t.includes('windows'))
713
- if (hasWindowsTarget && successful.length > 0) {
714
- console.log('\n📦 Windows executable metadata:')
715
- if (options['windows-hide-console']) console.log(' • Console window: hidden')
716
- if (options['windows-icon']) console.log(` • Icon: ${options['windows-icon']}`)
717
- console.log(` • Title: ${options['windows-title'] || appName}`)
718
- console.log(` • Publisher: ${options['windows-publisher'] || appName}`)
719
- console.log(` • Version: ${windowsVersion}`)
720
- console.log(` • Description: ${options['windows-description'] || `${appName} Application`}`)
721
- console.log(` • Copyright: ${options['windows-copyright'] || `Copyright © ${currentYear} ${appName}`}`)
722
- }
723
-
724
- if (failed.length > 0) {
725
- process.exit(1)
726
- }
727
- }
728
- })
174
+ // Discover and register plugin-provided commands
175
+ try {
176
+ await pluginDiscovery.discoverAndRegisterCommands()
177
+ } catch (error) {
178
+ // Plugin command discovery is optional
179
+ // console.warn('Failed to discover plugin commands:', error)
180
+ }
729
181
  }
730
182
 
731
- // Main CLI logic
183
+ // Initialize and run CLI
732
184
  async function main() {
733
- // Register built-in commands
734
185
  await registerBuiltInCommands()
735
-
736
- // Discover and register plugin commands
737
- await pluginDiscovery.discoverAndRegisterCommands()
738
-
739
- // Handle special cases first
740
- if (!command || command === 'help' || command === '--help' || command === '-h') {
741
- await cliRegistry.execute('help', args)
742
- return
743
- }
744
-
745
- // Check if it's a registered command (built-in or plugin)
746
- if (cliRegistry.has(command)) {
747
- const exitCode = await cliRegistry.execute(command, args)
748
- process.exit(exitCode)
749
- return
750
- }
751
-
752
- // Command not found - show error and help
753
- console.error(`❌ Unknown command: ${command}`)
754
- console.error()
755
- await cliRegistry.execute('help', args)
756
- process.exit(1)
186
+ await cliRegistry.execute(command, args)
757
187
  }
758
188
 
759
- // Run main CLI
760
- main().catch(error => {
761
- console.error('❌ CLI Error:', error instanceof Error ? error.message : String(error))
189
+ main().catch((error) => {
190
+ console.error('CLI Error:', error)
762
191
  process.exit(1)
763
- })
192
+ })