create-fluxstack 1.7.4 → 1.8.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.
Files changed (45) hide show
  1. package/.dockerignore +82 -0
  2. package/Dockerfile +70 -0
  3. package/app/server/app.ts +20 -5
  4. package/app/server/backend-only.ts +15 -12
  5. package/app/server/index.ts +83 -96
  6. package/app/server/live/FluxStackConfig.ts +5 -5
  7. package/app/server/routes/env-test.ts +59 -0
  8. package/config/app.config.ts +2 -54
  9. package/config/client.config.ts +95 -0
  10. package/config/index.ts +57 -22
  11. package/config/monitoring.config.ts +114 -0
  12. package/config/plugins.config.ts +59 -0
  13. package/config/runtime.config.ts +0 -17
  14. package/config/server.config.ts +50 -30
  15. package/core/build/bundler.ts +17 -16
  16. package/core/build/flux-plugins-generator.ts +29 -18
  17. package/core/build/index.ts +72 -65
  18. package/core/build/live-components-generator.ts +29 -18
  19. package/core/build/optimizer.ts +37 -17
  20. package/core/cli/index.ts +6 -2
  21. package/core/config/env.ts +4 -0
  22. package/core/config/runtime-config.ts +10 -8
  23. package/core/config/schema.ts +24 -2
  24. package/core/framework/server.ts +1 -0
  25. package/core/index.ts +31 -23
  26. package/core/plugins/built-in/monitoring/index.ts +2 -0
  27. package/core/plugins/built-in/static/index.ts +73 -244
  28. package/core/plugins/built-in/swagger/index.ts +2 -0
  29. package/core/plugins/built-in/vite/index.ts +377 -374
  30. package/core/plugins/config.ts +2 -0
  31. package/core/plugins/discovery.ts +2 -0
  32. package/core/plugins/executor.ts +2 -0
  33. package/core/plugins/index.ts +2 -2
  34. package/core/plugins/registry.ts +22 -18
  35. package/core/server/backend-entry.ts +51 -0
  36. package/core/types/plugin.ts +6 -0
  37. package/core/utils/build-logger.ts +324 -0
  38. package/core/utils/config-schema.ts +2 -6
  39. package/core/utils/helpers.ts +14 -9
  40. package/core/utils/regenerate-files.ts +69 -0
  41. package/core/utils/version.ts +1 -1
  42. package/fluxstack.config.ts +138 -252
  43. package/package.json +2 -17
  44. package/vitest.config.ts +8 -26
  45. package/config/build.config.ts +0 -24
@@ -7,6 +7,8 @@ import type { FluxStack, PluginConfigSchema, PluginValidationResult } from "./ty
7
7
  import type { FluxStackConfig } from "../config/schema"
8
8
  import type { Logger } from "../utils/logger/index"
9
9
 
10
+ type Plugin = FluxStack.Plugin
11
+
10
12
  export interface PluginConfigManager {
11
13
  validatePluginConfig(plugin: Plugin, config: any): PluginValidationResult
12
14
  mergePluginConfig(plugin: Plugin, userConfig: any): any
@@ -9,6 +9,8 @@ import { readdir, readFile } from "fs/promises"
9
9
  import { join, resolve } from "path"
10
10
  import { existsSync } from "fs"
11
11
 
12
+ type Plugin = FluxStack.Plugin
13
+
12
14
  export interface PluginDiscoveryConfig {
13
15
  logger?: Logger
14
16
  baseDir?: string
@@ -28,6 +28,8 @@ export interface PluginExecutionStep {
28
28
  canExecuteInParallel: boolean
29
29
  }
30
30
 
31
+ type Plugin = FluxStack.Plugin
32
+
31
33
  export class PluginExecutor {
32
34
  private logger: Logger
33
35
 
@@ -27,7 +27,6 @@ export type {
27
27
  BuildContext
28
28
  } from './types'
29
29
 
30
- export type { FluxStack }
31
30
  export type Plugin = FluxStack.Plugin
32
31
 
33
32
  // Plugin registry
@@ -200,5 +199,6 @@ import type {
200
199
  PluginPriority,
201
200
  RequestContext,
202
201
  ResponseContext,
203
- ErrorContext
202
+ ErrorContext,
203
+ FluxStack
204
204
  } from './types'
@@ -291,7 +291,21 @@ export class PluginRegistry {
291
291
  }
292
292
  }
293
293
 
294
- // Try to import the plugin
294
+ // Install dependencies BEFORE importing the plugin
295
+ if (manifest && manifest.dependencies && Object.keys(manifest.dependencies).length > 0) {
296
+ try {
297
+ const resolution = await this.dependencyManager.resolvePluginDependencies(pluginPath)
298
+ if (resolution.dependencies.length > 0) {
299
+ // Install dependencies directly in the plugin directory
300
+ await this.dependencyManager.installPluginDependenciesLocally(pluginPath, resolution.dependencies)
301
+ this.logger?.debug(`Dependencies installed for plugin at: ${pluginPath}`)
302
+ }
303
+ } catch (error) {
304
+ this.logger?.warn(`Failed to install dependencies for plugin at '${pluginPath}'`, { error })
305
+ }
306
+ }
307
+
308
+ // Try to import the plugin (after dependencies are installed)
295
309
  const pluginModule = await import(resolve(pluginPath))
296
310
  const plugin: FluxStackPlugin = pluginModule.default || pluginModule
297
311
 
@@ -434,38 +448,28 @@ export class PluginRegistry {
434
448
  * Resolver dependências de todos os plugins descobertos
435
449
  */
436
450
  private async resolveDependencies(results: PluginLoadResult[]): Promise<void> {
437
- const resolutions = []
438
-
439
- // Resolver dependências para cada plugin carregado com sucesso
451
+ // Dependencies are now installed during plugin loading in loadPlugin()
452
+ // This method is kept for compatibility but no longer performs installation
453
+
454
+ // Only check for dependency conflicts on successfully loaded plugins
440
455
  for (const result of results) {
441
456
  if (result.success && result.plugin) {
442
457
  try {
443
- // Tentar encontrar o diretório do plugin
444
458
  const pluginDir = this.findPluginDirectory(result.plugin.name)
445
459
  if (pluginDir) {
446
460
  const resolution = await this.dependencyManager.resolvePluginDependencies(pluginDir)
447
- resolutions.push(resolution)
448
-
461
+
449
462
  if (!resolution.resolved) {
450
- this.logger?.warn(`Plugin '${result.plugin.name}' tem conflitos de dependências`, {
463
+ this.logger?.warn(`Plugin '${result.plugin.name}' has dependency conflicts`, {
451
464
  conflicts: resolution.conflicts.length
452
465
  })
453
466
  }
454
467
  }
455
468
  } catch (error) {
456
- this.logger?.warn(`Erro ao resolver dependências do plugin '${result.plugin.name}'`, { error })
469
+ this.logger?.warn(`Failed to check dependencies for plugin '${result.plugin.name}'`, { error })
457
470
  }
458
471
  }
459
472
  }
460
-
461
- // Instalar dependências se houver resoluções
462
- if (resolutions.length > 0) {
463
- try {
464
- await this.dependencyManager.installPluginDependencies(resolutions)
465
- } catch (error) {
466
- this.logger?.error('Erro ao instalar dependências de plugins', { error })
467
- }
468
- }
469
473
  }
470
474
 
471
475
  /**
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Backend Entry Point - Core Framework
3
+ *
4
+ * This file contains the protected logic for running backend standalone mode.
5
+ * DO NOT modify this file directly - it's part of the FluxStack framework core.
6
+ *
7
+ * For customization, use app/server/backend-only.ts
8
+ */
9
+
10
+ import type { Elysia } from "elysia"
11
+ import { startBackendOnly } from "./standalone"
12
+
13
+ export interface BackendEntryConfig {
14
+ port: number
15
+ apiPrefix?: string
16
+ host?: string
17
+ }
18
+
19
+ /**
20
+ * Start backend in standalone mode
21
+ *
22
+ * @param apiRoutes - Elysia routes from app/server/routes
23
+ * @param config - Backend configuration
24
+ */
25
+ export function startBackend(
26
+ apiRoutes: Elysia,
27
+ config: BackendEntryConfig
28
+ ) {
29
+ const { port, apiPrefix = '/api', host = 'localhost' } = config
30
+
31
+ console.log(`🚀 Backend standalone: ${host}:${port}`)
32
+ console.log(`📡 API Prefix: ${apiPrefix}`)
33
+ console.log()
34
+
35
+ // Start backend using the standalone utility
36
+ startBackendOnly(apiRoutes, { port, apiPrefix })
37
+ }
38
+
39
+ /**
40
+ * Create backend entry config from declarative config
41
+ * Helper to make it easy to use with the config system
42
+ */
43
+ export function createBackendConfig(
44
+ serverConfig: { server: { backendPort: number; apiPrefix: string; host: string } }
45
+ ): BackendEntryConfig {
46
+ return {
47
+ port: serverConfig.server.backendPort,
48
+ apiPrefix: serverConfig.server.apiPrefix,
49
+ host: serverConfig.server.host
50
+ }
51
+ }
@@ -3,6 +3,9 @@
3
3
  * Comprehensive type definitions for the plugin system
4
4
  */
5
5
 
6
+ // Import namespace for type alias
7
+ import type { FluxStack } from "../plugins/types"
8
+
6
9
  // Re-export plugin types
7
10
  export type {
8
11
  FluxStack,
@@ -13,6 +16,9 @@ export type {
13
16
  ErrorContext
14
17
  } from "../plugins/types"
15
18
 
19
+ // Export Plugin as a standalone type for convenience
20
+ export type Plugin = FluxStack.Plugin
21
+
16
22
  // Additional plugin-related types
17
23
  export interface PluginManifest {
18
24
  name: string
@@ -0,0 +1,324 @@
1
+ /**
2
+ * FluxStack Build Logger - Beautiful terminal output for build process
3
+ * Provides formatted tables, boxes, and colored output
4
+ */
5
+
6
+ // ANSI Color codes
7
+ const colors = {
8
+ reset: '\x1b[0m',
9
+ bright: '\x1b[1m',
10
+ dim: '\x1b[2m',
11
+
12
+ // Text colors
13
+ cyan: '\x1b[36m',
14
+ blue: '\x1b[34m',
15
+ green: '\x1b[32m',
16
+ yellow: '\x1b[33m',
17
+ red: '\x1b[31m',
18
+ magenta: '\x1b[35m',
19
+ white: '\x1b[37m',
20
+ gray: '\x1b[90m',
21
+
22
+ // Background colors
23
+ bgCyan: '\x1b[46m',
24
+ bgBlue: '\x1b[44m',
25
+ bgGreen: '\x1b[42m',
26
+ bgYellow: '\x1b[43m',
27
+ bgRed: '\x1b[41m',
28
+ }
29
+
30
+ // Box drawing characters
31
+ const box = {
32
+ topLeft: '╭',
33
+ topRight: '╮',
34
+ bottomLeft: '╰',
35
+ bottomRight: '╯',
36
+ horizontal: '─',
37
+ vertical: '│',
38
+ verticalRight: '├',
39
+ verticalLeft: '┤',
40
+ horizontalDown: '┬',
41
+ horizontalUp: '┴',
42
+ cross: '┼',
43
+ }
44
+
45
+ export interface TableColumn {
46
+ header: string
47
+ key: string
48
+ width?: number
49
+ align?: 'left' | 'right' | 'center'
50
+ color?: keyof typeof colors
51
+ }
52
+
53
+ export interface TableRow {
54
+ [key: string]: string | number
55
+ }
56
+
57
+ export class BuildLogger {
58
+ private indent = ''
59
+ private startTime = Date.now()
60
+
61
+ /**
62
+ * Print a beautiful header banner
63
+ */
64
+ header(title: string) {
65
+ const width = 60
66
+ const padding = Math.floor((width - title.length - 2) / 2)
67
+ const paddingRight = width - title.length - 2 - padding
68
+
69
+ console.log()
70
+ console.log(colors.cyan + colors.bright + box.topLeft + box.horizontal.repeat(width) + box.topRight + colors.reset)
71
+ console.log(colors.cyan + box.vertical + ' '.repeat(padding) + colors.bright + colors.white + title + colors.cyan + ' '.repeat(paddingRight) + box.vertical + colors.reset)
72
+ console.log(colors.cyan + box.bottomLeft + box.horizontal.repeat(width) + box.bottomRight + colors.reset)
73
+ console.log()
74
+ }
75
+
76
+ /**
77
+ * Print a section header
78
+ */
79
+ section(title: string, icon: string = '📦') {
80
+ console.log()
81
+ console.log(colors.bright + colors.blue + `${icon} ${title}` + colors.reset)
82
+ console.log(colors.dim + colors.gray + box.horizontal.repeat(50) + colors.reset)
83
+ }
84
+
85
+ /**
86
+ * Print a success message
87
+ */
88
+ success(message: string) {
89
+ console.log(colors.green + '✓ ' + colors.reset + message)
90
+ }
91
+
92
+ /**
93
+ * Print an error message
94
+ */
95
+ error(message: string) {
96
+ console.log(colors.red + '✗ ' + colors.reset + message)
97
+ }
98
+
99
+ /**
100
+ * Print a warning message
101
+ */
102
+ warn(message: string) {
103
+ console.log(colors.yellow + '⚠ ' + colors.reset + message)
104
+ }
105
+
106
+ /**
107
+ * Print an info message
108
+ */
109
+ info(message: string, icon: string = '→') {
110
+ console.log(colors.cyan + icon + ' ' + colors.reset + message)
111
+ }
112
+
113
+ /**
114
+ * Print a step message
115
+ */
116
+ step(message: string, icon: string = '▸') {
117
+ console.log(colors.dim + colors.gray + icon + ' ' + colors.reset + message)
118
+ }
119
+
120
+ /**
121
+ * Print a table
122
+ */
123
+ table(columns: TableColumn[], rows: TableRow[]) {
124
+ if (rows.length === 0) {
125
+ this.warn('No data to display')
126
+ return
127
+ }
128
+
129
+ // Calculate column widths
130
+ const widths = columns.map(col => {
131
+ if (col.width) return col.width
132
+ const maxContentWidth = Math.max(
133
+ col.header.length,
134
+ ...rows.map(row => String(row[col.key] || '').length)
135
+ )
136
+ return Math.min(maxContentWidth, 40) // Max 40 chars per column
137
+ })
138
+
139
+ const totalWidth = widths.reduce((sum, w) => sum + w, 0) + (columns.length - 1) * 3 + 4
140
+
141
+ // Print top border
142
+ console.log(
143
+ colors.gray + box.topLeft +
144
+ widths.map((w, i) =>
145
+ box.horizontal.repeat(w + 2) + (i < widths.length - 1 ? box.horizontalDown : '')
146
+ ).join('') +
147
+ box.topRight + colors.reset
148
+ )
149
+
150
+ // Print header
151
+ const headerRow = columns.map((col, i) => {
152
+ const content = this.padContent(col.header, widths[i], 'center')
153
+ return colors.bright + colors.white + content + colors.reset
154
+ }).join(colors.gray + ' │ ' + colors.reset)
155
+
156
+ console.log(colors.gray + box.vertical + ' ' + colors.reset + headerRow + colors.gray + ' ' + box.vertical + colors.reset)
157
+
158
+ // Print header separator
159
+ console.log(
160
+ colors.gray + box.verticalRight +
161
+ widths.map((w, i) =>
162
+ box.horizontal.repeat(w + 2) + (i < widths.length - 1 ? box.cross : '')
163
+ ).join('') +
164
+ box.verticalLeft + colors.reset
165
+ )
166
+
167
+ // Print rows
168
+ rows.forEach((row, rowIndex) => {
169
+ const rowContent = columns.map((col, i) => {
170
+ const value = String(row[col.key] || '')
171
+ const content = this.padContent(value, widths[i], col.align || 'left')
172
+ const color = col.color ? colors[col.color] : ''
173
+ return color + content + colors.reset
174
+ }).join(colors.gray + ' │ ' + colors.reset)
175
+
176
+ console.log(colors.gray + box.vertical + ' ' + colors.reset + rowContent + colors.gray + ' ' + box.vertical + colors.reset)
177
+ })
178
+
179
+ // Print bottom border
180
+ console.log(
181
+ colors.gray + box.bottomLeft +
182
+ widths.map((w, i) =>
183
+ box.horizontal.repeat(w + 2) + (i < widths.length - 1 ? box.horizontalUp : '')
184
+ ).join('') +
185
+ box.bottomRight + colors.reset
186
+ )
187
+ }
188
+
189
+ /**
190
+ * Print a simple info box
191
+ */
192
+ box(title: string, items: Array<{ label: string; value: string | number; color?: keyof typeof colors }>) {
193
+ const maxLabelWidth = Math.max(...items.map(i => i.label.length))
194
+ const maxValueWidth = Math.max(...items.map(i => String(i.value).length))
195
+ const contentWidth = maxLabelWidth + maxValueWidth + 3
196
+ const boxWidth = Math.max(contentWidth, title.length) + 4
197
+
198
+ // Top border with title
199
+ console.log()
200
+ console.log(colors.cyan + box.topLeft + box.horizontal.repeat(2) + colors.bright + colors.white + title + colors.cyan + box.horizontal.repeat(boxWidth - title.length - 2) + box.topRight + colors.reset)
201
+
202
+ // Content
203
+ items.forEach(item => {
204
+ const label = item.label.padEnd(maxLabelWidth)
205
+ const value = String(item.value)
206
+ const valueColor = item.color ? colors[item.color] : colors.white
207
+ console.log(
208
+ colors.cyan + box.vertical + ' ' + colors.reset +
209
+ colors.gray + label + colors.reset +
210
+ colors.dim + ' : ' + colors.reset +
211
+ valueColor + colors.bright + value + colors.reset +
212
+ ' '.repeat(boxWidth - label.length - value.length - 3) +
213
+ colors.cyan + box.vertical + colors.reset
214
+ )
215
+ })
216
+
217
+ // Bottom border
218
+ console.log(colors.cyan + box.bottomLeft + box.horizontal.repeat(boxWidth) + box.bottomRight + colors.reset)
219
+ console.log()
220
+ }
221
+
222
+ /**
223
+ * Print a progress indicator
224
+ */
225
+ progress(current: number, total: number, label: string) {
226
+ const percentage = Math.round((current / total) * 100)
227
+ const barLength = 30
228
+ const filled = Math.round((percentage / 100) * barLength)
229
+ const empty = barLength - filled
230
+
231
+ const bar = colors.green + '█'.repeat(filled) + colors.gray + '░'.repeat(empty) + colors.reset
232
+ console.log(`${label} [${bar}] ${percentage}% (${current}/${total})`)
233
+ }
234
+
235
+ /**
236
+ * Start a timer
237
+ */
238
+ startTimer(label?: string) {
239
+ this.startTime = Date.now()
240
+ if (label) {
241
+ this.info(label, '⏱')
242
+ }
243
+ }
244
+
245
+ /**
246
+ * End timer and print elapsed time
247
+ */
248
+ endTimer(label: string = 'Completed') {
249
+ const elapsed = Date.now() - this.startTime
250
+ const seconds = (elapsed / 1000).toFixed(2)
251
+ this.success(`${label} in ${colors.bright}${seconds}s${colors.reset}`)
252
+ }
253
+
254
+ /**
255
+ * Print a summary box
256
+ */
257
+ summary(title: string, stats: Array<{ label: string; value: string | number; highlight?: boolean }>) {
258
+ console.log()
259
+ console.log(colors.bright + colors.green + '╔═══════════════════════════════════════════════════════════╗' + colors.reset)
260
+ console.log(colors.bright + colors.green + '║' + colors.reset + colors.bright + colors.white + ` ${title}`.padEnd(60) + colors.bright + colors.green + '║' + colors.reset)
261
+ console.log(colors.bright + colors.green + '╠═══════════════════════════════════════════════════════════╣' + colors.reset)
262
+
263
+ stats.forEach(stat => {
264
+ const label = ` ${stat.label}:`
265
+ const value = String(stat.value)
266
+ const valueColor = stat.highlight ? colors.yellow + colors.bright : colors.white
267
+ const padding = 60 - label.length - value.length - 1
268
+ console.log(
269
+ colors.bright + colors.green + '║' + colors.reset +
270
+ colors.cyan + label + colors.reset +
271
+ ' '.repeat(Math.max(padding, 1)) +
272
+ valueColor + value + colors.reset +
273
+ colors.bright + colors.green + ' ║' + colors.reset
274
+ )
275
+ })
276
+
277
+ console.log(colors.bright + colors.green + '╚═══════════════════════════════════════════════════════════╝' + colors.reset)
278
+ console.log()
279
+ }
280
+
281
+ /**
282
+ * Pad content based on alignment
283
+ */
284
+ private padContent(content: string, width: number, align: 'left' | 'right' | 'center' = 'left'): string {
285
+ if (content.length >= width) {
286
+ return content.slice(0, width)
287
+ }
288
+
289
+ const padding = width - content.length
290
+
291
+ switch (align) {
292
+ case 'right':
293
+ return ' '.repeat(padding) + content
294
+ case 'center':
295
+ const leftPad = Math.floor(padding / 2)
296
+ const rightPad = padding - leftPad
297
+ return ' '.repeat(leftPad) + content + ' '.repeat(rightPad)
298
+ default:
299
+ return content + ' '.repeat(padding)
300
+ }
301
+ }
302
+
303
+ /**
304
+ * Format file size
305
+ */
306
+ formatSize(bytes: number): string {
307
+ if (bytes === 0) return '0 B'
308
+ const k = 1024
309
+ const sizes = ['B', 'KB', 'MB', 'GB']
310
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
311
+ return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`
312
+ }
313
+
314
+ /**
315
+ * Format duration
316
+ */
317
+ formatDuration(ms: number): string {
318
+ if (ms < 1000) return `${ms}ms`
319
+ return `${(ms / 1000).toFixed(2)}s`
320
+ }
321
+ }
322
+
323
+ // Export singleton instance
324
+ export const buildLogger = new BuildLogger()
@@ -23,17 +23,13 @@
23
23
  * default: 3000,
24
24
  * validate: (value) => value > 0 && value < 65536
25
25
  * },
26
- * debug: {
27
- * type: 'boolean',
28
- * env: 'DEBUG',
29
- * default: false
30
- * }
26
+ * env: config.enum('NODE_ENV', ['development', 'production', 'test'] as const, 'development', true)
31
27
  * })
32
28
  *
33
29
  * // Access with full type safety
34
30
  * appConfig.name // string
35
31
  * appConfig.port // number
36
- * appConfig.debug // boolean
32
+ * appConfig.env // "development" | "production" | "test"
37
33
  * ```
38
34
  */
39
35
 
@@ -86,22 +86,27 @@ export const throttle = <T extends (...args: any[]) => any>(
86
86
  }
87
87
  }
88
88
 
89
+ /**
90
+ * Environment detection utilities
91
+ * Uses declarative config system instead of legacy env
92
+ */
93
+
89
94
  export const isProduction = (): boolean => {
90
- // Import here to avoid circular dependency
91
- const { env } = require('./env')
92
- return env.NODE_ENV === 'production'
95
+ // Lazy import to avoid circular dependency during module initialization
96
+ const { appConfig } = require('@/config/app.config')
97
+ return appConfig.env === 'production'
93
98
  }
94
99
 
95
100
  export const isDevelopment = (): boolean => {
96
- // Import here to avoid circular dependency
97
- const { env } = require('./env')
98
- return env.NODE_ENV === 'development' || !env.NODE_ENV
101
+ // Lazy import to avoid circular dependency during module initialization
102
+ const { appConfig } = require('@/config/app.config')
103
+ return appConfig.env === 'development'
99
104
  }
100
105
 
101
106
  export const isTest = (): boolean => {
102
- // Import here to avoid circular dependency
103
- const { env } = require('./env')
104
- return env.NODE_ENV === 'test'
107
+ // Lazy import to avoid circular dependency during module initialization
108
+ const { appConfig } = require('@/config/app.config')
109
+ return appConfig.env === 'test'
105
110
  }
106
111
 
107
112
  export const deepMerge = <T extends Record<string, any>>(target: T, source: Partial<T>): T => {
@@ -0,0 +1,69 @@
1
+ /**
2
+ * File Regeneration Utilities
3
+ *
4
+ * Provides functions to regenerate critical application files that might be
5
+ * accidentally deleted by developers.
6
+ */
7
+
8
+ import { join } from "path"
9
+ import { existsSync } from "fs"
10
+
11
+ const BACKEND_ONLY_TEMPLATE = `/**
12
+ * Backend Standalone Entry Point
13
+ *
14
+ * This is a minimal wrapper for starting the backend in standalone mode.
15
+ * The core logic is protected in @/core/server/backend-entry.ts
16
+ *
17
+ * You can customize the configuration here if needed.
18
+ */
19
+
20
+ import { startBackend, createBackendConfig } from "@/core/server/backend-entry"
21
+ import { apiRoutes } from "./routes"
22
+ import { serverConfig } from "@/config/server.config"
23
+
24
+ // Create backend configuration from declarative config
25
+ const backendConfig = createBackendConfig(serverConfig)
26
+
27
+ // Start backend in standalone mode
28
+ startBackend(apiRoutes, backendConfig)
29
+ `
30
+
31
+ /**
32
+ * Check if backend-only.ts exists, regenerate if missing
33
+ */
34
+ export async function ensureBackendEntry(projectRoot: string = process.cwd()): Promise<boolean> {
35
+ const backendOnlyPath = join(projectRoot, "app/server/backend-only.ts")
36
+
37
+ if (!existsSync(backendOnlyPath)) {
38
+ console.log("⚠️ backend-only.ts not found, regenerating...")
39
+
40
+ try {
41
+ await Bun.write(backendOnlyPath, BACKEND_ONLY_TEMPLATE)
42
+ console.log("✅ backend-only.ts regenerated successfully")
43
+ return true
44
+ } catch (error) {
45
+ console.error("❌ Failed to regenerate backend-only.ts:", error)
46
+ return false
47
+ }
48
+ }
49
+
50
+ return true
51
+ }
52
+
53
+ /**
54
+ * Regenerate backend-only.ts file
55
+ */
56
+ export async function regenerateBackendEntry(projectRoot: string = process.cwd()): Promise<boolean> {
57
+ const backendOnlyPath = join(projectRoot, "app/server/backend-only.ts")
58
+
59
+ console.log("🔄 Regenerating backend-only.ts...")
60
+
61
+ try {
62
+ await Bun.write(backendOnlyPath, BACKEND_ONLY_TEMPLATE)
63
+ console.log("✅ backend-only.ts regenerated successfully")
64
+ return true
65
+ } catch (error) {
66
+ console.error("❌ Failed to regenerate backend-only.ts:", error)
67
+ return false
68
+ }
69
+ }
@@ -3,4 +3,4 @@
3
3
  * Single source of truth for version number
4
4
  * Auto-synced with package.json
5
5
  */
6
- export const FLUXSTACK_VERSION = '1.7.4'
6
+ export const FLUXSTACK_VERSION = '1.8.1'