create-fluxstack 1.8.1 โ 1.9.1
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/.env.example +19 -0
- package/README.md +653 -272
- package/app/client/SIMPLIFICATION.md +140 -0
- package/app/client/frontend-only.ts +1 -1
- package/app/client/src/App.tsx +148 -283
- package/app/client/src/index.css +5 -20
- package/app/client/src/lib/eden-api.ts +53 -220
- package/app/client/src/main.tsx +2 -3
- package/app/server/controllers/users.controller.ts +57 -31
- package/app/server/index.ts +5 -2
- package/app/server/live/register-components.ts +18 -7
- package/app/server/routes/env-test.ts +53 -2
- package/app/server/routes/index.ts +1 -8
- package/app/server/routes/users.routes.ts +192 -91
- package/config/fluxstack.config.ts +2 -2
- package/config/plugins.config.ts +22 -1
- package/core/build/bundler.ts +199 -55
- package/core/build/flux-plugins-generator.ts +5 -5
- package/core/build/index.ts +4 -0
- package/core/build/live-components-generator.ts +15 -12
- package/core/cli/command-registry.ts +4 -14
- package/core/cli/commands/plugin-deps.ts +8 -8
- package/core/cli/generators/component.ts +3 -3
- package/core/cli/generators/controller.ts +4 -4
- package/core/cli/generators/index.ts +8 -8
- package/core/cli/generators/interactive.ts +4 -4
- package/core/cli/generators/plugin.ts +9 -9
- package/core/cli/generators/prompts.ts +1 -1
- package/core/cli/generators/route.ts +27 -11
- package/core/cli/generators/service.ts +5 -5
- package/core/cli/generators/template-engine.ts +1 -1
- package/core/cli/generators/types.ts +1 -1
- package/core/cli/index.ts +258 -193
- package/core/cli/plugin-discovery.ts +3 -3
- package/core/client/hooks/index.ts +2 -2
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +1 -1
- package/core/client/hooks/useChunkedUpload.ts +1 -1
- package/core/client/hooks/useHybridLiveComponent.ts +1 -1
- package/core/client/hooks/useWebSocket.ts +1 -1
- package/core/config/env.ts +1 -1
- package/core/config/runtime-config.ts +5 -5
- package/core/config/schema.ts +9 -0
- package/core/framework/server.ts +30 -15
- package/core/framework/types.ts +2 -2
- package/core/live/ComponentRegistry.ts +1 -1
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +1 -1
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +65 -161
- package/core/plugins/built-in/static/index.ts +18 -47
- package/core/plugins/built-in/swagger/index.ts +301 -231
- package/core/plugins/built-in/vite/index.ts +74 -109
- package/core/plugins/config.ts +2 -2
- package/core/plugins/dependency-manager.ts +2 -2
- package/core/plugins/discovery.ts +1 -1
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/manager.ts +19 -4
- package/core/plugins/module-resolver.ts +1 -1
- package/core/plugins/registry.ts +3 -3
- package/core/plugins/types.ts +147 -5
- package/core/server/framework.ts +2 -2
- package/core/server/live/ComponentRegistry.ts +9 -26
- package/core/server/live/FileUploadManager.ts +1 -1
- package/core/server/live/auto-generated-components.ts +26 -0
- package/core/server/live/websocket-plugin.ts +211 -19
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +4 -4
- package/core/server/plugins/database.ts +1 -2
- package/core/server/plugins/static-files-plugin.ts +259 -231
- package/core/server/plugins/swagger.ts +1 -1
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +4 -4
- package/core/server/standalone.ts +16 -1
- package/core/testing/index.ts +1 -1
- package/core/testing/setup.ts +1 -1
- package/core/types/build.ts +21 -0
- package/core/utils/logger/startup-banner.ts +7 -33
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +73 -30
- package/package.json +3 -2
- package/plugins/crypto-auth/index.ts +52 -47
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/helpers.ts +16 -1
- package/tsconfig.json +2 -1
- package/vitest.config.ts +11 -2
- package/app/client/src/App.css +0 -883
- package/app/client/src/components/ErrorBoundary.tsx +0 -107
- package/app/client/src/components/ErrorDisplay.css +0 -365
- package/app/client/src/components/ErrorDisplay.tsx +0 -258
- package/app/client/src/components/FluxStackConfig.tsx +0 -1321
- package/app/client/src/components/HybridLiveCounter.tsx +0 -140
- package/app/client/src/components/LiveClock.tsx +0 -286
- package/app/client/src/components/MainLayout.tsx +0 -388
- package/app/client/src/components/SidebarNavigation.tsx +0 -391
- package/app/client/src/components/StateDemo.tsx +0 -178
- package/app/client/src/components/SystemMonitor.tsx +0 -1044
- package/app/client/src/components/UserProfile.tsx +0 -809
- package/app/client/src/hooks/useAuth.ts +0 -39
- package/app/client/src/hooks/useNotifications.ts +0 -56
- package/app/client/src/lib/errors.ts +0 -340
- package/app/client/src/lib/hooks/useErrorHandler.ts +0 -258
- package/app/client/src/lib/index.ts +0 -45
- package/app/client/src/pages/ApiDocs.tsx +0 -182
- package/app/client/src/pages/CryptoAuthPage.tsx +0 -394
- package/app/client/src/pages/Demo.tsx +0 -174
- package/app/client/src/pages/HybridLive.tsx +0 -263
- package/app/client/src/pages/Overview.tsx +0 -155
- package/app/client/src/store/README.md +0 -43
- package/app/client/src/store/index.ts +0 -16
- package/app/client/src/store/slices/uiSlice.ts +0 -151
- package/app/client/src/store/slices/userSlice.ts +0 -161
- package/app/client/src/test/README.md +0 -257
- package/app/client/src/test/setup.ts +0 -70
- package/app/client/src/test/types.ts +0 -12
- package/app/server/live/CounterComponent.ts +0 -191
- package/app/server/live/FluxStackConfig.ts +0 -534
- package/app/server/live/SidebarNavigation.ts +0 -157
- package/app/server/live/SystemMonitor.ts +0 -595
- package/app/server/live/SystemMonitorIntegration.ts +0 -151
- package/app/server/live/UserProfileComponent.ts +0 -141
- package/app/server/middleware/auth.ts +0 -136
- package/app/server/middleware/errorHandling.ts +0 -252
- package/app/server/middleware/index.ts +0 -10
- package/app/server/middleware/rateLimit.ts +0 -193
- package/app/server/middleware/requestLogging.ts +0 -215
- package/app/server/middleware/validation.ts +0 -270
- package/app/server/routes/config.ts +0 -145
- package/app/server/routes/crypto-auth-demo.routes.ts +0 -167
- package/app/server/routes/example-with-crypto-auth.routes.ts +0 -235
- package/app/server/routes/exemplo-posts.routes.ts +0 -161
- package/app/server/routes/upload.ts +0 -92
- package/app/server/services/NotificationService.ts +0 -302
- package/app/server/services/UserService.ts +0 -222
- package/app/server/services/index.ts +0 -46
- package/app/server/types/index.ts +0 -1
|
@@ -1,114 +1,215 @@
|
|
|
1
|
-
import { Elysia, t } from
|
|
2
|
-
import { UsersController } from
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
import { Elysia, t } from 'elysia'
|
|
2
|
+
import { UsersController } from '@/app/server/controllers/users.controller'
|
|
3
|
+
|
|
4
|
+
// ===== Request/Response Schemas =====
|
|
5
|
+
|
|
6
|
+
const UserSchema = t.Object({
|
|
7
|
+
id: t.Number(),
|
|
8
|
+
name: t.String(),
|
|
9
|
+
email: t.String()
|
|
10
|
+
}, {
|
|
11
|
+
description: 'User object'
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const CreateUserRequestSchema = t.Object({
|
|
15
|
+
name: t.String({ minLength: 2, description: 'User name (minimum 2 characters)' }),
|
|
16
|
+
email: t.String({ format: 'email', description: 'Valid email address' })
|
|
17
|
+
}, {
|
|
18
|
+
description: 'Request body for creating a new user'
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
const CreateUserResponseSchema = t.Union([
|
|
22
|
+
t.Object({
|
|
23
|
+
success: t.Literal(true),
|
|
24
|
+
user: UserSchema,
|
|
25
|
+
message: t.Optional(t.String())
|
|
26
|
+
}),
|
|
27
|
+
t.Object({
|
|
28
|
+
success: t.Literal(false),
|
|
29
|
+
error: t.String()
|
|
30
|
+
})
|
|
31
|
+
], {
|
|
32
|
+
description: 'Response after attempting to create a user'
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const GetUsersResponseSchema = t.Object({
|
|
36
|
+
success: t.Boolean(),
|
|
37
|
+
users: t.Array(UserSchema),
|
|
38
|
+
count: t.Number()
|
|
39
|
+
}, {
|
|
40
|
+
description: 'List of all users'
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const GetUserResponseSchema = t.Union([
|
|
44
|
+
t.Object({
|
|
45
|
+
success: t.Literal(true),
|
|
46
|
+
user: UserSchema
|
|
47
|
+
}),
|
|
48
|
+
t.Object({
|
|
49
|
+
success: t.Literal(false),
|
|
50
|
+
error: t.String()
|
|
51
|
+
})
|
|
52
|
+
], {
|
|
53
|
+
description: 'Single user or error'
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const DeleteUserResponseSchema = t.Union([
|
|
57
|
+
t.Object({
|
|
58
|
+
success: t.Literal(true),
|
|
59
|
+
message: t.String()
|
|
60
|
+
}),
|
|
61
|
+
t.Object({
|
|
62
|
+
success: t.Literal(false),
|
|
63
|
+
message: t.String()
|
|
64
|
+
})
|
|
65
|
+
], {
|
|
66
|
+
description: 'Result of delete operation'
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const ErrorResponseSchema = t.Object({
|
|
70
|
+
error: t.String()
|
|
71
|
+
}, {
|
|
72
|
+
description: 'Error response'
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Users API Routes
|
|
77
|
+
*/
|
|
78
|
+
export const usersRoutes = new Elysia({ prefix: '/users', tags: ['Users'] })
|
|
79
|
+
// GET /users - Get all users
|
|
80
|
+
.get('/', async () => {
|
|
81
|
+
return await UsersController.getUsers()
|
|
82
|
+
}, {
|
|
14
83
|
detail: {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
84
|
+
summary: 'Get All Users',
|
|
85
|
+
description: 'Retrieves a list of all registered users',
|
|
86
|
+
tags: ['Users', 'CRUD']
|
|
87
|
+
},
|
|
88
|
+
response: GetUsersResponseSchema
|
|
19
89
|
})
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
90
|
+
|
|
91
|
+
// GET /users/:id - Get user by ID
|
|
92
|
+
.get('/:id', async ({ params, set }) => {
|
|
93
|
+
const id = parseInt(params.id)
|
|
94
|
+
|
|
95
|
+
// Handle invalid ID
|
|
96
|
+
if (isNaN(id)) {
|
|
97
|
+
set.status = 400
|
|
98
|
+
return { error: 'ID invรกlido' }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const result = await UsersController.getUserById(id)
|
|
102
|
+
|
|
25
103
|
if (!result) {
|
|
26
104
|
set.status = 404
|
|
27
|
-
return { error:
|
|
105
|
+
return { error: 'Usuรกrio nรฃo encontrado' }
|
|
28
106
|
}
|
|
29
|
-
|
|
107
|
+
|
|
30
108
|
return result
|
|
31
109
|
}, {
|
|
32
|
-
params: t.Object({
|
|
33
|
-
id: t.String()
|
|
34
|
-
}),
|
|
35
110
|
detail: {
|
|
36
|
-
tags: ['Users'],
|
|
37
111
|
summary: 'Get User by ID',
|
|
38
|
-
description: '
|
|
112
|
+
description: 'Retrieves a single user by their unique identifier',
|
|
113
|
+
tags: ['Users', 'CRUD']
|
|
114
|
+
},
|
|
115
|
+
params: t.Object({
|
|
116
|
+
id: t.String({ description: 'User ID' })
|
|
117
|
+
}),
|
|
118
|
+
response: {
|
|
119
|
+
200: GetUserResponseSchema,
|
|
120
|
+
400: ErrorResponseSchema,
|
|
121
|
+
404: ErrorResponseSchema
|
|
39
122
|
}
|
|
40
123
|
})
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
124
|
+
|
|
125
|
+
// POST /users - Create new user
|
|
126
|
+
.post('/', async ({ body, set }) => {
|
|
127
|
+
// Validate required fields
|
|
128
|
+
if (!body.name || !body.email) {
|
|
129
|
+
set.status = 400
|
|
130
|
+
return {
|
|
131
|
+
success: false,
|
|
132
|
+
error: 'Nome e email sรฃo obrigatรณrios'
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Validate name length
|
|
137
|
+
if (body.name.length < 2) {
|
|
138
|
+
set.status = 400
|
|
139
|
+
return {
|
|
140
|
+
success: false,
|
|
141
|
+
error: 'Nome deve ter pelo menos 2 caracteres'
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Validate email format
|
|
146
|
+
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
|
|
147
|
+
if (!emailRegex.test(body.email)) {
|
|
46
148
|
set.status = 400
|
|
47
|
-
return {
|
|
48
|
-
success: false,
|
|
49
|
-
error:
|
|
50
|
-
details: error instanceof Error ? error.message : 'Unknown error'
|
|
149
|
+
return {
|
|
150
|
+
success: false,
|
|
151
|
+
error: 'Email invรกlido'
|
|
51
152
|
}
|
|
52
153
|
}
|
|
154
|
+
|
|
155
|
+
const result = await UsersController.createUser(body)
|
|
156
|
+
|
|
157
|
+
// If email is duplicate, still return 200 but with success: false
|
|
158
|
+
if (!result.success) {
|
|
159
|
+
return result
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return result
|
|
53
163
|
}, {
|
|
54
|
-
body: t.Object({
|
|
55
|
-
name: t.String({ minLength: 2 }),
|
|
56
|
-
email: t.String({ format: "email" })
|
|
57
|
-
}),
|
|
58
|
-
response: t.Object({
|
|
59
|
-
success: t.Boolean(),
|
|
60
|
-
user: t.Optional(t.Object({
|
|
61
|
-
id: t.Number(),
|
|
62
|
-
name: t.String(),
|
|
63
|
-
email: t.String(),
|
|
64
|
-
createdAt: t.Date()
|
|
65
|
-
})),
|
|
66
|
-
message: t.Optional(t.String())
|
|
67
|
-
}),
|
|
68
164
|
detail: {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
165
|
+
summary: 'Create New User',
|
|
166
|
+
description: 'Creates a new user with name and email. Email must be unique.',
|
|
167
|
+
tags: ['Users', 'CRUD']
|
|
72
168
|
},
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
details: error.message
|
|
81
|
-
}
|
|
82
|
-
default:
|
|
83
|
-
set.status = 500
|
|
84
|
-
return {
|
|
85
|
-
success: false,
|
|
86
|
-
error: "Erro interno do servidor"
|
|
87
|
-
}
|
|
88
|
-
}
|
|
169
|
+
body: CreateUserRequestSchema,
|
|
170
|
+
response: {
|
|
171
|
+
200: CreateUserResponseSchema,
|
|
172
|
+
400: t.Object({
|
|
173
|
+
success: t.Literal(false),
|
|
174
|
+
error: t.String()
|
|
175
|
+
})
|
|
89
176
|
}
|
|
90
177
|
})
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
178
|
+
|
|
179
|
+
// DELETE /users/:id - Delete user
|
|
180
|
+
.delete('/:id', async ({ params, set }) => {
|
|
181
|
+
const id = parseInt(params.id)
|
|
182
|
+
|
|
183
|
+
if (isNaN(id)) {
|
|
184
|
+
set.status = 400
|
|
185
|
+
return {
|
|
186
|
+
success: false,
|
|
187
|
+
message: 'ID invรกlido'
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const result = await UsersController.deleteUser(id)
|
|
192
|
+
|
|
193
|
+
if (!result.success) {
|
|
194
|
+
// Don't set 404 status, just return success: false
|
|
195
|
+
return result
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return result
|
|
95
199
|
}, {
|
|
96
|
-
params: t.Object({
|
|
97
|
-
id: t.String()
|
|
98
|
-
}),
|
|
99
|
-
response: t.Object({
|
|
100
|
-
success: t.Boolean(),
|
|
101
|
-
user: t.Optional(t.Object({
|
|
102
|
-
id: t.Number(),
|
|
103
|
-
name: t.String(),
|
|
104
|
-
email: t.String(),
|
|
105
|
-
createdAt: t.Date()
|
|
106
|
-
})),
|
|
107
|
-
message: t.Optional(t.String())
|
|
108
|
-
}),
|
|
109
200
|
detail: {
|
|
110
|
-
tags: ['Users'],
|
|
111
201
|
summary: 'Delete User',
|
|
112
|
-
description: '
|
|
202
|
+
description: 'Deletes a user by their ID',
|
|
203
|
+
tags: ['Users', 'CRUD']
|
|
204
|
+
},
|
|
205
|
+
params: t.Object({
|
|
206
|
+
id: t.String({ description: 'User ID to delete' })
|
|
207
|
+
}),
|
|
208
|
+
response: {
|
|
209
|
+
200: DeleteUserResponseSchema,
|
|
210
|
+
400: t.Object({
|
|
211
|
+
success: t.Literal(false),
|
|
212
|
+
message: t.String()
|
|
213
|
+
})
|
|
113
214
|
}
|
|
114
|
-
})
|
|
215
|
+
})
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* @deprecated Use the configuration from the root fluxstack.config.ts instead
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { getConfigSync, createLegacyConfig } from '
|
|
8
|
-
import type { FluxStackConfig } from '
|
|
7
|
+
import { getConfigSync, createLegacyConfig } from '@/core/config'
|
|
8
|
+
import type { FluxStackConfig } from '@/core/config'
|
|
9
9
|
|
|
10
10
|
// Load the new configuration
|
|
11
11
|
const newConfig = getConfigSync()
|
package/config/plugins.config.ts
CHANGED
|
@@ -28,7 +28,8 @@ const pluginsConfigSchema = {
|
|
|
28
28
|
// Logger plugin (handled by logger.config.ts)
|
|
29
29
|
loggerEnabled: config.boolean('LOGGER_PLUGIN_ENABLED', true),
|
|
30
30
|
|
|
31
|
-
// Swagger plugin
|
|
31
|
+
// Swagger plugin
|
|
32
|
+
swaggerEnabled: config.boolean('SWAGGER_ENABLED', true),
|
|
32
33
|
swaggerTitle: config.string('SWAGGER_TITLE', 'FluxStack API'),
|
|
33
34
|
swaggerVersion: config.string('SWAGGER_VERSION', FLUXSTACK_VERSION),
|
|
34
35
|
swaggerDescription: config.string(
|
|
@@ -37,6 +38,26 @@ const pluginsConfigSchema = {
|
|
|
37
38
|
),
|
|
38
39
|
swaggerPath: config.string('SWAGGER_PATH', '/swagger'),
|
|
39
40
|
|
|
41
|
+
// Swagger advanced options
|
|
42
|
+
swaggerExcludePaths: config.array('SWAGGER_EXCLUDE_PATHS', []),
|
|
43
|
+
|
|
44
|
+
// Swagger servers (comma-separated list of URLs)
|
|
45
|
+
// Format: "url1|description1,url2|description2"
|
|
46
|
+
// Example: "https://api.prod.com|Production,https://api.staging.com|Staging"
|
|
47
|
+
swaggerServers: config.string('SWAGGER_SERVERS', ''),
|
|
48
|
+
|
|
49
|
+
// Swagger UI options
|
|
50
|
+
swaggerPersistAuthorization: config.boolean('SWAGGER_PERSIST_AUTH', true),
|
|
51
|
+
swaggerDisplayRequestDuration: config.boolean('SWAGGER_DISPLAY_DURATION', true),
|
|
52
|
+
swaggerEnableFilter: config.boolean('SWAGGER_ENABLE_FILTER', true),
|
|
53
|
+
swaggerShowExtensions: config.boolean('SWAGGER_SHOW_EXTENSIONS', true),
|
|
54
|
+
swaggerTryItOutEnabled: config.boolean('SWAGGER_TRY_IT_OUT', true),
|
|
55
|
+
|
|
56
|
+
// Swagger authentication (Basic Auth)
|
|
57
|
+
swaggerAuthEnabled: config.boolean('SWAGGER_AUTH_ENABLED', false),
|
|
58
|
+
swaggerAuthUsername: config.string('SWAGGER_AUTH_USERNAME', 'admin'),
|
|
59
|
+
swaggerAuthPassword: config.string('SWAGGER_AUTH_PASSWORD', ''),
|
|
60
|
+
|
|
40
61
|
// Static files plugin
|
|
41
62
|
staticFilesEnabled: config.boolean('STATIC_FILES_ENABLED', true),
|
|
42
63
|
staticPublicDir: config.string('STATIC_PUBLIC_DIR', 'public'),
|
package/core/build/bundler.ts
CHANGED
|
@@ -79,34 +79,18 @@ export class Bundler {
|
|
|
79
79
|
let liveComponentsGenerator: any = null
|
|
80
80
|
|
|
81
81
|
try {
|
|
82
|
-
//
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
const discoveredComponents = await liveComponentsGenerator.preBuild()
|
|
86
|
-
|
|
87
|
-
// ๐ PRE-BUILD: Auto-generate FluxStack Plugins registration
|
|
88
|
-
const pluginsGeneratorModule = await import('./flux-plugins-generator')
|
|
89
|
-
const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
|
|
90
|
-
const discoveredPlugins = await fluxPluginsGenerator.preBuild()
|
|
91
|
-
|
|
82
|
+
// Run pre-build steps
|
|
83
|
+
liveComponentsGenerator = await this.runPreBuildSteps()
|
|
84
|
+
|
|
92
85
|
// Ensure output directory exists
|
|
93
|
-
|
|
94
|
-
mkdirSync(this.config.outDir, { recursive: true })
|
|
95
|
-
}
|
|
86
|
+
this.ensureOutputDirectory()
|
|
96
87
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
"tailwindcss",
|
|
100
|
-
"lightningcss",
|
|
101
|
-
"vite",
|
|
102
|
-
"@vitejs/plugin-react",
|
|
103
|
-
...(this.config.external || []),
|
|
104
|
-
...(options.external || [])
|
|
105
|
-
]
|
|
88
|
+
// Get external dependencies
|
|
89
|
+
const external = this.getExternalDependencies(options)
|
|
106
90
|
|
|
107
91
|
const buildArgs = [
|
|
108
|
-
"bun", "build",
|
|
109
|
-
entryPoint,
|
|
92
|
+
"bun", "build",
|
|
93
|
+
entryPoint,
|
|
110
94
|
"--outdir", this.config.outDir,
|
|
111
95
|
"--target", this.config.target,
|
|
112
96
|
...external.flatMap(ext => ["--external", ext])
|
|
@@ -132,20 +116,12 @@ export class Bundler {
|
|
|
132
116
|
const exitCode = await buildProcess.exited
|
|
133
117
|
const duration = Date.now() - startTime
|
|
134
118
|
|
|
135
|
-
// ๐งน POST-BUILD: Handle auto-generated registration file
|
|
136
|
-
// (liveComponentsGenerator already available from above)
|
|
137
|
-
|
|
138
119
|
if (exitCode === 0) {
|
|
139
120
|
buildLogger.success(`Server bundle completed in ${buildLogger.formatDuration(duration)}`)
|
|
140
|
-
|
|
141
|
-
//
|
|
142
|
-
await
|
|
143
|
-
|
|
144
|
-
// Cleanup plugins registry
|
|
145
|
-
const pluginsGeneratorModule = await import('./flux-plugins-generator')
|
|
146
|
-
const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
|
|
147
|
-
await fluxPluginsGenerator.postBuild(false)
|
|
148
|
-
|
|
121
|
+
|
|
122
|
+
// Run post-build cleanup
|
|
123
|
+
await this.runPostBuildCleanup(liveComponentsGenerator)
|
|
124
|
+
|
|
149
125
|
return {
|
|
150
126
|
success: true,
|
|
151
127
|
duration,
|
|
@@ -154,15 +130,10 @@ export class Bundler {
|
|
|
154
130
|
}
|
|
155
131
|
} else {
|
|
156
132
|
buildLogger.error("Server bundle failed")
|
|
157
|
-
|
|
158
|
-
//
|
|
159
|
-
await
|
|
160
|
-
|
|
161
|
-
// Restore plugins registry
|
|
162
|
-
const pluginsGeneratorModule = await import('./flux-plugins-generator')
|
|
163
|
-
const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
|
|
164
|
-
await fluxPluginsGenerator.postBuild(false)
|
|
165
|
-
|
|
133
|
+
|
|
134
|
+
// Run post-build cleanup
|
|
135
|
+
await this.runPostBuildCleanup(liveComponentsGenerator)
|
|
136
|
+
|
|
166
137
|
const stderr = await new Response(buildProcess.stderr).text()
|
|
167
138
|
return {
|
|
168
139
|
success: false,
|
|
@@ -172,21 +143,139 @@ export class Bundler {
|
|
|
172
143
|
}
|
|
173
144
|
} catch (error) {
|
|
174
145
|
const duration = Date.now() - startTime
|
|
175
|
-
|
|
146
|
+
|
|
147
|
+
// ๐งน CLEANUP: Restore original files on error
|
|
148
|
+
try {
|
|
149
|
+
await this.runPostBuildCleanup(liveComponentsGenerator)
|
|
150
|
+
} catch (cleanupError) {
|
|
151
|
+
buildLogger.warn(`Failed to cleanup generated files: ${cleanupError}`)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
duration,
|
|
157
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async compileToExecutable(entryPoint: string, outputName: string = "app", options: BundleOptions = {}): Promise<BundleResult> {
|
|
163
|
+
buildLogger.section('Executable Build', '๐ฆ')
|
|
164
|
+
|
|
165
|
+
const startTime = Date.now()
|
|
166
|
+
let liveComponentsGenerator: any = null
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
// Run pre-build steps
|
|
170
|
+
liveComponentsGenerator = await this.runPreBuildSteps()
|
|
171
|
+
|
|
172
|
+
// Ensure output directory exists
|
|
173
|
+
this.ensureOutputDirectory()
|
|
174
|
+
|
|
175
|
+
const outputPath = join(this.config.outDir, outputName)
|
|
176
|
+
|
|
177
|
+
// Get external dependencies
|
|
178
|
+
const external = this.getExternalDependencies(options)
|
|
179
|
+
|
|
180
|
+
const buildArgs = [
|
|
181
|
+
"bun", "build",
|
|
182
|
+
entryPoint,
|
|
183
|
+
"--compile",
|
|
184
|
+
"--outfile", outputPath,
|
|
185
|
+
"--target", this.config.target,
|
|
186
|
+
...external.flatMap(ext => ["--external", ext])
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
if (this.config.sourceMaps) {
|
|
190
|
+
buildArgs.push("--sourcemap")
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (this.config.minify) {
|
|
194
|
+
buildArgs.push("--minify")
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Add Windows-specific options if provided
|
|
198
|
+
if (options.executable?.windows) {
|
|
199
|
+
const winOpts = options.executable.windows
|
|
200
|
+
if (winOpts.hideConsole) {
|
|
201
|
+
buildArgs.push("--windows-hide-console")
|
|
202
|
+
}
|
|
203
|
+
if (winOpts.icon) {
|
|
204
|
+
buildArgs.push("--windows-icon", winOpts.icon)
|
|
205
|
+
}
|
|
206
|
+
if (winOpts.title) {
|
|
207
|
+
buildArgs.push("--windows-title", winOpts.title)
|
|
208
|
+
}
|
|
209
|
+
if (winOpts.publisher) {
|
|
210
|
+
buildArgs.push("--windows-publisher", winOpts.publisher)
|
|
211
|
+
}
|
|
212
|
+
if (winOpts.version) {
|
|
213
|
+
buildArgs.push("--windows-version", winOpts.version)
|
|
214
|
+
}
|
|
215
|
+
if (winOpts.description) {
|
|
216
|
+
buildArgs.push("--windows-description", winOpts.description)
|
|
217
|
+
}
|
|
218
|
+
if (winOpts.copyright) {
|
|
219
|
+
buildArgs.push("--windows-copyright", winOpts.copyright)
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Add custom build arguments if provided
|
|
224
|
+
if (options.executable?.customArgs) {
|
|
225
|
+
buildArgs.push(...options.executable.customArgs)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
buildLogger.step(`Compiling ${entryPoint} to ${outputPath}...`)
|
|
229
|
+
|
|
230
|
+
const buildProcess = spawn({
|
|
231
|
+
cmd: buildArgs,
|
|
232
|
+
stdout: "pipe",
|
|
233
|
+
stderr: "pipe",
|
|
234
|
+
env: {
|
|
235
|
+
...process.env,
|
|
236
|
+
NODE_ENV: 'production',
|
|
237
|
+
...options.env
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
const exitCode = await buildProcess.exited
|
|
242
|
+
const duration = Date.now() - startTime
|
|
243
|
+
|
|
244
|
+
if (exitCode === 0) {
|
|
245
|
+
buildLogger.success(`Executable compiled in ${buildLogger.formatDuration(duration)}`)
|
|
246
|
+
|
|
247
|
+
// Run post-build cleanup
|
|
248
|
+
await this.runPostBuildCleanup(liveComponentsGenerator)
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
success: true,
|
|
252
|
+
duration,
|
|
253
|
+
outputPath,
|
|
254
|
+
entryPoint: outputPath
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
buildLogger.error("Executable compilation failed")
|
|
258
|
+
|
|
259
|
+
// Run post-build cleanup
|
|
260
|
+
await this.runPostBuildCleanup(liveComponentsGenerator)
|
|
261
|
+
|
|
262
|
+
const stderr = await new Response(buildProcess.stderr).text()
|
|
263
|
+
return {
|
|
264
|
+
success: false,
|
|
265
|
+
duration,
|
|
266
|
+
error: stderr || "Executable compilation failed"
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
const duration = Date.now() - startTime
|
|
271
|
+
|
|
176
272
|
// ๐งน CLEANUP: Restore original files on error
|
|
177
273
|
try {
|
|
178
|
-
|
|
179
|
-
await liveComponentsGenerator.postBuild(false)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
// Cleanup plugins registry
|
|
183
|
-
const pluginsGeneratorModule = await import('./flux-plugins-generator')
|
|
184
|
-
const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
|
|
185
|
-
await fluxPluginsGenerator.postBuild(false)
|
|
274
|
+
await this.runPostBuildCleanup(liveComponentsGenerator)
|
|
186
275
|
} catch (cleanupError) {
|
|
187
276
|
buildLogger.warn(`Failed to cleanup generated files: ${cleanupError}`)
|
|
188
277
|
}
|
|
189
|
-
|
|
278
|
+
|
|
190
279
|
return {
|
|
191
280
|
success: false,
|
|
192
281
|
duration,
|
|
@@ -195,6 +284,61 @@ export class Bundler {
|
|
|
195
284
|
}
|
|
196
285
|
}
|
|
197
286
|
|
|
287
|
+
/**
|
|
288
|
+
* Get list of external dependencies that should not be bundled
|
|
289
|
+
*/
|
|
290
|
+
private getExternalDependencies(options: BundleOptions = {}): string[] {
|
|
291
|
+
return [
|
|
292
|
+
"@tailwindcss/vite",
|
|
293
|
+
"tailwindcss",
|
|
294
|
+
"lightningcss",
|
|
295
|
+
"vite",
|
|
296
|
+
"@vitejs/plugin-react",
|
|
297
|
+
"rollup",
|
|
298
|
+
...(this.config.external || []),
|
|
299
|
+
...(options.external || [])
|
|
300
|
+
]
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Ensure output directory exists
|
|
305
|
+
*/
|
|
306
|
+
private ensureOutputDirectory(): void {
|
|
307
|
+
if (!existsSync(this.config.outDir)) {
|
|
308
|
+
mkdirSync(this.config.outDir, { recursive: true })
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Run pre-build steps (Live Components and Plugins generation)
|
|
314
|
+
*/
|
|
315
|
+
private async runPreBuildSteps(): Promise<any> {
|
|
316
|
+
// ๐ PRE-BUILD: Auto-generate Live Components registration
|
|
317
|
+
const generatorModule = await import('./live-components-generator')
|
|
318
|
+
const liveComponentsGenerator = generatorModule.liveComponentsGenerator
|
|
319
|
+
await liveComponentsGenerator.preBuild()
|
|
320
|
+
|
|
321
|
+
// ๐ PRE-BUILD: Auto-generate FluxStack Plugins registration
|
|
322
|
+
const pluginsGeneratorModule = await import('./flux-plugins-generator')
|
|
323
|
+
const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
|
|
324
|
+
await fluxPluginsGenerator.preBuild()
|
|
325
|
+
|
|
326
|
+
return liveComponentsGenerator
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Run post-build cleanup
|
|
331
|
+
*/
|
|
332
|
+
private async runPostBuildCleanup(liveComponentsGenerator: any): Promise<void> {
|
|
333
|
+
if (liveComponentsGenerator) {
|
|
334
|
+
await liveComponentsGenerator.postBuild(false)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const pluginsGeneratorModule = await import('./flux-plugins-generator')
|
|
338
|
+
const fluxPluginsGenerator = pluginsGeneratorModule.fluxPluginsGenerator
|
|
339
|
+
await fluxPluginsGenerator.postBuild(false)
|
|
340
|
+
}
|
|
341
|
+
|
|
198
342
|
private async getClientAssets(): Promise<string[]> {
|
|
199
343
|
// This would analyze the build output to get asset information
|
|
200
344
|
// For now, return empty array - can be enhanced later
|
|
@@ -95,12 +95,12 @@ export class FluxPluginsGenerator {
|
|
|
95
95
|
private findPluginEntryFile(pluginDir: string): string | null {
|
|
96
96
|
const possibleFiles = [
|
|
97
97
|
'index.ts',
|
|
98
|
-
'index
|
|
98
|
+
'index',
|
|
99
99
|
'plugin.ts',
|
|
100
|
-
'plugin
|
|
100
|
+
'plugin',
|
|
101
101
|
'src/index.ts',
|
|
102
|
-
'src/index
|
|
103
|
-
'dist/index
|
|
102
|
+
'src/index',
|
|
103
|
+
'dist/index'
|
|
104
104
|
]
|
|
105
105
|
|
|
106
106
|
for (const file of possibleFiles) {
|
|
@@ -130,7 +130,7 @@ export class FluxPluginsGenerator {
|
|
|
130
130
|
const imports = plugins
|
|
131
131
|
.map(plugin => {
|
|
132
132
|
const importName = this.getImportName(plugin.pluginName)
|
|
133
|
-
const importPath = plugin.entryFile === 'index.ts' || plugin.entryFile === 'index
|
|
133
|
+
const importPath = plugin.entryFile === 'index.ts' || plugin.entryFile === 'index'
|
|
134
134
|
? plugin.relativePath
|
|
135
135
|
: `${plugin.relativePath}/${plugin.entryFile.replace(/\.(ts|js)$/, '')}`
|
|
136
136
|
return `import ${importName} from "${importPath}"`
|
package/core/build/index.ts
CHANGED
|
@@ -47,6 +47,10 @@ export class FluxStackBuilder {
|
|
|
47
47
|
return await this.bundler.bundleServer("app/server/index.ts")
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
async buildExecutable(outputName: string = "CLauncher", options?: import("../types/build").BundleOptions) {
|
|
51
|
+
return await this.bundler.compileToExecutable("app/server/index.ts", outputName, options)
|
|
52
|
+
}
|
|
53
|
+
|
|
50
54
|
async createDockerFiles() {
|
|
51
55
|
buildLogger.section('Docker Configuration', '๐ณ')
|
|
52
56
|
|