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
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack CLI - Build Command
|
|
3
|
+
* Build the application for production
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { CLICommand } from '../command-registry'
|
|
7
|
+
import { FluxStackBuilder } from '@core/build'
|
|
8
|
+
import { fluxStackConfig } from '@config'
|
|
9
|
+
|
|
10
|
+
export const buildCommand: CLICommand = {
|
|
11
|
+
name: 'build',
|
|
12
|
+
description: 'Build the application for production',
|
|
13
|
+
category: 'Build',
|
|
14
|
+
usage: 'flux build [options]',
|
|
15
|
+
examples: [
|
|
16
|
+
'flux build # Build both frontend and backend',
|
|
17
|
+
'flux build --frontend-only # Build only frontend',
|
|
18
|
+
'flux build --backend-only # Build only backend'
|
|
19
|
+
],
|
|
20
|
+
options: [
|
|
21
|
+
{
|
|
22
|
+
name: 'frontend-only',
|
|
23
|
+
description: 'Build only frontend',
|
|
24
|
+
type: 'boolean'
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: 'backend-only',
|
|
28
|
+
description: 'Build only backend',
|
|
29
|
+
type: 'boolean'
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'production',
|
|
33
|
+
description: 'Build for production (minified)',
|
|
34
|
+
type: 'boolean',
|
|
35
|
+
default: true
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
handler: async (args, options, context) => {
|
|
39
|
+
const config = fluxStackConfig
|
|
40
|
+
|
|
41
|
+
// Load plugins for build hooks
|
|
42
|
+
const { PluginRegistry } = await import('@core/plugins/registry')
|
|
43
|
+
const { PluginManager } = await import('@core/plugins/manager')
|
|
44
|
+
const pluginRegistry = new PluginRegistry({ config, logger: context.logger })
|
|
45
|
+
const pluginManager = new PluginManager({ config, logger: context.logger })
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await pluginManager.initialize()
|
|
49
|
+
// Sync plugins to registry (same as framework does)
|
|
50
|
+
const discoveredPlugins = pluginManager.getRegistry().getAll()
|
|
51
|
+
for (const plugin of discoveredPlugins) {
|
|
52
|
+
if (!pluginRegistry.has(plugin.name)) {
|
|
53
|
+
(pluginRegistry as any).plugins.set(plugin.name, plugin)
|
|
54
|
+
if (plugin.dependencies) {
|
|
55
|
+
(pluginRegistry as any).dependencies.set(plugin.name, plugin.dependencies)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
(pluginRegistry as any).updateLoadOrder()
|
|
61
|
+
} catch (error) {
|
|
62
|
+
const plugins = (pluginRegistry as any).plugins as Map<string, any>
|
|
63
|
+
;(pluginRegistry as any).loadOrder = Array.from(plugins.keys())
|
|
64
|
+
}
|
|
65
|
+
} catch (error) {
|
|
66
|
+
context.logger.warn('Failed to load plugins for build hooks', { error })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const builder = new FluxStackBuilder(config, pluginRegistry)
|
|
70
|
+
|
|
71
|
+
if (options['frontend-only']) {
|
|
72
|
+
await builder.buildClient()
|
|
73
|
+
} else if (options['backend-only']) {
|
|
74
|
+
await builder.buildServer()
|
|
75
|
+
} else {
|
|
76
|
+
await builder.build()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack CLI - Create Command
|
|
3
|
+
* Create a new FluxStack project
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { CLICommand } from '../command-registry'
|
|
7
|
+
import { ProjectCreator } from '@core/templates/create-project'
|
|
8
|
+
|
|
9
|
+
export const createCommand: CLICommand = {
|
|
10
|
+
name: 'create',
|
|
11
|
+
description: 'Create a new FluxStack project',
|
|
12
|
+
category: 'Project',
|
|
13
|
+
usage: 'flux create <project-name> [template]',
|
|
14
|
+
examples: [
|
|
15
|
+
'flux create my-app # Create basic project',
|
|
16
|
+
'flux create my-app full # Create full-featured project'
|
|
17
|
+
],
|
|
18
|
+
arguments: [
|
|
19
|
+
{
|
|
20
|
+
name: 'project-name',
|
|
21
|
+
description: 'Name of the project to create',
|
|
22
|
+
required: true,
|
|
23
|
+
type: 'string'
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
name: 'template',
|
|
27
|
+
description: 'Project template to use',
|
|
28
|
+
required: false,
|
|
29
|
+
type: 'string',
|
|
30
|
+
default: 'basic',
|
|
31
|
+
choices: ['basic', 'full']
|
|
32
|
+
}
|
|
33
|
+
],
|
|
34
|
+
handler: async (args, options, context) => {
|
|
35
|
+
const [projectName, template] = args
|
|
36
|
+
|
|
37
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(projectName)) {
|
|
38
|
+
console.error("❌ Project name can only contain letters, numbers, hyphens, and underscores")
|
|
39
|
+
return
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const creator = new ProjectCreator({
|
|
44
|
+
name: projectName,
|
|
45
|
+
template: template as 'basic' | 'full' || 'basic'
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
await creator.create()
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error("❌ Failed to create project:", error instanceof Error ? error.message : String(error))
|
|
51
|
+
throw error
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack CLI - Dev Command
|
|
3
|
+
* Start full-stack development server with hot reload
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { CLICommand } from '../command-registry'
|
|
7
|
+
import { serverConfig, clientConfig } from '@config'
|
|
8
|
+
|
|
9
|
+
export const devCommand: CLICommand = {
|
|
10
|
+
name: 'dev',
|
|
11
|
+
description: 'Start full-stack development server',
|
|
12
|
+
category: 'Development',
|
|
13
|
+
usage: 'flux dev [options]',
|
|
14
|
+
examples: [
|
|
15
|
+
'flux dev # Start full-stack development',
|
|
16
|
+
'flux dev --port 4000 # Start on custom port',
|
|
17
|
+
'flux dev --frontend-only # Start only frontend (Vite)',
|
|
18
|
+
'flux dev --backend-only # Start only backend (Elysia)'
|
|
19
|
+
],
|
|
20
|
+
options: [
|
|
21
|
+
{
|
|
22
|
+
name: 'port',
|
|
23
|
+
short: 'p',
|
|
24
|
+
description: 'Port for backend server',
|
|
25
|
+
type: 'number',
|
|
26
|
+
default: serverConfig.server.port
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
name: 'frontend-port',
|
|
30
|
+
description: 'Port for frontend server',
|
|
31
|
+
type: 'number',
|
|
32
|
+
default: clientConfig.vite.port
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'frontend-only',
|
|
36
|
+
short: 'f',
|
|
37
|
+
description: 'Start only the frontend (Vite dev server)',
|
|
38
|
+
type: 'boolean',
|
|
39
|
+
default: false
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'backend-only',
|
|
43
|
+
short: 'b',
|
|
44
|
+
description: 'Start only the backend (Elysia server)',
|
|
45
|
+
type: 'boolean',
|
|
46
|
+
default: false
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
handler: async (args, options, context) => {
|
|
50
|
+
const { spawn } = await import("child_process")
|
|
51
|
+
|
|
52
|
+
const frontendOnly = options['frontend-only'] === true
|
|
53
|
+
const backendOnly = options['backend-only'] === true
|
|
54
|
+
|
|
55
|
+
if (frontendOnly && backendOnly) {
|
|
56
|
+
console.error('❌ Cannot use --frontend-only and --backend-only together')
|
|
57
|
+
process.exit(1)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Determine mode and entry point
|
|
61
|
+
const mode = frontendOnly ? 'Frontend only' : backendOnly ? 'Backend only' : 'Full-stack'
|
|
62
|
+
|
|
63
|
+
// Frontend-only: roda direto do core (não passa pelo app/server/index.ts)
|
|
64
|
+
const entryPoint = frontendOnly
|
|
65
|
+
? 'core/client/standalone-entry.ts'
|
|
66
|
+
: 'app/server/index.ts'
|
|
67
|
+
|
|
68
|
+
const fluxstackMode = backendOnly ? 'backend-only' : 'full-stack'
|
|
69
|
+
|
|
70
|
+
console.log(`⚡ Starting ${mode} development server...`)
|
|
71
|
+
|
|
72
|
+
const devProcess = spawn("bun", ["--watch", entryPoint], {
|
|
73
|
+
stdio: "inherit",
|
|
74
|
+
cwd: process.cwd(),
|
|
75
|
+
env: {
|
|
76
|
+
...process.env,
|
|
77
|
+
FRONTEND_PORT: options['frontend-port'].toString(),
|
|
78
|
+
BACKEND_PORT: options.port.toString(),
|
|
79
|
+
FLUXSTACK_MODE: fluxstackMode
|
|
80
|
+
}
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
process.on('SIGINT', () => {
|
|
84
|
+
console.log('\n🛑 Shutting down gracefully...')
|
|
85
|
+
devProcess.kill('SIGTERM')
|
|
86
|
+
setTimeout(() => {
|
|
87
|
+
devProcess.kill('SIGKILL')
|
|
88
|
+
process.exit(0)
|
|
89
|
+
}, 5000)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
devProcess.on('close', (code) => {
|
|
93
|
+
process.exit(code || 0)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
// Keep the CLI running until the child process exits
|
|
97
|
+
return new Promise((resolve) => {
|
|
98
|
+
devProcess.on('exit', resolve)
|
|
99
|
+
})
|
|
100
|
+
}
|
|
101
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack CLI - Help Command
|
|
3
|
+
* Display help information for commands
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { CLICommand } from '../command-registry'
|
|
7
|
+
import { cliRegistry } from '../command-registry'
|
|
8
|
+
|
|
9
|
+
export const helpCommand: CLICommand = {
|
|
10
|
+
name: 'help',
|
|
11
|
+
description: 'Show help information',
|
|
12
|
+
category: 'General',
|
|
13
|
+
aliases: ['h', '--help', '-h'],
|
|
14
|
+
arguments: [
|
|
15
|
+
{
|
|
16
|
+
name: 'command',
|
|
17
|
+
description: 'Command to show help for',
|
|
18
|
+
required: false
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
handler: async (args, options, context) => {
|
|
22
|
+
if (args[0]) {
|
|
23
|
+
const targetCommand = cliRegistry.get(args[0])
|
|
24
|
+
if (targetCommand) {
|
|
25
|
+
cliRegistry.showCommandHelp(targetCommand)
|
|
26
|
+
} else {
|
|
27
|
+
console.error(`❌ Unknown command: ${args[0]}`)
|
|
28
|
+
cliRegistry.showHelp()
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
cliRegistry.showHelp()
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack CLI Commands
|
|
3
|
+
* Central export for all command modules
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// General commands
|
|
7
|
+
export { helpCommand } from './help'
|
|
8
|
+
|
|
9
|
+
// Development commands
|
|
10
|
+
export { devCommand } from './dev'
|
|
11
|
+
|
|
12
|
+
// Build commands
|
|
13
|
+
export { buildCommand } from './build'
|
|
14
|
+
|
|
15
|
+
// Project commands
|
|
16
|
+
export { createCommand } from './create'
|
|
17
|
+
|
|
18
|
+
// Plugin commands (management)
|
|
19
|
+
export { makePluginCommand } from './make-plugin'
|
|
20
|
+
|
|
21
|
+
// Export all commands as an array for easy registration
|
|
22
|
+
import { helpCommand } from './help'
|
|
23
|
+
import { devCommand } from './dev'
|
|
24
|
+
import { buildCommand } from './build'
|
|
25
|
+
import { createCommand } from './create'
|
|
26
|
+
import { makePluginCommand } from './make-plugin'
|
|
27
|
+
|
|
28
|
+
export const builtInCommands = [
|
|
29
|
+
helpCommand,
|
|
30
|
+
devCommand,
|
|
31
|
+
buildCommand,
|
|
32
|
+
createCommand,
|
|
33
|
+
makePluginCommand,
|
|
34
|
+
]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack CLI - Make Plugin Command
|
|
3
|
+
* Create a new FluxStack plugin
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { CLICommand } from '../command-registry'
|
|
7
|
+
|
|
8
|
+
export const makePluginCommand: CLICommand = {
|
|
9
|
+
name: 'make:plugin',
|
|
10
|
+
description: 'Create a new FluxStack plugin',
|
|
11
|
+
category: 'Plugins',
|
|
12
|
+
usage: 'flux make:plugin <name> [options]',
|
|
13
|
+
aliases: ['create:plugin'],
|
|
14
|
+
examples: [
|
|
15
|
+
'flux make:plugin my-plugin # Create basic plugin',
|
|
16
|
+
'flux make:plugin my-plugin --template full # Create full plugin with server/client',
|
|
17
|
+
'flux make:plugin auth --template server # Create server-only plugin'
|
|
18
|
+
],
|
|
19
|
+
arguments: [
|
|
20
|
+
{
|
|
21
|
+
name: 'name',
|
|
22
|
+
description: 'Name of the plugin to create',
|
|
23
|
+
required: true,
|
|
24
|
+
type: 'string'
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
options: [
|
|
28
|
+
{
|
|
29
|
+
name: 'template',
|
|
30
|
+
short: 't',
|
|
31
|
+
description: 'Plugin template to use',
|
|
32
|
+
type: 'string',
|
|
33
|
+
choices: ['basic', 'full', 'server', 'client'],
|
|
34
|
+
default: 'basic'
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: 'description',
|
|
38
|
+
short: 'd',
|
|
39
|
+
description: 'Plugin description',
|
|
40
|
+
type: 'string',
|
|
41
|
+
default: 'A FluxStack plugin'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: 'force',
|
|
45
|
+
short: 'f',
|
|
46
|
+
description: 'Overwrite existing plugin',
|
|
47
|
+
type: 'boolean',
|
|
48
|
+
default: false
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
handler: async (args, options, context) => {
|
|
52
|
+
const [name] = args
|
|
53
|
+
|
|
54
|
+
if (!/^[a-zA-Z0-9-_]+$/.test(name)) {
|
|
55
|
+
console.error("❌ Plugin name can only contain letters, numbers, hyphens, and underscores")
|
|
56
|
+
return
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Use the plugin generator
|
|
60
|
+
const { generatorRegistry } = await import('../generators/index')
|
|
61
|
+
const pluginGenerator = generatorRegistry.get('plugin')
|
|
62
|
+
|
|
63
|
+
if (!pluginGenerator) {
|
|
64
|
+
console.error("❌ Plugin generator not found")
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const generatorContext = {
|
|
69
|
+
workingDir: context.workingDir,
|
|
70
|
+
config: context.config,
|
|
71
|
+
logger: context.logger,
|
|
72
|
+
utils: context.utils
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const generatorOptions = {
|
|
76
|
+
name,
|
|
77
|
+
template: options.template,
|
|
78
|
+
force: options.force,
|
|
79
|
+
dryRun: false,
|
|
80
|
+
description: options.description
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
await pluginGenerator.generate(generatorContext, generatorOptions)
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error("❌ Failed to create plugin:", error instanceof Error ? error.message : String(error))
|
|
87
|
+
throw error
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack CLI - Plugin Add Command
|
|
3
|
+
* Safely install and 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 PluginAddOptions {
|
|
13
|
+
skipAudit?: boolean
|
|
14
|
+
skipConfirmation?: boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createPluginAddCommand(): Command {
|
|
18
|
+
const command = new Command('plugin:add')
|
|
19
|
+
.description('Install and whitelist an NPM plugin securely')
|
|
20
|
+
.argument('<plugin-name>', 'Name of the plugin to install (e.g., fluxstack-plugin-auth)')
|
|
21
|
+
.option('--skip-audit', 'Skip npm audit check')
|
|
22
|
+
.option('--skip-confirmation', 'Skip confirmation prompt')
|
|
23
|
+
.action(async (pluginName: string, options: PluginAddOptions) => {
|
|
24
|
+
console.log(chalk.blue('\n🔌 FluxStack Plugin Installer\n'))
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
// 1. Validate plugin name
|
|
28
|
+
if (!isValidPluginName(pluginName)) {
|
|
29
|
+
console.error(chalk.red(`❌ Invalid plugin name: ${pluginName}`))
|
|
30
|
+
console.log(chalk.yellow('\n📝 Valid plugin names:'))
|
|
31
|
+
console.log(' - fluxstack-plugin-*')
|
|
32
|
+
console.log(' - fplugin-*')
|
|
33
|
+
console.log(' - @fluxstack/plugin-*')
|
|
34
|
+
console.log(' - @fplugin/*')
|
|
35
|
+
console.log(' - @org/fluxstack-plugin-*')
|
|
36
|
+
console.log(' - @org/fplugin-*')
|
|
37
|
+
process.exit(1)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 2. Check if plugin already installed
|
|
41
|
+
const packageJsonPath = join(process.cwd(), 'package.json')
|
|
42
|
+
if (!existsSync(packageJsonPath)) {
|
|
43
|
+
console.error(chalk.red('❌ package.json not found'))
|
|
44
|
+
process.exit(1)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'))
|
|
48
|
+
const isAlreadyInstalled =
|
|
49
|
+
packageJson.dependencies?.[pluginName] ||
|
|
50
|
+
packageJson.devDependencies?.[pluginName]
|
|
51
|
+
|
|
52
|
+
if (isAlreadyInstalled && !options.skipConfirmation) {
|
|
53
|
+
console.log(chalk.yellow(`⚠️ Plugin ${pluginName} is already installed`))
|
|
54
|
+
console.log(chalk.yellow(' Will only update whitelist\n'))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 3. Audit plugin (unless skipped)
|
|
58
|
+
if (!options.skipAudit && !isAlreadyInstalled) {
|
|
59
|
+
console.log(chalk.blue('🔍 Auditing plugin security...\n'))
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
// Get plugin info
|
|
63
|
+
const info = await $`npm view ${pluginName} repository homepage version description`.text()
|
|
64
|
+
console.log(chalk.gray(info))
|
|
65
|
+
|
|
66
|
+
// Run audit
|
|
67
|
+
console.log(chalk.blue('\n🛡️ Running npm audit...\n'))
|
|
68
|
+
const auditResult = await $`npm audit ${pluginName}`.text()
|
|
69
|
+
console.log(chalk.gray(auditResult))
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.warn(chalk.yellow(`⚠️ Could not audit plugin: ${error instanceof Error ? error.message : 'Unknown error'}`))
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// 4. Confirmation prompt (unless skipped)
|
|
76
|
+
if (!options.skipConfirmation) {
|
|
77
|
+
console.log(chalk.yellow('\n⚠️ Security Warning:'))
|
|
78
|
+
console.log(chalk.yellow(' NPM plugins can execute arbitrary code'))
|
|
79
|
+
console.log(chalk.yellow(' Only install plugins from trusted sources\n'))
|
|
80
|
+
|
|
81
|
+
const answer = prompt(chalk.blue('Continue with installation? (yes/no): '))
|
|
82
|
+
if (answer?.toLowerCase() !== 'yes' && answer?.toLowerCase() !== 'y') {
|
|
83
|
+
console.log(chalk.red('❌ Installation cancelled'))
|
|
84
|
+
process.exit(0)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 5. Install plugin
|
|
89
|
+
if (!isAlreadyInstalled) {
|
|
90
|
+
console.log(chalk.blue(`\n📦 Installing ${pluginName}...\n`))
|
|
91
|
+
await $`bun add ${pluginName}`.quiet()
|
|
92
|
+
console.log(chalk.green(`✅ Plugin installed successfully`))
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// 6. Update .env file
|
|
96
|
+
console.log(chalk.blue('\n🔧 Updating configuration...\n'))
|
|
97
|
+
updateEnvFile(pluginName)
|
|
98
|
+
|
|
99
|
+
// 7. Success message
|
|
100
|
+
console.log(chalk.green('\n✅ Plugin setup complete!\n'))
|
|
101
|
+
console.log(chalk.blue('📋 What was done:'))
|
|
102
|
+
if (!isAlreadyInstalled) {
|
|
103
|
+
console.log(chalk.gray(` • Installed ${pluginName}`))
|
|
104
|
+
}
|
|
105
|
+
console.log(chalk.gray(' • Enabled NPM plugin discovery (PLUGINS_DISCOVER_NPM=true)'))
|
|
106
|
+
console.log(chalk.gray(` • Added ${pluginName} to whitelist (PLUGINS_ALLOWED)`))
|
|
107
|
+
|
|
108
|
+
console.log(chalk.blue('\n🚀 Next steps:'))
|
|
109
|
+
console.log(chalk.gray(' 1. Restart your dev server: bun run dev'))
|
|
110
|
+
console.log(chalk.gray(' 2. Plugin will be auto-discovered and loaded'))
|
|
111
|
+
console.log(chalk.gray(' 3. Check logs for plugin initialization'))
|
|
112
|
+
|
|
113
|
+
} catch (error) {
|
|
114
|
+
console.error(chalk.red('\n❌ Failed to install plugin:'))
|
|
115
|
+
console.error(chalk.red(error instanceof Error ? error.message : String(error)))
|
|
116
|
+
process.exit(1)
|
|
117
|
+
}
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
return command
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Validate plugin name against FluxStack naming conventions
|
|
125
|
+
*/
|
|
126
|
+
function isValidPluginName(name: string): boolean {
|
|
127
|
+
const patterns = [
|
|
128
|
+
/^fluxstack-plugin-/,
|
|
129
|
+
/^fplugin-/,
|
|
130
|
+
/^@fluxstack\/plugin-/,
|
|
131
|
+
/^@fplugin\//,
|
|
132
|
+
/^@[\w-]+\/fluxstack-plugin-/,
|
|
133
|
+
/^@[\w-]+\/fplugin-/,
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
return patterns.some(pattern => pattern.test(name))
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Update .env file with plugin configuration
|
|
141
|
+
*/
|
|
142
|
+
function updateEnvFile(pluginName: string): void {
|
|
143
|
+
const envPath = join(process.cwd(), '.env')
|
|
144
|
+
|
|
145
|
+
if (!existsSync(envPath)) {
|
|
146
|
+
console.warn(chalk.yellow('⚠️ .env file not found, creating...'))
|
|
147
|
+
writeFileSync(envPath, '', 'utf-8')
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let envContent = readFileSync(envPath, 'utf-8')
|
|
151
|
+
let updated = false
|
|
152
|
+
|
|
153
|
+
// 1. Enable NPM plugin discovery
|
|
154
|
+
if (/^PLUGINS_DISCOVER_NPM=false/m.test(envContent)) {
|
|
155
|
+
envContent = envContent.replace(
|
|
156
|
+
/^PLUGINS_DISCOVER_NPM=false/m,
|
|
157
|
+
'PLUGINS_DISCOVER_NPM=true'
|
|
158
|
+
)
|
|
159
|
+
updated = true
|
|
160
|
+
console.log(chalk.gray(' • Set PLUGINS_DISCOVER_NPM=true'))
|
|
161
|
+
} else if (!/^PLUGINS_DISCOVER_NPM=/m.test(envContent)) {
|
|
162
|
+
envContent += '\n# Plugin Discovery\nPLUGINS_DISCOVER_NPM=true\n'
|
|
163
|
+
updated = true
|
|
164
|
+
console.log(chalk.gray(' • Added PLUGINS_DISCOVER_NPM=true'))
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 2. Add plugin to whitelist
|
|
168
|
+
const allowedPluginsRegex = /^PLUGINS_ALLOWED=(.*)$/m
|
|
169
|
+
const match = envContent.match(allowedPluginsRegex)
|
|
170
|
+
|
|
171
|
+
if (match) {
|
|
172
|
+
const currentPlugins = match[1]
|
|
173
|
+
.split(',')
|
|
174
|
+
.map(p => p.trim())
|
|
175
|
+
.filter(p => p.length > 0)
|
|
176
|
+
|
|
177
|
+
if (!currentPlugins.includes(pluginName)) {
|
|
178
|
+
const newPlugins = [...currentPlugins, pluginName].join(',')
|
|
179
|
+
envContent = envContent.replace(
|
|
180
|
+
allowedPluginsRegex,
|
|
181
|
+
`PLUGINS_ALLOWED=${newPlugins}`
|
|
182
|
+
)
|
|
183
|
+
updated = true
|
|
184
|
+
console.log(chalk.gray(` • Added ${pluginName} to PLUGINS_ALLOWED`))
|
|
185
|
+
} else {
|
|
186
|
+
console.log(chalk.gray(` • ${pluginName} already in PLUGINS_ALLOWED`))
|
|
187
|
+
}
|
|
188
|
+
} else {
|
|
189
|
+
envContent += `PLUGINS_ALLOWED=${pluginName}\n`
|
|
190
|
+
updated = true
|
|
191
|
+
console.log(chalk.gray(` • Created PLUGINS_ALLOWED with ${pluginName}`))
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (updated) {
|
|
195
|
+
writeFileSync(envPath, envContent, 'utf-8')
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import { Command } from 'commander'
|
|
6
6
|
import chalk from 'chalk'
|
|
7
|
-
import { PluginDependencyManager } from '
|
|
8
|
-
import { PluginRegistry } from '
|
|
7
|
+
import { PluginDependencyManager } from '@core/plugins/dependency-manager'
|
|
8
|
+
import { PluginRegistry } from '@core/plugins/registry'
|
|
9
9
|
import { existsSync, readFileSync } from 'fs'
|
|
10
10
|
import { join } from 'path'
|
|
11
11
|
|