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,208 @@
1
+ /**
2
+ * FluxStack CLI - Plugin List Command
3
+ * List all plugins (installed, whitelisted, discovered)
4
+ */
5
+
6
+ import { Command } from 'commander'
7
+ import { readFileSync, existsSync } from 'fs'
8
+ import { join } from 'path'
9
+ import chalk from 'chalk'
10
+
11
+ export function createPluginListCommand(): Command {
12
+ const command = new Command('plugin:list')
13
+ .description('List all plugins (installed, whitelisted, and discovered)')
14
+ .option('--installed', 'Show only installed NPM plugins')
15
+ .option('--whitelisted', 'Show only whitelisted plugins')
16
+ .option('--json', 'Output as JSON')
17
+ .action(async (options: { installed?: boolean; whitelisted?: boolean; json?: boolean }) => {
18
+ try {
19
+ const info = getPluginInfo()
20
+
21
+ if (options.json) {
22
+ console.log(JSON.stringify(info, null, 2))
23
+ return
24
+ }
25
+
26
+ console.log(chalk.blue('\n🔌 FluxStack Plugin Status\n'))
27
+
28
+ // Configuration
29
+ console.log(chalk.bold('⚙️ Configuration:'))
30
+ console.log(chalk.gray(` NPM Plugin Discovery: ${info.config.npmDiscoveryEnabled ? chalk.green('enabled') : chalk.red('disabled')}`))
31
+ console.log(chalk.gray(` Project Plugin Discovery: ${info.config.projectDiscoveryEnabled ? chalk.green('enabled') : chalk.red('disabled')}`))
32
+ console.log()
33
+
34
+ // Whitelisted plugins
35
+ if (!options.installed) {
36
+ console.log(chalk.bold('🛡️ Whitelisted NPM Plugins:'))
37
+ if (info.whitelisted.length === 0) {
38
+ console.log(chalk.gray(' (none)'))
39
+ } else {
40
+ info.whitelisted.forEach(plugin => {
41
+ const isInstalled = info.installed.includes(plugin)
42
+ const status = isInstalled ? chalk.green('✓ installed') : chalk.yellow('⚠ not installed')
43
+ console.log(chalk.gray(` • ${plugin} ${status}`))
44
+ })
45
+ }
46
+ console.log()
47
+ }
48
+
49
+ // Installed NPM plugins
50
+ if (!options.whitelisted) {
51
+ console.log(chalk.bold('📦 Installed NPM Plugins:'))
52
+ if (info.installed.length === 0) {
53
+ console.log(chalk.gray(' (none)'))
54
+ } else {
55
+ info.installed.forEach(plugin => {
56
+ const isWhitelisted = info.whitelisted.includes(plugin)
57
+ let status = ''
58
+ if (!info.config.npmDiscoveryEnabled) {
59
+ status = chalk.red('✗ discovery disabled')
60
+ } else if (!isWhitelisted) {
61
+ status = chalk.red('✗ not whitelisted (blocked)')
62
+ } else {
63
+ status = chalk.green('✓ whitelisted (loaded)')
64
+ }
65
+ console.log(chalk.gray(` • ${plugin} ${status}`))
66
+ })
67
+ }
68
+ console.log()
69
+ }
70
+
71
+ // Project plugins (from plugins/ directory)
72
+ console.log(chalk.bold('📁 Project Plugins (plugins/):'))
73
+ if (info.projectPlugins.length === 0) {
74
+ console.log(chalk.gray(' (none found)'))
75
+ } else {
76
+ info.projectPlugins.forEach(plugin => {
77
+ const status = info.config.projectDiscoveryEnabled
78
+ ? chalk.green('✓ auto-discovered')
79
+ : chalk.red('✗ discovery disabled')
80
+ console.log(chalk.gray(` • ${plugin} ${status}`))
81
+ })
82
+ }
83
+ console.log()
84
+
85
+ // Summary
86
+ console.log(chalk.bold('📊 Summary:'))
87
+ console.log(chalk.gray(` Total NPM plugins installed: ${info.installed.length}`))
88
+ console.log(chalk.gray(` Total NPM plugins whitelisted: ${info.whitelisted.length}`))
89
+ console.log(chalk.gray(` Total project plugins: ${info.projectPlugins.length}`))
90
+
91
+ const blockedCount = info.installed.filter(p => !info.whitelisted.includes(p)).length
92
+ if (blockedCount > 0) {
93
+ console.log(chalk.yellow(` ⚠️ ${blockedCount} installed plugin(s) blocked (not whitelisted)`))
94
+ }
95
+ console.log()
96
+
97
+ // Help
98
+ if (info.installed.length > 0 && !info.config.npmDiscoveryEnabled) {
99
+ console.log(chalk.yellow('💡 Tip: Enable NPM plugin discovery with:'))
100
+ console.log(chalk.gray(' echo "PLUGINS_DISCOVER_NPM=true" >> .env'))
101
+ console.log()
102
+ }
103
+
104
+ if (blockedCount > 0 && info.config.npmDiscoveryEnabled) {
105
+ console.log(chalk.yellow('💡 Tip: Add blocked plugins to whitelist with:'))
106
+ console.log(chalk.gray(' bun run fluxstack plugin:add <plugin-name>'))
107
+ console.log()
108
+ }
109
+
110
+ } catch (error) {
111
+ console.error(chalk.red('\n❌ Failed to list plugins:'))
112
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)))
113
+ process.exit(1)
114
+ }
115
+ })
116
+
117
+ return command
118
+ }
119
+
120
+ interface PluginInfo {
121
+ config: {
122
+ npmDiscoveryEnabled: boolean
123
+ projectDiscoveryEnabled: boolean
124
+ }
125
+ whitelisted: string[]
126
+ installed: string[]
127
+ projectPlugins: string[]
128
+ }
129
+
130
+ /**
131
+ * Get plugin information from package.json and .env
132
+ */
133
+ function getPluginInfo(): PluginInfo {
134
+ const info: PluginInfo = {
135
+ config: {
136
+ npmDiscoveryEnabled: false,
137
+ projectDiscoveryEnabled: true,
138
+ },
139
+ whitelisted: [],
140
+ installed: [],
141
+ projectPlugins: [],
142
+ }
143
+
144
+ // Read .env for configuration
145
+ const envPath = join(process.cwd(), '.env')
146
+ if (existsSync(envPath)) {
147
+ const envContent = readFileSync(envPath, 'utf-8')
148
+
149
+ // Check NPM discovery enabled
150
+ const npmDiscoveryMatch = envContent.match(/^PLUGINS_DISCOVER_NPM=(.*)$/m)
151
+ if (npmDiscoveryMatch) {
152
+ info.config.npmDiscoveryEnabled = npmDiscoveryMatch[1].toLowerCase() === 'true'
153
+ }
154
+
155
+ // Check project discovery enabled
156
+ const projectDiscoveryMatch = envContent.match(/^PLUGINS_DISCOVER_PROJECT=(.*)$/m)
157
+ if (projectDiscoveryMatch) {
158
+ info.config.projectDiscoveryEnabled = projectDiscoveryMatch[1].toLowerCase() === 'true'
159
+ }
160
+
161
+ // Get whitelisted plugins
162
+ const whitelistMatch = envContent.match(/^PLUGINS_ALLOWED=(.*)$/m)
163
+ if (whitelistMatch) {
164
+ info.whitelisted = whitelistMatch[1]
165
+ .split(',')
166
+ .map(p => p.trim())
167
+ .filter(p => p.length > 0)
168
+ }
169
+ }
170
+
171
+ // Read package.json for installed plugins
172
+ const packageJsonPath = join(process.cwd(), 'package.json')
173
+ if (existsSync(packageJsonPath)) {
174
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))
175
+ const allDeps = {
176
+ ...packageJson.dependencies,
177
+ ...packageJson.devDependencies,
178
+ }
179
+
180
+ // Find FluxStack plugins
181
+ const pluginPatterns = [
182
+ /^fluxstack-plugin-/,
183
+ /^fplugin-/,
184
+ /^@fluxstack\/plugin-/,
185
+ /^@fplugin\//,
186
+ ]
187
+
188
+ info.installed = Object.keys(allDeps).filter(name =>
189
+ pluginPatterns.some(pattern => pattern.test(name))
190
+ )
191
+ }
192
+
193
+ // Scan plugins/ directory for project plugins
194
+ const pluginsDir = join(process.cwd(), 'plugins')
195
+ if (existsSync(pluginsDir)) {
196
+ const fs = require('fs')
197
+ try {
198
+ const entries = fs.readdirSync(pluginsDir, { withFileTypes: true })
199
+ info.projectPlugins = entries
200
+ .filter((entry: any) => entry.isDirectory())
201
+ .map((entry: any) => entry.name)
202
+ } catch (error) {
203
+ // Ignore errors reading directory
204
+ }
205
+ }
206
+
207
+ return info
208
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * FluxStack CLI - Plugin Remove Command
3
+ * Safely remove and un-whitelist NPM plugins
4
+ */
5
+
6
+ import { Command } from 'commander'
7
+ import { readFileSync, writeFileSync, existsSync } from 'fs'
8
+ import { join } from 'path'
9
+ import { $ } from 'bun'
10
+ import chalk from 'chalk'
11
+
12
+ interface PluginRemoveOptions {
13
+ skipConfirmation?: boolean
14
+ keepInstalled?: boolean
15
+ }
16
+
17
+ export function createPluginRemoveCommand(): Command {
18
+ const command = new Command('plugin:remove')
19
+ .description('Remove plugin from whitelist and optionally uninstall')
20
+ .argument('<plugin-name>', 'Name of the plugin to remove (e.g., fluxstack-plugin-auth)')
21
+ .option('--skip-confirmation', 'Skip confirmation prompt')
22
+ .option('--keep-installed', 'Keep plugin installed, only remove from whitelist')
23
+ .action(async (pluginName: string, options: PluginRemoveOptions) => {
24
+ console.log(chalk.blue('\n🔌 FluxStack Plugin Remover\n'))
25
+
26
+ try {
27
+ // 1. Check if plugin is installed
28
+ const packageJsonPath = join(process.cwd(), 'package.json')
29
+ if (!existsSync(packageJsonPath)) {
30
+ console.error(chalk.red('❌ package.json not found'))
31
+ process.exit(1)
32
+ }
33
+
34
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))
35
+ const isInstalled =
36
+ packageJson.dependencies?.[pluginName] ||
37
+ packageJson.devDependencies?.[pluginName]
38
+
39
+ if (!isInstalled && !options.keepInstalled) {
40
+ console.log(chalk.yellow(`⚠️ Plugin ${pluginName} is not installed`))
41
+ console.log(chalk.yellow(' Will only remove from whitelist\n'))
42
+ }
43
+
44
+ // 2. Confirmation prompt (unless skipped)
45
+ if (!options.skipConfirmation) {
46
+ const action = options.keepInstalled
47
+ ? 'remove from whitelist'
48
+ : 'uninstall and remove from whitelist'
49
+
50
+ const answer = prompt(chalk.yellow(`Remove ${pluginName}? This will ${action}. (yes/no): `))
51
+ if (answer?.toLowerCase() !== 'yes' && answer?.toLowerCase() !== 'y') {
52
+ console.log(chalk.red('❌ Removal cancelled'))
53
+ process.exit(0)
54
+ }
55
+ }
56
+
57
+ // 3. Remove from whitelist
58
+ console.log(chalk.blue('\n🔧 Updating configuration...\n'))
59
+ const removed = removeFromWhitelist(pluginName)
60
+
61
+ if (!removed) {
62
+ console.log(chalk.yellow(`⚠️ Plugin ${pluginName} was not in whitelist`))
63
+ } else {
64
+ console.log(chalk.gray(` • Removed ${pluginName} from PLUGINS_ALLOWED`))
65
+ }
66
+
67
+ // 4. Uninstall plugin (unless --keep-installed)
68
+ if (!options.keepInstalled && isInstalled) {
69
+ console.log(chalk.blue(`\n📦 Uninstalling ${pluginName}...\n`))
70
+ await $`bun remove ${pluginName}`.quiet()
71
+ console.log(chalk.green(`✅ Plugin uninstalled successfully`))
72
+ }
73
+
74
+ // 5. Check if should disable NPM discovery
75
+ checkAndDisableNpmDiscovery()
76
+
77
+ // 6. Success message
78
+ console.log(chalk.green('\n✅ Plugin removal complete!\n'))
79
+ console.log(chalk.blue('📋 What was done:'))
80
+ console.log(chalk.gray(` • Removed ${pluginName} from whitelist (PLUGINS_ALLOWED)`))
81
+ if (!options.keepInstalled && isInstalled) {
82
+ console.log(chalk.gray(` • Uninstalled ${pluginName}`))
83
+ }
84
+
85
+ console.log(chalk.blue('\n🚀 Next steps:'))
86
+ console.log(chalk.gray(' 1. Restart your dev server: bun run dev'))
87
+ console.log(chalk.gray(' 2. Plugin will no longer be loaded'))
88
+
89
+ } catch (error) {
90
+ console.error(chalk.red('\n❌ Failed to remove plugin:'))
91
+ console.error(chalk.red(error instanceof Error ? error.message : String(error)))
92
+ process.exit(1)
93
+ }
94
+ })
95
+
96
+ return command
97
+ }
98
+
99
+ /**
100
+ * Remove plugin from whitelist in .env file
101
+ */
102
+ function removeFromWhitelist(pluginName: string): boolean {
103
+ const envPath = join(process.cwd(), '.env')
104
+
105
+ if (!existsSync(envPath)) {
106
+ return false
107
+ }
108
+
109
+ let envContent = readFileSync(envPath, 'utf-8')
110
+ const allowedPluginsRegex = /^PLUGINS_ALLOWED=(.*)$/m
111
+ const match = envContent.match(allowedPluginsRegex)
112
+
113
+ if (!match) {
114
+ return false
115
+ }
116
+
117
+ const currentPlugins = match[1]
118
+ .split(',')
119
+ .map(p => p.trim())
120
+ .filter(p => p.length > 0)
121
+
122
+ if (!currentPlugins.includes(pluginName)) {
123
+ return false
124
+ }
125
+
126
+ const newPlugins = currentPlugins.filter(p => p !== pluginName)
127
+ envContent = envContent.replace(
128
+ allowedPluginsRegex,
129
+ `PLUGINS_ALLOWED=${newPlugins.join(',')}`
130
+ )
131
+
132
+ writeFileSync(envPath, envContent, 'utf-8')
133
+ return true
134
+ }
135
+
136
+ /**
137
+ * Check if whitelist is empty and disable NPM discovery if so
138
+ */
139
+ function checkAndDisableNpmDiscovery(): void {
140
+ const envPath = join(process.cwd(), '.env')
141
+
142
+ if (!existsSync(envPath)) {
143
+ return
144
+ }
145
+
146
+ let envContent = readFileSync(envPath, 'utf-8')
147
+ const allowedPluginsRegex = /^PLUGINS_ALLOWED=(.*)$/m
148
+ const match = envContent.match(allowedPluginsRegex)
149
+
150
+ if (!match) {
151
+ return
152
+ }
153
+
154
+ const currentPlugins = match[1]
155
+ .split(',')
156
+ .map(p => p.trim())
157
+ .filter(p => p.length > 0)
158
+
159
+ // If whitelist is empty, disable NPM discovery
160
+ if (currentPlugins.length === 0) {
161
+ if (/^PLUGINS_DISCOVER_NPM=true/m.test(envContent)) {
162
+ envContent = envContent.replace(
163
+ /^PLUGINS_DISCOVER_NPM=true/m,
164
+ 'PLUGINS_DISCOVER_NPM=false'
165
+ )
166
+ writeFileSync(envPath, envContent, 'utf-8')
167
+ console.log(chalk.gray(' • Disabled NPM plugin discovery (whitelist empty)'))
168
+ }
169
+ }
170
+ }