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.
- package/.dockerignore +1 -2
- package/Dockerfile +8 -8
- package/LLMD/INDEX.md +64 -0
- package/LLMD/MAINTENANCE.md +197 -0
- package/LLMD/MIGRATION.md +156 -0
- package/LLMD/config/.gitkeep +1 -0
- package/LLMD/config/declarative-system.md +268 -0
- package/LLMD/config/environment-vars.md +327 -0
- package/LLMD/config/runtime-reload.md +401 -0
- package/LLMD/core/.gitkeep +1 -0
- package/LLMD/core/build-system.md +599 -0
- package/LLMD/core/framework-lifecycle.md +229 -0
- package/LLMD/core/plugin-system.md +451 -0
- package/LLMD/patterns/.gitkeep +1 -0
- package/LLMD/patterns/anti-patterns.md +297 -0
- package/LLMD/patterns/project-structure.md +264 -0
- package/LLMD/patterns/type-safety.md +440 -0
- package/LLMD/reference/.gitkeep +1 -0
- package/LLMD/reference/cli-commands.md +250 -0
- package/LLMD/reference/plugin-hooks.md +357 -0
- package/LLMD/reference/routing.md +39 -0
- package/LLMD/reference/troubleshooting.md +364 -0
- package/LLMD/resources/.gitkeep +1 -0
- package/LLMD/resources/controllers.md +465 -0
- package/LLMD/resources/live-components.md +703 -0
- package/LLMD/resources/live-rooms.md +482 -0
- package/LLMD/resources/live-upload.md +130 -0
- package/LLMD/resources/plugins-external.md +617 -0
- package/LLMD/resources/routes-eden.md +254 -0
- package/README.md +37 -17
- package/app/client/index.html +0 -1
- package/app/client/src/App.tsx +107 -150
- package/app/client/src/components/AppLayout.tsx +68 -0
- package/app/client/src/components/BackButton.tsx +13 -0
- package/app/client/src/components/DemoPage.tsx +20 -0
- package/app/client/src/components/LiveUploadWidget.tsx +204 -0
- package/app/client/src/lib/eden-api.ts +85 -60
- package/app/client/src/live/ChatDemo.tsx +107 -0
- package/app/client/src/live/CounterDemo.tsx +206 -0
- package/app/client/src/live/FormDemo.tsx +119 -0
- package/app/client/src/live/RoomChatDemo.tsx +242 -0
- package/app/client/src/live/UploadDemo.tsx +21 -0
- package/app/client/src/main.tsx +4 -1
- package/app/client/src/pages/ApiTestPage.tsx +108 -0
- package/app/client/src/pages/HomePage.tsx +76 -0
- package/app/server/app.ts +1 -4
- package/app/server/controllers/users.controller.ts +36 -44
- package/app/server/index.ts +25 -35
- package/app/server/live/LiveChat.ts +77 -0
- package/app/server/live/LiveCounter.ts +67 -0
- package/app/server/live/LiveForm.ts +63 -0
- package/app/server/live/LiveLocalCounter.ts +32 -0
- package/app/server/live/LiveRoomChat.ts +285 -0
- package/app/server/live/LiveUpload.ts +81 -0
- package/app/server/routes/index.ts +3 -1
- package/app/server/routes/room.routes.ts +117 -0
- package/app/server/routes/users.routes.ts +35 -27
- package/app/shared/types/index.ts +14 -2
- package/config/app.config.ts +2 -62
- package/config/client.config.ts +2 -95
- package/config/database.config.ts +2 -99
- package/config/fluxstack.config.ts +25 -45
- package/config/index.ts +57 -38
- package/config/monitoring.config.ts +2 -114
- package/config/plugins.config.ts +2 -80
- package/config/server.config.ts +2 -68
- package/config/services.config.ts +2 -130
- package/config/system/app.config.ts +29 -0
- package/config/system/build.config.ts +49 -0
- package/config/system/client.config.ts +68 -0
- package/config/system/database.config.ts +17 -0
- package/config/system/fluxstack.config.ts +114 -0
- package/config/{logger.config.ts → system/logger.config.ts} +3 -1
- package/config/system/monitoring.config.ts +114 -0
- package/config/system/plugins.config.ts +84 -0
- package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
- package/config/system/server.config.ts +68 -0
- package/config/system/services.config.ts +46 -0
- package/config/{system.config.ts → system/system.config.ts} +1 -1
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +39 -27
- package/core/build/live-components-generator.ts +3 -3
- package/core/build/optimizer.ts +235 -235
- package/core/cli/command-registry.ts +6 -4
- package/core/cli/commands/build.ts +79 -0
- package/core/cli/commands/create.ts +54 -0
- package/core/cli/commands/dev.ts +101 -0
- package/core/cli/commands/help.ts +34 -0
- package/core/cli/commands/index.ts +34 -0
- package/core/cli/commands/make-plugin.ts +90 -0
- package/core/cli/commands/plugin-add.ts +197 -0
- package/core/cli/commands/plugin-deps.ts +2 -2
- package/core/cli/commands/plugin-list.ts +208 -0
- package/core/cli/commands/plugin-remove.ts +170 -0
- package/core/cli/generators/component.ts +769 -769
- package/core/cli/generators/controller.ts +1 -1
- package/core/cli/generators/index.ts +146 -146
- package/core/cli/generators/interactive.ts +227 -227
- package/core/cli/generators/plugin.ts +2 -2
- package/core/cli/generators/prompts.ts +82 -82
- package/core/cli/generators/route.ts +6 -6
- package/core/cli/generators/service.ts +2 -2
- package/core/cli/generators/template-engine.ts +4 -3
- package/core/cli/generators/types.ts +2 -2
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +115 -686
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/LiveComponentsProvider.tsx +60 -8
- package/core/client/api/eden.ts +183 -0
- package/core/client/api/index.ts +11 -0
- package/core/client/components/Live.tsx +104 -0
- package/core/client/fluxstack.ts +1 -9
- package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/hooks/useChunkedUpload.ts +85 -35
- package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
- package/core/client/hooks/useLiveComponent.ts +800 -0
- package/core/client/hooks/useLiveUpload.ts +71 -0
- package/core/client/hooks/useRoom.ts +409 -0
- package/core/client/hooks/useRoomProxy.ts +382 -0
- package/core/client/index.ts +17 -68
- package/core/client/standalone-entry.ts +8 -0
- package/core/client/standalone.ts +74 -53
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +70 -291
- package/core/config/schema.ts +42 -723
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +47 -40
- package/core/framework/types.ts +2 -2
- package/core/index.ts +23 -4
- package/core/live/ComponentRegistry.ts +3 -3
- package/core/live/types.ts +77 -0
- package/core/plugins/built-in/index.ts +134 -134
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +111 -47
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +68 -265
- package/core/plugins/built-in/vite/index.ts +85 -185
- package/core/plugins/built-in/vite/vite-dev.ts +10 -16
- package/core/plugins/config.ts +9 -7
- package/core/plugins/dependency-manager.ts +31 -1
- package/core/plugins/discovery.ts +19 -7
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +203 -203
- package/core/plugins/manager.ts +27 -39
- package/core/plugins/module-resolver.ts +19 -8
- package/core/plugins/registry.ts +255 -19
- package/core/plugins/types.ts +20 -53
- package/core/server/framework.ts +66 -43
- package/core/server/index.ts +15 -15
- package/core/server/live/ComponentRegistry.ts +78 -71
- package/core/server/live/FileUploadManager.ts +23 -10
- package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
- package/core/server/live/LiveRoomManager.ts +261 -0
- package/core/server/live/RoomEventBus.ts +234 -0
- package/core/server/live/RoomStateManager.ts +172 -0
- package/core/server/live/StateSignature.ts +643 -643
- package/core/server/live/WebSocketConnectionManager.ts +30 -19
- package/core/server/live/auto-generated-components.ts +21 -9
- package/core/server/live/index.ts +14 -0
- package/core/server/live/websocket-plugin.ts +214 -67
- package/core/server/middleware/elysia-helpers.ts +7 -2
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +180 -180
- package/core/server/plugins/static-files-plugin.ts +69 -69
- package/core/server/plugins/swagger.ts +1 -1
- package/core/server/rooms/RoomBroadcaster.ts +357 -0
- package/core/server/rooms/RoomSystem.ts +463 -0
- package/core/server/rooms/index.ts +13 -0
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +12 -12
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/build.ts +219 -219
- package/core/types/config.ts +56 -26
- package/core/types/index.ts +4 -4
- package/core/types/plugin.ts +107 -107
- package/core/types/types.ts +353 -14
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +2 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +36 -1
- package/core/utils/errors/index.ts +49 -5
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +6 -16
- package/core/utils/index.ts +17 -17
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +13 -9
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +6 -1
- package/core/utils/logger/stack-trace.ts +3 -1
- package/core/utils/logger/startup-banner.ts +82 -82
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +66 -66
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +8 -7
- package/package.json +12 -13
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
- package/plugins/crypto-auth/client/components/index.ts +11 -11
- package/plugins/crypto-auth/client/index.ts +11 -11
- package/plugins/crypto-auth/config/index.ts +1 -1
- package/plugins/crypto-auth/index.ts +4 -4
- package/plugins/crypto-auth/package.json +65 -65
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/index.ts +21 -21
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/tsconfig.api-strict.json +16 -0
- package/tsconfig.json +48 -52
- package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
- package/types/global.d.ts +29 -29
- package/types/vitest.d.ts +8 -8
- package/vite.config.ts +38 -62
- package/vitest.config.live.ts +10 -9
- package/vitest.config.ts +29 -17
- package/app/client/README.md +0 -69
- package/app/client/SIMPLIFICATION.md +0 -140
- package/app/client/frontend-only.ts +0 -12
- package/app/client/src/live/FileUploadExample.tsx +0 -359
- package/app/client/src/live/MinimalLiveClock.tsx +0 -47
- package/app/client/src/live/QuickUploadTest.tsx +0 -193
- package/app/client/tsconfig.app.json +0 -45
- package/app/client/tsconfig.json +0 -7
- package/app/client/zustand-setup.md +0 -65
- package/app/server/backend-only.ts +0 -18
- package/app/server/live/LiveClockComponent.ts +0 -215
- package/app/server/live/LiveFileUploadComponent.ts +0 -77
- package/app/server/routes/env-test.ts +0 -110
- package/core/client/hooks/index.ts +0 -7
- package/core/client/hooks/useHybridLiveComponent.ts +0 -685
- package/core/client/hooks/useTypedLiveComponent.ts +0 -133
- package/core/client/hooks/useWebSocket.ts +0 -361
- package/core/config/env.ts +0 -546
- package/core/config/loader.ts +0 -522
- package/core/config/runtime-config.ts +0 -327
- package/core/config/validator.ts +0 -540
- package/core/server/backend-entry.ts +0 -51
- package/core/server/standalone.ts +0 -106
- package/core/utils/regenerate-files.ts +0 -69
- package/fluxstack.config.ts +0 -354
|
@@ -1,228 +1,228 @@
|
|
|
1
|
-
import type { CliCommand } from "../../plugins/types"
|
|
2
|
-
import { generatorRegistry } from "./index"
|
|
3
|
-
import type { GeneratorContext, GeneratorOptions } from "./types"
|
|
4
|
-
import { promptSystem } from "./prompts"
|
|
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
|
-
}
|
|
1
|
+
import type { CliCommand } from "../../plugins/types"
|
|
2
|
+
import { generatorRegistry } from "./index"
|
|
3
|
+
import type { GeneratorContext, GeneratorOptions } from "./types"
|
|
4
|
+
import { promptSystem } from "./prompts"
|
|
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
228
|
}
|
|
@@ -110,7 +110,7 @@ export class PluginGenerator implements Generator {
|
|
|
110
110
|
* Declarative config using FluxStack config system
|
|
111
111
|
*/
|
|
112
112
|
|
|
113
|
-
import { defineConfig, config } from '
|
|
113
|
+
import { defineConfig, config } from '@core/utils/config-schema'
|
|
114
114
|
|
|
115
115
|
const {{camelName}}ConfigSchema = {
|
|
116
116
|
// Enable/disable plugin
|
|
@@ -131,7 +131,7 @@ export default {{camelName}}Config
|
|
|
131
131
|
},
|
|
132
132
|
{
|
|
133
133
|
path: 'plugins/{{name}}/index.ts',
|
|
134
|
-
content: `import type { ErrorContext, FluxStack, PluginContext, RequestContext, ResponseContext } from "
|
|
134
|
+
content: `import type { ErrorContext, FluxStack, PluginContext, RequestContext, ResponseContext } from "@core/plugins/types"
|
|
135
135
|
// ✅ Plugin imports its own configuration
|
|
136
136
|
import { {{camelName}}Config } from './config'
|
|
137
137
|
|
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
import type { PromptConfig } from "./types"
|
|
2
|
-
|
|
3
|
-
export class PromptSystem {
|
|
4
|
-
async prompt(config: PromptConfig): Promise<any> {
|
|
5
|
-
// Simple implementation using process.stdin
|
|
6
|
-
// In a real implementation, you'd use a library like inquirer or prompts
|
|
7
|
-
|
|
8
|
-
return new Promise((resolve) => {
|
|
9
|
-
process.stdout.write(`${config.message} `)
|
|
10
|
-
|
|
11
|
-
if (config.default !== undefined) {
|
|
12
|
-
process.stdout.write(`(${config.default}) `)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
if (config.choices) {
|
|
16
|
-
process.stdout.write(`\nChoices: ${config.choices.map(c => c.name || c.value).join(', ')}\n`)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
process.stdout.write(': ')
|
|
20
|
-
|
|
21
|
-
process.stdin.once('data', (data) => {
|
|
22
|
-
const input = data.toString().trim()
|
|
23
|
-
|
|
24
|
-
if (!input && config.default !== undefined) {
|
|
25
|
-
resolve(config.default)
|
|
26
|
-
return
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (config.validate) {
|
|
30
|
-
const validation = config.validate(input)
|
|
31
|
-
if (validation !== true) {
|
|
32
|
-
console.log(`Error: ${validation}`)
|
|
33
|
-
// In a real implementation, you'd re-prompt
|
|
34
|
-
resolve(input)
|
|
35
|
-
return
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (config.choices) {
|
|
40
|
-
const choice = config.choices.find(c =>
|
|
41
|
-
c.name === input || c.value === input
|
|
42
|
-
)
|
|
43
|
-
if (choice) {
|
|
44
|
-
resolve(choice.value)
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
resolve(input)
|
|
50
|
-
})
|
|
51
|
-
})
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async confirm(message: string, defaultValue: boolean = false): Promise<boolean> {
|
|
55
|
-
return this.prompt({
|
|
56
|
-
type: 'confirm',
|
|
57
|
-
message: `${message} (y/n)`,
|
|
58
|
-
default: defaultValue
|
|
59
|
-
}).then(result => {
|
|
60
|
-
const value = String(result).toLowerCase()
|
|
61
|
-
return value === 'y' || value === 'yes' || value === 'true'
|
|
62
|
-
})
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async select(message: string, choices: Array<{ name: string; value: any; description?: string }>): Promise<any> {
|
|
66
|
-
return this.prompt({
|
|
67
|
-
type: 'select',
|
|
68
|
-
message,
|
|
69
|
-
choices
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async input(message: string, defaultValue?: string, validate?: (value: string) => boolean | string): Promise<string> {
|
|
74
|
-
return this.prompt({
|
|
75
|
-
type: 'input',
|
|
76
|
-
message,
|
|
77
|
-
default: defaultValue,
|
|
78
|
-
validate
|
|
79
|
-
})
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
1
|
+
import type { PromptConfig } from "./types"
|
|
2
|
+
|
|
3
|
+
export class PromptSystem {
|
|
4
|
+
async prompt(config: PromptConfig): Promise<any> {
|
|
5
|
+
// Simple implementation using process.stdin
|
|
6
|
+
// In a real implementation, you'd use a library like inquirer or prompts
|
|
7
|
+
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
process.stdout.write(`${config.message} `)
|
|
10
|
+
|
|
11
|
+
if (config.default !== undefined) {
|
|
12
|
+
process.stdout.write(`(${config.default}) `)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (config.choices) {
|
|
16
|
+
process.stdout.write(`\nChoices: ${config.choices.map(c => c.name || c.value).join(', ')}\n`)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
process.stdout.write(': ')
|
|
20
|
+
|
|
21
|
+
process.stdin.once('data', (data) => {
|
|
22
|
+
const input = data.toString().trim()
|
|
23
|
+
|
|
24
|
+
if (!input && config.default !== undefined) {
|
|
25
|
+
resolve(config.default)
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (config.validate) {
|
|
30
|
+
const validation = config.validate(input)
|
|
31
|
+
if (validation !== true) {
|
|
32
|
+
console.log(`Error: ${validation}`)
|
|
33
|
+
// In a real implementation, you'd re-prompt
|
|
34
|
+
resolve(input)
|
|
35
|
+
return
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (config.choices) {
|
|
40
|
+
const choice = config.choices.find(c =>
|
|
41
|
+
c.name === input || c.value === input
|
|
42
|
+
)
|
|
43
|
+
if (choice) {
|
|
44
|
+
resolve(choice.value)
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
resolve(input)
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async confirm(message: string, defaultValue: boolean = false): Promise<boolean> {
|
|
55
|
+
return this.prompt({
|
|
56
|
+
type: 'confirm',
|
|
57
|
+
message: `${message} (y/n)`,
|
|
58
|
+
default: defaultValue
|
|
59
|
+
}).then(result => {
|
|
60
|
+
const value = String(result).toLowerCase()
|
|
61
|
+
return value === 'y' || value === 'yes' || value === 'true'
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async select(message: string, choices: Array<{ name: string; value: any; description?: string }>): Promise<any> {
|
|
66
|
+
return this.prompt({
|
|
67
|
+
type: 'select',
|
|
68
|
+
message,
|
|
69
|
+
choices
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async input(message: string, defaultValue?: string, validate?: (value: string) => boolean | string): Promise<string> {
|
|
74
|
+
return this.prompt({
|
|
75
|
+
type: 'input',
|
|
76
|
+
message,
|
|
77
|
+
default: defaultValue,
|
|
78
|
+
validate
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
83
|
export const promptSystem = new PromptSystem()
|
|
@@ -54,8 +54,8 @@ export class RouteGenerator implements Generator {
|
|
|
54
54
|
path: 'app/server/routes/{{kebabName}}.routes.ts',
|
|
55
55
|
content: `import { Elysia, t } from 'elysia'
|
|
56
56
|
import { {{pascalName}}Controller } from '../controllers/{{kebabName}}.controller'
|
|
57
|
-
import { errorHandler } from '
|
|
58
|
-
import { logger } from '
|
|
57
|
+
import { errorHandler } from '@core/utils/errors/middleware'
|
|
58
|
+
import { logger } from '@core/utils/logger'
|
|
59
59
|
|
|
60
60
|
const controller = new {{pascalName}}Controller()
|
|
61
61
|
|
|
@@ -308,9 +308,9 @@ export const {{camelName}}Routes = new Elysia({ prefix: '/api/{{kebabName}}s' })
|
|
|
308
308
|
path: 'app/server/routes/auth.routes.ts',
|
|
309
309
|
content: `import { Elysia, t } from 'elysia'
|
|
310
310
|
import { AuthController } from '../controllers/auth.controller'
|
|
311
|
-
import { errorHandler } from '
|
|
311
|
+
import { errorHandler } from '@core/utils/errors/middleware'
|
|
312
312
|
import { authMiddleware } from '../middleware/auth.middleware'
|
|
313
|
-
import { logger } from '
|
|
313
|
+
import { logger } from '@core/utils/logger'
|
|
314
314
|
|
|
315
315
|
const controller = new AuthController()
|
|
316
316
|
|
|
@@ -472,8 +472,8 @@ export const authRoutes = new Elysia({ prefix: '/api/auth' })
|
|
|
472
472
|
{
|
|
473
473
|
path: 'app/server/middleware/auth.middleware.ts',
|
|
474
474
|
content: `import { Context } from 'elysia'
|
|
475
|
-
import { UnauthorizedError } from '
|
|
476
|
-
import { logger } from '
|
|
475
|
+
import { UnauthorizedError } from '@core/utils/errors'
|
|
476
|
+
import { logger } from '@core/utils/logger'
|
|
477
477
|
|
|
478
478
|
// JWT verification function (implement based on your JWT library)
|
|
479
479
|
async function verifyJWT(token: string): Promise<any> {
|