create-fluxstack 1.0.13 → 1.0.15

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 (214) hide show
  1. package/.env.example +29 -29
  2. package/app/client/README.md +69 -69
  3. package/app/client/index.html +14 -13
  4. package/app/client/src/App.tsx +157 -524
  5. package/app/client/src/components/ErrorBoundary.tsx +107 -0
  6. package/app/client/src/components/ErrorDisplay.css +365 -0
  7. package/app/client/src/components/ErrorDisplay.tsx +258 -0
  8. package/app/client/src/components/FluxStackConfig.tsx +1321 -0
  9. package/app/client/src/components/HybridLiveCounter.tsx +140 -0
  10. package/app/client/src/components/LiveClock.tsx +286 -0
  11. package/app/client/src/components/MainLayout.tsx +390 -0
  12. package/app/client/src/components/SidebarNavigation.tsx +391 -0
  13. package/app/client/src/components/StateDemo.tsx +178 -0
  14. package/app/client/src/components/SystemMonitor.tsx +1038 -0
  15. package/app/client/src/components/Teste.tsx +104 -0
  16. package/app/client/src/components/UserProfile.tsx +809 -0
  17. package/app/client/src/hooks/useAuth.ts +39 -0
  18. package/app/client/src/hooks/useNotifications.ts +56 -0
  19. package/app/client/src/lib/eden-api.ts +189 -53
  20. package/app/client/src/lib/errors.ts +340 -0
  21. package/app/client/src/lib/hooks/useErrorHandler.ts +258 -0
  22. package/app/client/src/lib/index.ts +45 -0
  23. package/app/client/src/main.tsx +3 -2
  24. package/app/client/src/pages/ApiDocs.tsx +182 -0
  25. package/app/client/src/pages/Demo.tsx +174 -0
  26. package/app/client/src/pages/HybridLive.tsx +263 -0
  27. package/app/client/src/pages/Overview.tsx +155 -0
  28. package/app/client/src/store/README.md +43 -0
  29. package/app/client/src/store/index.ts +16 -0
  30. package/app/client/src/store/slices/uiSlice.ts +151 -0
  31. package/app/client/src/store/slices/userSlice.ts +161 -0
  32. package/app/client/src/test/README.md +257 -0
  33. package/app/client/src/test/setup.ts +70 -0
  34. package/app/client/src/test/types.ts +12 -0
  35. package/app/client/src/vite-env.d.ts +1 -1
  36. package/app/client/tsconfig.app.json +44 -43
  37. package/app/client/tsconfig.json +7 -7
  38. package/app/client/tsconfig.node.json +25 -25
  39. package/app/client/zustand-setup.md +65 -0
  40. package/app/server/controllers/users.controller.ts +68 -68
  41. package/app/server/index.ts +9 -1
  42. package/app/server/live/CounterComponent.ts +191 -0
  43. package/app/server/live/FluxStackConfig.ts +529 -0
  44. package/app/server/live/LiveClockComponent.ts +214 -0
  45. package/app/server/live/SidebarNavigation.ts +156 -0
  46. package/app/server/live/SystemMonitor.ts +594 -0
  47. package/app/server/live/SystemMonitorIntegration.ts +151 -0
  48. package/app/server/live/TesteComponent.ts +87 -0
  49. package/app/server/live/UserProfileComponent.ts +135 -0
  50. package/app/server/live/register-components.ts +28 -0
  51. package/app/server/middleware/auth.ts +136 -0
  52. package/app/server/middleware/errorHandling.ts +250 -0
  53. package/app/server/middleware/index.ts +10 -0
  54. package/app/server/middleware/rateLimit.ts +193 -0
  55. package/app/server/middleware/requestLogging.ts +215 -0
  56. package/app/server/middleware/validation.ts +270 -0
  57. package/app/server/routes/index.ts +14 -2
  58. package/app/server/routes/upload.ts +92 -0
  59. package/app/server/routes/users.routes.ts +2 -9
  60. package/app/server/services/NotificationService.ts +302 -0
  61. package/app/server/services/UserService.ts +222 -0
  62. package/app/server/services/index.ts +46 -0
  63. package/core/cli/commands/plugin-deps.ts +263 -0
  64. package/core/cli/generators/README.md +339 -0
  65. package/core/cli/generators/component.ts +770 -0
  66. package/core/cli/generators/controller.ts +299 -0
  67. package/core/cli/generators/index.ts +144 -0
  68. package/core/cli/generators/interactive.ts +228 -0
  69. package/core/cli/generators/prompts.ts +83 -0
  70. package/core/cli/generators/route.ts +513 -0
  71. package/core/cli/generators/service.ts +465 -0
  72. package/core/cli/generators/template-engine.ts +154 -0
  73. package/core/cli/generators/types.ts +71 -0
  74. package/core/cli/generators/utils.ts +192 -0
  75. package/core/cli/index.ts +69 -0
  76. package/core/cli/plugin-discovery.ts +16 -85
  77. package/core/client/fluxstack.ts +17 -0
  78. package/core/client/hooks/index.ts +7 -0
  79. package/core/client/hooks/state-validator.ts +130 -0
  80. package/core/client/hooks/useAuth.ts +49 -0
  81. package/core/client/hooks/useChunkedUpload.ts +258 -0
  82. package/core/client/hooks/useHybridLiveComponent.ts +967 -0
  83. package/core/client/hooks/useWebSocket.ts +373 -0
  84. package/core/client/index.ts +47 -0
  85. package/core/client/state/createStore.ts +193 -0
  86. package/core/client/state/index.ts +15 -0
  87. package/core/config/env-dynamic.ts +1 -1
  88. package/core/config/env.ts +2 -1
  89. package/core/config/runtime-config.ts +3 -3
  90. package/core/config/schema.ts +84 -49
  91. package/core/framework/server.ts +30 -0
  92. package/core/index.ts +25 -0
  93. package/core/live/ComponentRegistry.ts +399 -0
  94. package/core/live/types.ts +164 -0
  95. package/core/plugins/built-in/live-components/commands/create-live-component.ts +1201 -0
  96. package/core/plugins/built-in/live-components/index.ts +27 -0
  97. package/core/plugins/built-in/logger/index.ts +1 -1
  98. package/core/plugins/built-in/monitoring/index.ts +1 -1
  99. package/core/plugins/built-in/static/index.ts +1 -1
  100. package/core/plugins/built-in/swagger/index.ts +1 -1
  101. package/core/plugins/built-in/vite/index.ts +1 -1
  102. package/core/plugins/dependency-manager.ts +384 -0
  103. package/core/plugins/index.ts +5 -1
  104. package/core/plugins/manager.ts +7 -3
  105. package/core/plugins/registry.ts +88 -10
  106. package/core/plugins/types.ts +11 -11
  107. package/core/server/framework.ts +43 -0
  108. package/core/server/index.ts +11 -1
  109. package/core/server/live/ComponentRegistry.ts +1017 -0
  110. package/core/server/live/FileUploadManager.ts +272 -0
  111. package/core/server/live/LiveComponentPerformanceMonitor.ts +930 -0
  112. package/core/server/live/SingleConnectionManager.ts +0 -0
  113. package/core/server/live/StateSignature.ts +644 -0
  114. package/core/server/live/WebSocketConnectionManager.ts +688 -0
  115. package/core/server/live/websocket-plugin.ts +435 -0
  116. package/core/server/middleware/errorHandling.ts +141 -0
  117. package/core/server/middleware/index.ts +16 -0
  118. package/core/server/plugins/static-files-plugin.ts +232 -0
  119. package/core/server/services/BaseService.ts +95 -0
  120. package/core/server/services/ServiceContainer.ts +144 -0
  121. package/core/server/services/index.ts +9 -0
  122. package/core/templates/create-project.ts +196 -33
  123. package/core/testing/index.ts +10 -0
  124. package/core/testing/setup.ts +74 -0
  125. package/core/types/build.ts +38 -14
  126. package/core/types/types.ts +319 -0
  127. package/core/utils/env-runtime.ts +7 -0
  128. package/core/utils/errors/handlers.ts +264 -39
  129. package/core/utils/errors/index.ts +528 -18
  130. package/core/utils/errors/middleware.ts +114 -0
  131. package/core/utils/logger/formatters.ts +222 -0
  132. package/core/utils/logger/index.ts +167 -48
  133. package/core/utils/logger/middleware.ts +253 -0
  134. package/core/utils/logger/performance.ts +384 -0
  135. package/core/utils/logger/transports.ts +365 -0
  136. package/create-fluxstack.ts +296 -296
  137. package/fluxstack.config.ts +17 -1
  138. package/package-template.json +66 -66
  139. package/package.json +31 -6
  140. package/public/README.md +16 -0
  141. package/vite.config.ts +29 -14
  142. package/.claude/settings.local.json +0 -74
  143. package/.github/workflows/ci-build-tests.yml +0 -480
  144. package/.github/workflows/dependency-management.yml +0 -324
  145. package/.github/workflows/release-validation.yml +0 -355
  146. package/.kiro/specs/fluxstack-architecture-optimization/design.md +0 -700
  147. package/.kiro/specs/fluxstack-architecture-optimization/requirements.md +0 -127
  148. package/.kiro/specs/fluxstack-architecture-optimization/tasks.md +0 -330
  149. package/CLAUDE.md +0 -200
  150. package/Dockerfile +0 -58
  151. package/Dockerfile.backend +0 -52
  152. package/Dockerfile.frontend +0 -54
  153. package/README-Docker.md +0 -85
  154. package/ai-context/00-QUICK-START.md +0 -86
  155. package/ai-context/README.md +0 -88
  156. package/ai-context/development/eden-treaty-guide.md +0 -362
  157. package/ai-context/development/patterns.md +0 -382
  158. package/ai-context/development/plugins-guide.md +0 -572
  159. package/ai-context/examples/crud-complete.md +0 -626
  160. package/ai-context/project/architecture.md +0 -399
  161. package/ai-context/project/overview.md +0 -213
  162. package/ai-context/recent-changes/eden-treaty-refactor.md +0 -281
  163. package/ai-context/recent-changes/type-inference-fix.md +0 -223
  164. package/ai-context/reference/environment-vars.md +0 -384
  165. package/ai-context/reference/troubleshooting.md +0 -407
  166. package/app/client/src/components/TestPage.tsx +0 -453
  167. package/bun.lock +0 -1063
  168. package/bunfig.toml +0 -16
  169. package/core/__tests__/integration.test.ts +0 -227
  170. package/core/build/index.ts +0 -186
  171. package/core/config/__tests__/config-loader.test.ts +0 -554
  172. package/core/config/__tests__/config-merger.test.ts +0 -657
  173. package/core/config/__tests__/env-converter.test.ts +0 -372
  174. package/core/config/__tests__/env-processor.test.ts +0 -431
  175. package/core/config/__tests__/env.test.ts +0 -452
  176. package/core/config/__tests__/integration.test.ts +0 -418
  177. package/core/config/__tests__/loader.test.ts +0 -331
  178. package/core/config/__tests__/schema.test.ts +0 -129
  179. package/core/config/__tests__/validator.test.ts +0 -318
  180. package/core/framework/__tests__/server.test.ts +0 -233
  181. package/core/plugins/__tests__/built-in.test.ts.disabled +0 -366
  182. package/core/plugins/__tests__/manager.test.ts +0 -398
  183. package/core/plugins/__tests__/monitoring.test.ts +0 -401
  184. package/core/plugins/__tests__/registry.test.ts +0 -335
  185. package/core/utils/__tests__/errors.test.ts +0 -139
  186. package/core/utils/__tests__/helpers.test.ts +0 -297
  187. package/core/utils/__tests__/logger.test.ts +0 -141
  188. package/create-test-app.ts +0 -156
  189. package/docker-compose.microservices.yml +0 -75
  190. package/docker-compose.simple.yml +0 -57
  191. package/docker-compose.yml +0 -71
  192. package/eslint.config.js +0 -23
  193. package/flux-cli.ts +0 -214
  194. package/nginx-lb.conf +0 -37
  195. package/publish.sh +0 -63
  196. package/run-clean.ts +0 -26
  197. package/run-env-tests.ts +0 -313
  198. package/tailwind.config.js +0 -34
  199. package/tests/__mocks__/api.ts +0 -56
  200. package/tests/fixtures/users.ts +0 -69
  201. package/tests/integration/api/users.routes.test.ts +0 -221
  202. package/tests/setup.ts +0 -29
  203. package/tests/unit/app/client/App-simple.test.tsx +0 -56
  204. package/tests/unit/app/client/App.test.tsx.skip +0 -237
  205. package/tests/unit/app/client/eden-api.test.ts +0 -186
  206. package/tests/unit/app/client/simple.test.tsx +0 -23
  207. package/tests/unit/app/controllers/users.controller.test.ts +0 -150
  208. package/tests/unit/core/create-project.test.ts.skip +0 -95
  209. package/tests/unit/core/framework.test.ts +0 -144
  210. package/tests/unit/core/plugins/logger.test.ts.skip +0 -268
  211. package/tests/unit/core/plugins/vite.test.ts.disabled +0 -188
  212. package/tests/utils/test-helpers.ts +0 -61
  213. package/vitest.config.ts +0 -50
  214. package/workspace.json +0 -6
@@ -0,0 +1,299 @@
1
+ import type { Generator } from "./index.js"
2
+ import type { GeneratorContext, GeneratorOptions, Template } from "./types.js"
3
+ import { templateEngine } from "./template-engine.js"
4
+
5
+ export class ControllerGenerator implements Generator {
6
+ name = 'controller'
7
+ description = 'Generate a new controller with CRUD operations'
8
+
9
+ async generate(context: GeneratorContext, options: GeneratorOptions): Promise<void> {
10
+ const template = this.getTemplate(options.template)
11
+
12
+ // Execute before hook if present
13
+ if (template.hooks?.beforeGenerate) {
14
+ await template.hooks.beforeGenerate(context, options)
15
+ }
16
+
17
+ const files = await templateEngine.processTemplate(template, context, options)
18
+
19
+ if (options.dryRun) {
20
+ console.log(`\nšŸ“‹ Would generate controller '${options.name}':\n`)
21
+ for (const file of files) {
22
+ console.log(`${file.action === 'create' ? 'šŸ“„' : 'āœļø'} ${file.path}`)
23
+ }
24
+ return
25
+ }
26
+
27
+ await templateEngine.generateFiles(files, options.dryRun)
28
+
29
+ // Execute after hook if present
30
+ if (template.hooks?.afterGenerate) {
31
+ const filePaths = files.map(f => f.path)
32
+ await template.hooks.afterGenerate(context, options, filePaths)
33
+ }
34
+
35
+ console.log(`\nāœ… Generated controller '${options.name}' with ${files.length} files`)
36
+ }
37
+
38
+ private getTemplate(templateName?: string): Template {
39
+ switch (templateName) {
40
+ case 'minimal':
41
+ return this.getMinimalTemplate()
42
+ case 'crud':
43
+ default:
44
+ return this.getCrudTemplate()
45
+ }
46
+ }
47
+
48
+ private getCrudTemplate(): Template {
49
+ return {
50
+ name: 'crud-controller',
51
+ description: 'Full CRUD controller with validation',
52
+ files: [
53
+ {
54
+ path: 'app/server/controllers/{{kebabName}}.controller.ts',
55
+ content: `import { Elysia, t } from 'elysia'
56
+ import { {{pascalName}}Service } from '../services/{{kebabName}}.service'
57
+ import { {{pascalName}}Schema, Create{{pascalName}}Schema, Update{{pascalName}}Schema } from '../schemas/{{kebabName}}.schema'
58
+ import { NotFoundError, ValidationError } from '../../../core/utils/errors'
59
+
60
+ export class {{pascalName}}Controller {
61
+ private service: {{pascalName}}Service
62
+
63
+ constructor() {
64
+ this.service = new {{pascalName}}Service()
65
+ }
66
+
67
+ routes() {
68
+ return new Elysia({ prefix: '/{{kebabName}}s' })
69
+ .get('/', this.getAll.bind(this))
70
+ .get('/:id', this.getById.bind(this), {
71
+ params: t.Object({
72
+ id: t.String()
73
+ })
74
+ })
75
+ .post('/', this.create.bind(this), {
76
+ body: Create{{pascalName}}Schema
77
+ })
78
+ .put('/:id', this.update.bind(this), {
79
+ params: t.Object({
80
+ id: t.String()
81
+ }),
82
+ body: Update{{pascalName}}Schema
83
+ })
84
+ .delete('/:id', this.delete.bind(this), {
85
+ params: t.Object({
86
+ id: t.String()
87
+ })
88
+ })
89
+ }
90
+
91
+ async getAll() {
92
+ try {
93
+ const {{camelName}}s = await this.service.findAll()
94
+ return {
95
+ success: true,
96
+ data: {{camelName}}s,
97
+ count: {{camelName}}s.length
98
+ }
99
+ } catch (error) {
100
+ throw new Error(\`Failed to fetch {{camelName}}s: \${error instanceof Error ? error.message : 'Unknown error'}\`)
101
+ }
102
+ }
103
+
104
+ async getById({ params }: { params: { id: string } }) {
105
+ try {
106
+ const {{camelName}} = await this.service.findById(params.id)
107
+
108
+ if (!{{camelName}}) {
109
+ throw new NotFoundError('{{pascalName}} not found')
110
+ }
111
+
112
+ return {
113
+ success: true,
114
+ data: {{camelName}}
115
+ }
116
+ } catch (error) {
117
+ if (error instanceof NotFoundError) {
118
+ throw error
119
+ }
120
+ throw new Error(\`Failed to fetch {{camelName}}: \${error instanceof Error ? error.message : 'Unknown error'}\`)
121
+ }
122
+ }
123
+
124
+ async create({ body }: { body: any }) {
125
+ try {
126
+ const {{camelName}} = await this.service.create(body)
127
+
128
+ return {
129
+ success: true,
130
+ data: {{camelName}},
131
+ message: '{{pascalName}} created successfully'
132
+ }
133
+ } catch (error) {
134
+ if (error instanceof ValidationError) {
135
+ throw error
136
+ }
137
+ throw new Error(\`Failed to create {{camelName}}: \${error instanceof Error ? error.message : 'Unknown error'}\`)
138
+ }
139
+ }
140
+
141
+ async update({ params, body }: { params: { id: string }, body: any }) {
142
+ try {
143
+ const {{camelName}} = await this.service.update(params.id, body)
144
+
145
+ if (!{{camelName}}) {
146
+ throw new NotFoundError('{{pascalName}} not found')
147
+ }
148
+
149
+ return {
150
+ success: true,
151
+ data: {{camelName}},
152
+ message: '{{pascalName}} updated successfully'
153
+ }
154
+ } catch (error) {
155
+ if (error instanceof NotFoundError || error instanceof ValidationError) {
156
+ throw error
157
+ }
158
+ throw new Error(\`Failed to update {{camelName}}: \${error instanceof Error ? error.message : 'Unknown error'}\`)
159
+ }
160
+ }
161
+
162
+ async delete({ params }: { params: { id: string } }) {
163
+ try {
164
+ const deleted = await this.service.delete(params.id)
165
+
166
+ if (!deleted) {
167
+ throw new NotFoundError('{{pascalName}} not found')
168
+ }
169
+
170
+ return {
171
+ success: true,
172
+ message: '{{pascalName}} deleted successfully'
173
+ }
174
+ } catch (error) {
175
+ if (error instanceof NotFoundError) {
176
+ throw error
177
+ }
178
+ throw new Error(\`Failed to delete {{camelName}}: \${error instanceof Error ? error.message : 'Unknown error'}\`)
179
+ }
180
+ }
181
+ }
182
+ `
183
+ },
184
+ {
185
+ path: 'app/server/schemas/{{kebabName}}.schema.ts',
186
+ content: `import { t } from 'elysia'
187
+
188
+ export const {{pascalName}}Schema = t.Object({
189
+ id: t.String(),
190
+ name: t.String(),
191
+ description: t.Optional(t.String()),
192
+ createdAt: t.Date(),
193
+ updatedAt: t.Date()
194
+ })
195
+
196
+ export const Create{{pascalName}}Schema = t.Object({
197
+ name: t.String({
198
+ minLength: 1,
199
+ maxLength: 100,
200
+ error: 'Name must be between 1 and 100 characters'
201
+ }),
202
+ description: t.Optional(t.String({
203
+ maxLength: 500,
204
+ error: 'Description must be less than 500 characters'
205
+ }))
206
+ })
207
+
208
+ export const Update{{pascalName}}Schema = t.Partial(Create{{pascalName}}Schema)
209
+
210
+ export type {{pascalName}} = typeof {{pascalName}}Schema.static
211
+ export type Create{{pascalName}} = typeof Create{{pascalName}}Schema.static
212
+ export type Update{{pascalName}} = typeof Update{{pascalName}}Schema.static
213
+ `
214
+ }
215
+ ],
216
+ hooks: {
217
+ afterGenerate: async (context, options, files) => {
218
+ context.logger.info(`Generated controller files:`)
219
+ files.forEach(file => {
220
+ context.logger.info(` - ${file}`)
221
+ })
222
+ context.logger.info(`\nNext steps:`)
223
+ context.logger.info(`1. Generate the corresponding service: flux generate service ${options.name}`)
224
+ context.logger.info(`2. Add the controller to your routes in app/server/routes/`)
225
+ context.logger.info(`3. Implement the business logic in the service`)
226
+ }
227
+ }
228
+ }
229
+ }
230
+
231
+ private getMinimalTemplate(): Template {
232
+ return {
233
+ name: 'minimal-controller',
234
+ description: 'Minimal controller with basic structure',
235
+ files: [
236
+ {
237
+ path: 'app/server/controllers/{{kebabName}}.controller.ts',
238
+ content: `import { Elysia } from 'elysia'
239
+
240
+ export class {{pascalName}}Controller {
241
+ routes() {
242
+ return new Elysia({ prefix: '/{{kebabName}}s' })
243
+ .get('/', this.index.bind(this))
244
+ .get('/:id', this.show.bind(this))
245
+ .post('/', this.create.bind(this))
246
+ .put('/:id', this.update.bind(this))
247
+ .delete('/:id', this.destroy.bind(this))
248
+ }
249
+
250
+ async index() {
251
+ // TODO: Implement list logic
252
+ return {
253
+ success: true,
254
+ data: [],
255
+ message: 'List {{camelName}}s'
256
+ }
257
+ }
258
+
259
+ async show({ params }: { params: { id: string } }) {
260
+ // TODO: Implement show logic
261
+ return {
262
+ success: true,
263
+ data: { id: params.id },
264
+ message: 'Show {{camelName}}'
265
+ }
266
+ }
267
+
268
+ async create({ body }: { body: any }) {
269
+ // TODO: Implement create logic
270
+ return {
271
+ success: true,
272
+ data: body,
273
+ message: '{{pascalName}} created'
274
+ }
275
+ }
276
+
277
+ async update({ params, body }: { params: { id: string }, body: any }) {
278
+ // TODO: Implement update logic
279
+ return {
280
+ success: true,
281
+ data: { id: params.id, ...body },
282
+ message: '{{pascalName}} updated'
283
+ }
284
+ }
285
+
286
+ async destroy({ params }: { params: { id: string } }) {
287
+ // TODO: Implement delete logic
288
+ return {
289
+ success: true,
290
+ message: '{{pascalName}} deleted'
291
+ }
292
+ }
293
+ }
294
+ `
295
+ }
296
+ ]
297
+ }
298
+ }
299
+ }
@@ -0,0 +1,144 @@
1
+ import type { CliCommand } from "../../plugins/types.js"
2
+ import { ControllerGenerator } from "./controller.js"
3
+ import { RouteGenerator } from "./route.js"
4
+ import { ComponentGenerator } from "./component.js"
5
+ import { ServiceGenerator } from "./service.js"
6
+ import type { GeneratorContext, GeneratorOptions } from "./types.js"
7
+
8
+ export interface Generator {
9
+ name: string
10
+ description: string
11
+ generate(context: GeneratorContext, options: GeneratorOptions): Promise<void>
12
+ }
13
+
14
+ export class GeneratorRegistry {
15
+ private generators = new Map<string, Generator>()
16
+
17
+ constructor() {
18
+ this.registerBuiltInGenerators()
19
+ }
20
+
21
+ private registerBuiltInGenerators() {
22
+ this.register(new ControllerGenerator())
23
+ this.register(new RouteGenerator())
24
+ this.register(new ComponentGenerator())
25
+ this.register(new ServiceGenerator())
26
+ }
27
+
28
+ register(generator: Generator): void {
29
+ this.generators.set(generator.name, generator)
30
+ }
31
+
32
+ get(name: string): Generator | undefined {
33
+ return this.generators.get(name)
34
+ }
35
+
36
+ getAll(): Generator[] {
37
+ return Array.from(this.generators.values())
38
+ }
39
+
40
+ has(name: string): boolean {
41
+ return this.generators.has(name)
42
+ }
43
+ }
44
+
45
+ export const generatorRegistry = new GeneratorRegistry()
46
+
47
+ // Export additional commands
48
+ export { interactiveGenerateCommand } from "./interactive.js"
49
+
50
+ // CLI command for code generation
51
+ export const generateCommand: CliCommand = {
52
+ name: 'generate',
53
+ description: 'Generate code from templates',
54
+ usage: 'flux generate <type> <name> [options]',
55
+ aliases: ['g', 'gen'],
56
+ category: 'Development',
57
+ examples: [
58
+ 'flux generate controller user',
59
+ 'flux generate component UserCard',
60
+ 'flux generate service auth',
61
+ 'flux generate route api/users'
62
+ ],
63
+ arguments: [
64
+ {
65
+ name: 'type',
66
+ description: 'Type of code to generate',
67
+ required: true,
68
+ type: 'string',
69
+ choices: ['controller', 'route', 'component', 'service']
70
+ },
71
+ {
72
+ name: 'name',
73
+ description: 'Name of the generated item',
74
+ required: true,
75
+ type: 'string'
76
+ }
77
+ ],
78
+ options: [
79
+ {
80
+ name: 'path',
81
+ short: 'p',
82
+ description: 'Custom path for generated files',
83
+ type: 'string'
84
+ },
85
+ {
86
+ name: 'template',
87
+ short: 't',
88
+ description: 'Template variant to use',
89
+ type: 'string'
90
+ },
91
+ {
92
+ name: 'force',
93
+ short: 'f',
94
+ description: 'Overwrite existing files',
95
+ type: 'boolean',
96
+ default: false
97
+ },
98
+ {
99
+ name: 'dry-run',
100
+ description: 'Show what would be generated without creating files',
101
+ type: 'boolean',
102
+ default: false
103
+ }
104
+ ],
105
+ handler: async (args, options, context) => {
106
+ const [type, name] = args
107
+
108
+ const generator = generatorRegistry.get(type)
109
+ if (!generator) {
110
+ console.error(`āŒ Unknown generator type: ${type}`)
111
+ console.log('\nAvailable generators:')
112
+ for (const gen of generatorRegistry.getAll()) {
113
+ console.log(` ${gen.name.padEnd(12)} ${gen.description}`)
114
+ }
115
+ return
116
+ }
117
+
118
+ const generatorContext: GeneratorContext = {
119
+ workingDir: context.workingDir,
120
+ config: context.config,
121
+ logger: context.logger,
122
+ utils: context.utils
123
+ }
124
+
125
+ const generatorOptions: GeneratorOptions = {
126
+ name,
127
+ path: options.path,
128
+ template: options.template,
129
+ force: options.force,
130
+ dryRun: options['dry-run']
131
+ }
132
+
133
+ try {
134
+ await generator.generate(generatorContext, generatorOptions)
135
+
136
+ if (!options['dry-run']) {
137
+ console.log(`āœ… Successfully generated ${type}: ${name}`)
138
+ }
139
+ } catch (error) {
140
+ console.error(`āŒ Failed to generate ${type}:`, error instanceof Error ? error.message : String(error))
141
+ throw error
142
+ }
143
+ }
144
+ }
@@ -0,0 +1,228 @@
1
+ import type { CliCommand } from "../../plugins/types.js"
2
+ import { generatorRegistry } from "./index.js"
3
+ import type { GeneratorContext, GeneratorOptions } from "./types.js"
4
+ import { promptSystem } from "./prompts.js"
5
+
6
+ export const interactiveGenerateCommand: CliCommand = {
7
+ name: 'generate:interactive',
8
+ description: 'Generate code interactively with prompts',
9
+ usage: 'flux generate:interactive',
10
+ aliases: ['gi', 'gen:i'],
11
+ category: 'Development',
12
+ examples: [
13
+ 'flux generate:interactive',
14
+ 'flux gi'
15
+ ],
16
+ handler: async (args, options, context) => {
17
+ console.log('šŸŽÆ FluxStack Interactive Code Generator\n')
18
+
19
+ // Select generator type
20
+ const generators = generatorRegistry.getAll()
21
+ const generatorChoices = generators.map(gen => ({
22
+ name: `${gen.name} - ${gen.description}`,
23
+ value: gen.name
24
+ }))
25
+
26
+ const selectedType = await promptSystem.select(
27
+ 'What would you like to generate?',
28
+ generatorChoices
29
+ )
30
+
31
+ const generator = generatorRegistry.get(selectedType)
32
+ if (!generator) {
33
+ console.error(`āŒ Generator not found: ${selectedType}`)
34
+ return
35
+ }
36
+
37
+ // Get name
38
+ const name = await promptSystem.input(
39
+ `Enter the ${selectedType} name:`,
40
+ undefined,
41
+ (value) => {
42
+ if (!value.trim()) {
43
+ return 'Name is required'
44
+ }
45
+ if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(value)) {
46
+ return 'Name must start with a letter and contain only letters, numbers, hyphens, and underscores'
47
+ }
48
+ return true
49
+ }
50
+ )
51
+
52
+ // Get template variant (if applicable)
53
+ let template: string | undefined
54
+ if (selectedType === 'controller') {
55
+ template = await promptSystem.select(
56
+ 'Choose controller template:',
57
+ [
58
+ { name: 'CRUD - Full CRUD operations with validation', value: 'crud' },
59
+ { name: 'Minimal - Basic structure only', value: 'minimal' }
60
+ ]
61
+ )
62
+ } else if (selectedType === 'component') {
63
+ template = await promptSystem.select(
64
+ 'Choose component template:',
65
+ [
66
+ { name: 'Basic - Simple component', value: 'basic' },
67
+ { name: 'Functional - Component with hooks', value: 'functional' },
68
+ { name: 'Page - Page component with layout', value: 'page' },
69
+ { name: 'Form - Form component with validation', value: 'form' },
70
+ { name: 'Full - Complete with tests and stories', value: 'full' }
71
+ ]
72
+ )
73
+ } else if (selectedType === 'service') {
74
+ template = await promptSystem.select(
75
+ 'Choose service template:',
76
+ [
77
+ { name: 'CRUD - Full service with repository', value: 'crud' },
78
+ { name: 'Repository - Service with repository pattern', value: 'repository' },
79
+ { name: 'Minimal - Basic structure only', value: 'minimal' }
80
+ ]
81
+ )
82
+ } else if (selectedType === 'route') {
83
+ template = await promptSystem.select(
84
+ 'Choose route template:',
85
+ [
86
+ { name: 'CRUD - Full REST API routes', value: 'crud' },
87
+ { name: 'Auth - Authentication routes', value: 'auth' },
88
+ { name: 'Minimal - Basic routes only', value: 'minimal' }
89
+ ]
90
+ )
91
+ }
92
+
93
+ // Get custom path (optional)
94
+ const useCustomPath = await promptSystem.confirm(
95
+ 'Do you want to specify a custom path?',
96
+ false
97
+ )
98
+
99
+ let customPath: string | undefined
100
+ if (useCustomPath) {
101
+ customPath = await promptSystem.input(
102
+ 'Enter custom path (relative to project root):'
103
+ )
104
+ }
105
+
106
+ // Confirm overwrite if files exist
107
+ const force = await promptSystem.confirm(
108
+ 'Overwrite existing files if they exist?',
109
+ false
110
+ )
111
+
112
+ // Show dry run first
113
+ console.log('\nšŸ“‹ Preview of files to be generated:\n')
114
+
115
+ const generatorContext: GeneratorContext = {
116
+ workingDir: context.workingDir,
117
+ config: context.config,
118
+ logger: context.logger,
119
+ utils: context.utils
120
+ }
121
+
122
+ const generatorOptions: GeneratorOptions = {
123
+ name,
124
+ path: customPath,
125
+ template,
126
+ force,
127
+ dryRun: true
128
+ }
129
+
130
+ try {
131
+ await generator.generate(generatorContext, generatorOptions)
132
+
133
+ const proceed = await promptSystem.confirm(
134
+ '\nProceed with generation?',
135
+ true
136
+ )
137
+
138
+ if (!proceed) {
139
+ console.log('āŒ Generation cancelled')
140
+ return
141
+ }
142
+
143
+ // Generate for real
144
+ generatorOptions.dryRun = false
145
+ await generator.generate(generatorContext, generatorOptions)
146
+
147
+ console.log(`\nāœ… Successfully generated ${selectedType}: ${name}`)
148
+
149
+ // Ask if user wants to generate related files
150
+ await suggestRelatedGenerations(selectedType, name, generatorContext)
151
+
152
+ } catch (error) {
153
+ console.error(`āŒ Failed to generate ${selectedType}:`, error instanceof Error ? error.message : String(error))
154
+ throw error
155
+ }
156
+ }
157
+ }
158
+
159
+ async function suggestRelatedGenerations(
160
+ generatedType: string,
161
+ name: string,
162
+ context: GeneratorContext
163
+ ): Promise<void> {
164
+ const suggestions: Array<{ type: string; description: string }> = []
165
+
166
+ switch (generatedType) {
167
+ case 'controller':
168
+ suggestions.push(
169
+ { type: 'service', description: `Generate ${name} service for business logic` },
170
+ { type: 'route', description: `Generate ${name} routes to expose the controller` }
171
+ )
172
+ break
173
+ case 'service':
174
+ suggestions.push(
175
+ { type: 'controller', description: `Generate ${name} controller to use this service` }
176
+ )
177
+ break
178
+ case 'route':
179
+ suggestions.push(
180
+ { type: 'controller', description: `Generate ${name} controller for route handlers` }
181
+ )
182
+ break
183
+ case 'component':
184
+ // No automatic suggestions for components
185
+ break
186
+ }
187
+
188
+ if (suggestions.length === 0) {
189
+ return
190
+ }
191
+
192
+ console.log('\nšŸ’” Suggested next steps:')
193
+ for (const suggestion of suggestions) {
194
+ console.log(` • ${suggestion.description}`)
195
+ }
196
+
197
+ const generateRelated = await promptSystem.confirm(
198
+ '\nWould you like to generate related files now?',
199
+ false
200
+ )
201
+
202
+ if (!generateRelated) {
203
+ return
204
+ }
205
+
206
+ for (const suggestion of suggestions) {
207
+ const shouldGenerate = await promptSystem.confirm(
208
+ `Generate ${suggestion.type} for ${name}?`,
209
+ true
210
+ )
211
+
212
+ if (shouldGenerate) {
213
+ const generator = generatorRegistry.get(suggestion.type)
214
+ if (generator) {
215
+ try {
216
+ await generator.generate(context, {
217
+ name,
218
+ force: false,
219
+ dryRun: false
220
+ })
221
+ console.log(`āœ… Generated ${suggestion.type}: ${name}`)
222
+ } catch (error) {
223
+ console.error(`āŒ Failed to generate ${suggestion.type}:`, error instanceof Error ? error.message : String(error))
224
+ }
225
+ }
226
+ }
227
+ }
228
+ }