create-fluxstack 1.0.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.
Files changed (101) hide show
  1. package/.env +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +214 -0
  4. package/app/client/README.md +69 -0
  5. package/app/client/frontend-only.ts +12 -0
  6. package/app/client/index.html +13 -0
  7. package/app/client/public/vite.svg +1 -0
  8. package/app/client/src/App.css +883 -0
  9. package/app/client/src/App.tsx +669 -0
  10. package/app/client/src/assets/react.svg +1 -0
  11. package/app/client/src/components/TestPage.tsx +453 -0
  12. package/app/client/src/index.css +51 -0
  13. package/app/client/src/lib/eden-api.ts +110 -0
  14. package/app/client/src/main.tsx +10 -0
  15. package/app/client/src/vite-env.d.ts +1 -0
  16. package/app/client/tsconfig.app.json +43 -0
  17. package/app/client/tsconfig.json +7 -0
  18. package/app/client/tsconfig.node.json +25 -0
  19. package/app/server/app.ts +10 -0
  20. package/app/server/backend-only.ts +15 -0
  21. package/app/server/controllers/users.controller.ts +69 -0
  22. package/app/server/index.ts +104 -0
  23. package/app/server/routes/index.ts +25 -0
  24. package/app/server/routes/users.routes.ts +121 -0
  25. package/app/server/types/index.ts +1 -0
  26. package/app/shared/types/index.ts +18 -0
  27. package/bun.lock +1053 -0
  28. package/core/__tests__/integration.test.ts +227 -0
  29. package/core/build/index.ts +186 -0
  30. package/core/cli/command-registry.ts +334 -0
  31. package/core/cli/index.ts +394 -0
  32. package/core/cli/plugin-discovery.ts +200 -0
  33. package/core/client/standalone.ts +57 -0
  34. package/core/config/__tests__/config-loader.test.ts +591 -0
  35. package/core/config/__tests__/config-merger.test.ts +657 -0
  36. package/core/config/__tests__/env-converter.test.ts +372 -0
  37. package/core/config/__tests__/env-processor.test.ts +431 -0
  38. package/core/config/__tests__/env.test.ts +452 -0
  39. package/core/config/__tests__/integration.test.ts +418 -0
  40. package/core/config/__tests__/loader.test.ts +331 -0
  41. package/core/config/__tests__/schema.test.ts +129 -0
  42. package/core/config/__tests__/validator.test.ts +318 -0
  43. package/core/config/env-dynamic.ts +326 -0
  44. package/core/config/env.ts +597 -0
  45. package/core/config/index.ts +317 -0
  46. package/core/config/loader.ts +546 -0
  47. package/core/config/runtime-config.ts +322 -0
  48. package/core/config/schema.ts +694 -0
  49. package/core/config/validator.ts +540 -0
  50. package/core/framework/__tests__/server.test.ts +233 -0
  51. package/core/framework/client.ts +132 -0
  52. package/core/framework/index.ts +8 -0
  53. package/core/framework/server.ts +501 -0
  54. package/core/framework/types.ts +63 -0
  55. package/core/plugins/__tests__/built-in.test.ts.disabled +366 -0
  56. package/core/plugins/__tests__/manager.test.ts +398 -0
  57. package/core/plugins/__tests__/monitoring.test.ts +401 -0
  58. package/core/plugins/__tests__/registry.test.ts +335 -0
  59. package/core/plugins/built-in/index.ts +142 -0
  60. package/core/plugins/built-in/logger/index.ts +180 -0
  61. package/core/plugins/built-in/monitoring/README.md +193 -0
  62. package/core/plugins/built-in/monitoring/index.ts +912 -0
  63. package/core/plugins/built-in/static/index.ts +289 -0
  64. package/core/plugins/built-in/swagger/index.ts +229 -0
  65. package/core/plugins/built-in/vite/index.ts +316 -0
  66. package/core/plugins/config.ts +348 -0
  67. package/core/plugins/discovery.ts +350 -0
  68. package/core/plugins/executor.ts +351 -0
  69. package/core/plugins/index.ts +195 -0
  70. package/core/plugins/manager.ts +583 -0
  71. package/core/plugins/registry.ts +424 -0
  72. package/core/plugins/types.ts +254 -0
  73. package/core/server/framework.ts +123 -0
  74. package/core/server/index.ts +8 -0
  75. package/core/server/plugins/database.ts +182 -0
  76. package/core/server/plugins/logger.ts +47 -0
  77. package/core/server/plugins/swagger.ts +34 -0
  78. package/core/server/standalone.ts +91 -0
  79. package/core/templates/create-project.ts +455 -0
  80. package/core/types/api.ts +169 -0
  81. package/core/types/build.ts +174 -0
  82. package/core/types/config.ts +68 -0
  83. package/core/types/index.ts +127 -0
  84. package/core/types/plugin.ts +94 -0
  85. package/core/utils/__tests__/errors.test.ts +139 -0
  86. package/core/utils/__tests__/helpers.test.ts +297 -0
  87. package/core/utils/__tests__/logger.test.ts +141 -0
  88. package/core/utils/env-runtime-v2.ts +232 -0
  89. package/core/utils/env-runtime.ts +252 -0
  90. package/core/utils/errors/codes.ts +115 -0
  91. package/core/utils/errors/handlers.ts +63 -0
  92. package/core/utils/errors/index.ts +81 -0
  93. package/core/utils/helpers.ts +180 -0
  94. package/core/utils/index.ts +18 -0
  95. package/core/utils/logger/index.ts +161 -0
  96. package/core/utils/logger.ts +106 -0
  97. package/core/utils/monitoring/index.ts +212 -0
  98. package/create-fluxstack.ts +231 -0
  99. package/package.json +43 -0
  100. package/tsconfig.json +51 -0
  101. package/vite.config.ts +42 -0
@@ -0,0 +1,161 @@
1
+ /**
2
+ * FluxStack Logger
3
+ * Environment-aware logging system
4
+ */
5
+
6
+ // Environment info is handled via process.env directly
7
+
8
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error'
9
+
10
+ export interface Logger {
11
+ debug(message: string, meta?: any): void
12
+ info(message: string, meta?: any): void
13
+ warn(message: string, meta?: any): void
14
+ error(message: string, meta?: any): void
15
+
16
+ // Contextual logging
17
+ child(context: any): Logger
18
+
19
+ // Performance logging
20
+ time(label: string): void
21
+ timeEnd(label: string): void
22
+
23
+ // Request logging
24
+ request(method: string, path: string, status?: number, duration?: number): void
25
+ }
26
+
27
+ class FluxStackLogger implements Logger {
28
+ private static instance: FluxStackLogger | null = null
29
+ private logLevel: LogLevel
30
+ private context: any = {}
31
+ private timers: Map<string, number> = new Map()
32
+
33
+ private constructor(context?: any) {
34
+ // Default to 'info' level, can be overridden by config
35
+ this.logLevel = (process.env.LOG_LEVEL as LogLevel) || 'info'
36
+ this.context = context || {}
37
+ }
38
+
39
+ static getInstance(): FluxStackLogger {
40
+ if (FluxStackLogger.instance === null) {
41
+ FluxStackLogger.instance = new FluxStackLogger()
42
+ }
43
+ return FluxStackLogger.instance
44
+ }
45
+
46
+ private shouldLog(level: LogLevel): boolean {
47
+ const levels: Record<LogLevel, number> = {
48
+ debug: 0,
49
+ info: 1,
50
+ warn: 2,
51
+ error: 3
52
+ }
53
+
54
+ return levels[level] >= levels[this.logLevel]
55
+ }
56
+
57
+ private formatMessage(level: LogLevel, message: string, meta?: any): string {
58
+ const timestamp = new Date().toISOString()
59
+ const levelStr = level.toUpperCase().padEnd(5)
60
+
61
+ let formatted = `[${timestamp}] ${levelStr}`
62
+
63
+ // Add context if available
64
+ if (Object.keys(this.context).length > 0) {
65
+ const contextStr = Object.entries(this.context)
66
+ .map(([key, value]) => `${key}=${value}`)
67
+ .join(' ')
68
+ formatted += ` [${contextStr}]`
69
+ }
70
+
71
+ formatted += ` ${message}`
72
+
73
+ if (meta && typeof meta === 'object') {
74
+ formatted += ` ${JSON.stringify(meta)}`
75
+ } else if (meta !== undefined) {
76
+ formatted += ` ${meta}`
77
+ }
78
+
79
+ return formatted
80
+ }
81
+
82
+ debug(message: string, meta?: any): void {
83
+ if (this.shouldLog('debug')) {
84
+ console.debug(this.formatMessage('debug', message, meta))
85
+ }
86
+ }
87
+
88
+ info(message: string, meta?: any): void {
89
+ if (this.shouldLog('info')) {
90
+ console.info(this.formatMessage('info', message, meta))
91
+ }
92
+ }
93
+
94
+ warn(message: string, meta?: any): void {
95
+ if (this.shouldLog('warn')) {
96
+ console.warn(this.formatMessage('warn', message, meta))
97
+ }
98
+ }
99
+
100
+ error(message: string, meta?: any): void {
101
+ if (this.shouldLog('error')) {
102
+ console.error(this.formatMessage('error', message, meta))
103
+ }
104
+ }
105
+
106
+ // Contextual logging
107
+ child(context: any): FluxStackLogger {
108
+ return new FluxStackLogger({ ...this.context, ...context })
109
+ }
110
+
111
+ // Performance logging
112
+ time(label: string): void {
113
+ this.timers.set(label, Date.now())
114
+ }
115
+
116
+ timeEnd(label: string): void {
117
+ const startTime = this.timers.get(label)
118
+ if (startTime) {
119
+ const duration = Date.now() - startTime
120
+ this.info(`Timer ${label}: ${duration}ms`)
121
+ this.timers.delete(label)
122
+ }
123
+ }
124
+
125
+ // HTTP request logging
126
+ request(method: string, path: string, status?: number, duration?: number): void {
127
+ const statusStr = status ? ` ${status}` : ''
128
+ const durationStr = duration ? ` (${duration}ms)` : ''
129
+ this.info(`${method} ${path}${statusStr}${durationStr}`)
130
+ }
131
+
132
+ // Plugin logging
133
+ plugin(pluginName: string, message: string, meta?: any): void {
134
+ this.debug(`[${pluginName}] ${message}`, meta)
135
+ }
136
+
137
+ // Framework logging
138
+ framework(message: string, meta?: any): void {
139
+ this.info(`[FluxStack] ${message}`, meta)
140
+ }
141
+ }
142
+
143
+ // Export singleton instance
144
+ export const logger = FluxStackLogger.getInstance()
145
+
146
+ // Export convenience functions
147
+ export const log = {
148
+ debug: (message: string, meta?: any) => logger.debug(message, meta),
149
+ info: (message: string, meta?: any) => logger.info(message, meta),
150
+ warn: (message: string, meta?: any) => logger.warn(message, meta),
151
+ error: (message: string, meta?: any) => logger.error(message, meta),
152
+ request: (method: string, path: string, status?: number, duration?: number) =>
153
+ logger.request(method, path, status, duration),
154
+ plugin: (pluginName: string, message: string, meta?: any) =>
155
+ logger.plugin(pluginName, message, meta),
156
+ framework: (message: string, meta?: any) =>
157
+ logger.framework(message, meta),
158
+ child: (context: any) => logger.child(context),
159
+ time: (label: string) => logger.time(label),
160
+ timeEnd: (label: string) => logger.timeEnd(label)
161
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * FluxStack Logger
3
+ * Environment-aware logging system
4
+ */
5
+
6
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error'
7
+
8
+ class Logger {
9
+ private static instance: Logger | null = null
10
+ private logLevel: LogLevel
11
+
12
+ private constructor() {
13
+ this.logLevel = (process.env.LOG_LEVEL as LogLevel) || 'info'
14
+ }
15
+
16
+ static getInstance(): Logger {
17
+ if (Logger.instance === null) {
18
+ Logger.instance = new Logger()
19
+ }
20
+ return Logger.instance
21
+ }
22
+
23
+ private shouldLog(level: LogLevel): boolean {
24
+ const levels: Record<LogLevel, number> = {
25
+ debug: 0,
26
+ info: 1,
27
+ warn: 2,
28
+ error: 3
29
+ }
30
+
31
+ return levels[level] >= levels[this.logLevel]
32
+ }
33
+
34
+ private formatMessage(level: LogLevel, message: string, meta?: any): string {
35
+ const timestamp = new Date().toISOString()
36
+ const levelStr = level.toUpperCase().padEnd(5)
37
+
38
+ let formatted = `[${timestamp}] ${levelStr} ${message}`
39
+
40
+ if (meta && typeof meta === 'object') {
41
+ formatted += ` ${JSON.stringify(meta)}`
42
+ } else if (meta !== undefined) {
43
+ formatted += ` ${meta}`
44
+ }
45
+
46
+ return formatted
47
+ }
48
+
49
+ debug(message: string, meta?: any): void {
50
+ if (this.shouldLog('debug')) {
51
+ console.debug(this.formatMessage('debug', message, meta))
52
+ }
53
+ }
54
+
55
+ info(message: string, meta?: any): void {
56
+ if (this.shouldLog('info')) {
57
+ console.info(this.formatMessage('info', message, meta))
58
+ }
59
+ }
60
+
61
+ warn(message: string, meta?: any): void {
62
+ if (this.shouldLog('warn')) {
63
+ console.warn(this.formatMessage('warn', message, meta))
64
+ }
65
+ }
66
+
67
+ error(message: string, meta?: any): void {
68
+ if (this.shouldLog('error')) {
69
+ console.error(this.formatMessage('error', message, meta))
70
+ }
71
+ }
72
+
73
+ // HTTP request logging
74
+ request(method: string, path: string, status?: number, duration?: number): void {
75
+ const statusStr = status ? ` ${status}` : ''
76
+ const durationStr = duration ? ` (${duration}ms)` : ''
77
+ this.info(`${method} ${path}${statusStr}${durationStr}`)
78
+ }
79
+
80
+ // Plugin logging
81
+ plugin(pluginName: string, message: string, meta?: any): void {
82
+ this.debug(`[${pluginName}] ${message}`, meta)
83
+ }
84
+
85
+ // Framework logging
86
+ framework(message: string, meta?: any): void {
87
+ this.info(`[FluxStack] ${message}`, meta)
88
+ }
89
+ }
90
+
91
+ // Export singleton instance
92
+ export const logger = Logger.getInstance()
93
+
94
+ // Export convenience functions
95
+ export const log = {
96
+ debug: (message: string, meta?: any) => logger.debug(message, meta),
97
+ info: (message: string, meta?: any) => logger.info(message, meta),
98
+ warn: (message: string, meta?: any) => logger.warn(message, meta),
99
+ error: (message: string, meta?: any) => logger.error(message, meta),
100
+ request: (method: string, path: string, status?: number, duration?: number) =>
101
+ logger.request(method, path, status, duration),
102
+ plugin: (pluginName: string, message: string, meta?: any) =>
103
+ logger.plugin(pluginName, message, meta),
104
+ framework: (message: string, meta?: any) =>
105
+ logger.framework(message, meta)
106
+ }
@@ -0,0 +1,212 @@
1
+ export interface Metric {
2
+ name: string
3
+ type: 'counter' | 'gauge' | 'histogram'
4
+ help: string
5
+ labels?: string[]
6
+ value?: number
7
+ values?: number[]
8
+ }
9
+
10
+ export interface Counter extends Metric {
11
+ type: 'counter'
12
+ inc(value?: number, labels?: Record<string, string>): void
13
+ }
14
+
15
+ export interface Gauge extends Metric {
16
+ type: 'gauge'
17
+ set(value: number, labels?: Record<string, string>): void
18
+ inc(value?: number, labels?: Record<string, string>): void
19
+ dec(value?: number, labels?: Record<string, string>): void
20
+ }
21
+
22
+ export interface Histogram extends Metric {
23
+ type: 'histogram'
24
+ observe(value: number, labels?: Record<string, string>): void
25
+ buckets: number[]
26
+ }
27
+
28
+ export interface SystemMetrics {
29
+ memoryUsage: {
30
+ rss: number
31
+ heapTotal: number
32
+ heapUsed: number
33
+ external: number
34
+ }
35
+ cpuUsage: {
36
+ user: number
37
+ system: number
38
+ }
39
+ eventLoopLag: number
40
+ uptime: number
41
+ }
42
+
43
+ export interface HttpMetrics {
44
+ requestsTotal: number
45
+ requestDuration: number[]
46
+ requestSize: number[]
47
+ responseSize: number[]
48
+ errorRate: number
49
+ }
50
+
51
+ export class MetricsCollector {
52
+ private metrics: Map<string, Metric> = new Map()
53
+ private httpMetrics: HttpMetrics = {
54
+ requestsTotal: 0,
55
+ requestDuration: [],
56
+ requestSize: [],
57
+ responseSize: [],
58
+ errorRate: 0
59
+ }
60
+
61
+ // Create metrics
62
+ createCounter(name: string, help: string, labels?: string[]): Counter {
63
+ const counter: Counter = {
64
+ name,
65
+ type: 'counter',
66
+ help,
67
+ labels,
68
+ value: 0,
69
+ inc: (value = 1, _labels) => {
70
+ counter.value = (counter.value || 0) + value
71
+ }
72
+ }
73
+
74
+ this.metrics.set(name, counter)
75
+ return counter
76
+ }
77
+
78
+ createGauge(name: string, help: string, labels?: string[]): Gauge {
79
+ const gauge: Gauge = {
80
+ name,
81
+ type: 'gauge',
82
+ help,
83
+ labels,
84
+ value: 0,
85
+ set: (value, _labels) => {
86
+ gauge.value = value
87
+ },
88
+ inc: (value = 1, _labels) => {
89
+ gauge.value = (gauge.value || 0) + value
90
+ },
91
+ dec: (value = 1, _labels) => {
92
+ gauge.value = (gauge.value || 0) - value
93
+ }
94
+ }
95
+
96
+ this.metrics.set(name, gauge)
97
+ return gauge
98
+ }
99
+
100
+ createHistogram(name: string, help: string, buckets: number[] = [0.1, 0.5, 1, 2.5, 5, 10]): Histogram {
101
+ const histogram: Histogram = {
102
+ name,
103
+ type: 'histogram',
104
+ help,
105
+ buckets,
106
+ values: [],
107
+ observe: (value, _labels) => {
108
+ histogram.values = histogram.values || []
109
+ histogram.values.push(value)
110
+ }
111
+ }
112
+
113
+ this.metrics.set(name, histogram)
114
+ return histogram
115
+ }
116
+
117
+ // HTTP metrics
118
+ recordHttpRequest(_method: string, _path: string, statusCode: number, duration: number, requestSize?: number, responseSize?: number): void {
119
+ this.httpMetrics.requestsTotal++
120
+ this.httpMetrics.requestDuration.push(duration)
121
+
122
+ if (requestSize) {
123
+ this.httpMetrics.requestSize.push(requestSize)
124
+ }
125
+
126
+ if (responseSize) {
127
+ this.httpMetrics.responseSize.push(responseSize)
128
+ }
129
+
130
+ if (statusCode >= 400) {
131
+ this.httpMetrics.errorRate = this.calculateErrorRate()
132
+ }
133
+ }
134
+
135
+ // System metrics
136
+ getSystemMetrics(): SystemMetrics {
137
+ const memUsage = process.memoryUsage()
138
+ const cpuUsage = process.cpuUsage()
139
+
140
+ return {
141
+ memoryUsage: {
142
+ rss: memUsage.rss,
143
+ heapTotal: memUsage.heapTotal,
144
+ heapUsed: memUsage.heapUsed,
145
+ external: memUsage.external
146
+ },
147
+ cpuUsage: {
148
+ user: cpuUsage.user,
149
+ system: cpuUsage.system
150
+ },
151
+ eventLoopLag: this.measureEventLoopLag(),
152
+ uptime: process.uptime()
153
+ }
154
+ }
155
+
156
+ // Get all metrics
157
+ getAllMetrics(): Map<string, Metric> {
158
+ return new Map(this.metrics)
159
+ }
160
+
161
+ getHttpMetrics(): HttpMetrics {
162
+ return { ...this.httpMetrics }
163
+ }
164
+
165
+ // Export metrics in Prometheus format
166
+ exportPrometheus(): string {
167
+ let output = ''
168
+
169
+ for (const metric of this.metrics.values()) {
170
+ output += `# HELP ${metric.name} ${metric.help}\n`
171
+ output += `# TYPE ${metric.name} ${metric.type}\n`
172
+
173
+ if (metric.type === 'counter' || metric.type === 'gauge') {
174
+ output += `${metric.name} ${metric.value || 0}\n`
175
+ } else if (metric.type === 'histogram' && metric.values) {
176
+ const values = metric.values.sort((a, b) => a - b)
177
+ const buckets = (metric as Histogram).buckets
178
+
179
+ for (const bucket of buckets) {
180
+ const count = values.filter(v => v <= bucket).length
181
+ output += `${metric.name}_bucket{le="${bucket}"} ${count}\n`
182
+ }
183
+
184
+ output += `${metric.name}_bucket{le="+Inf"} ${values.length}\n`
185
+ output += `${metric.name}_count ${values.length}\n`
186
+ output += `${metric.name}_sum ${values.reduce((sum, v) => sum + v, 0)}\n`
187
+ }
188
+
189
+ output += '\n'
190
+ }
191
+
192
+ return output
193
+ }
194
+
195
+ private calculateErrorRate(): number {
196
+ const totalRequests = this.httpMetrics.requestsTotal
197
+ if (totalRequests === 0) return 0
198
+
199
+ // This is a simplified calculation - in a real implementation,
200
+ // you'd track error counts separately
201
+ return 0 // Placeholder
202
+ }
203
+
204
+ private measureEventLoopLag(): number {
205
+ const start = process.hrtime.bigint()
206
+ setImmediate(() => {
207
+ const lag = Number(process.hrtime.bigint() - start) / 1e6 // Convert to milliseconds
208
+ return lag
209
+ })
210
+ return 0 // Placeholder - actual implementation would be more complex
211
+ }
212
+ }
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { program } from 'commander'
4
+ import { resolve, join } from 'path'
5
+ import { existsSync, mkdirSync, cpSync, writeFileSync, readFileSync } from 'fs'
6
+ import chalk from 'chalk'
7
+ import ora from 'ora'
8
+
9
+ const logo = `
10
+ ⚔ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ
11
+ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ
12
+ ā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆ
13
+ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ
14
+ ā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ ā–ˆā–ˆ ā–ˆā–ˆ
15
+
16
+ ${chalk.cyan('šŸ’« Powered by Bun - The Divine Runtime ⚔')}
17
+ ${chalk.gray('Creates FluxStack apps by copying the working framework')}
18
+ `
19
+
20
+ program
21
+ .name('create-fluxstack')
22
+ .description('⚔ Create FluxStack apps with zero configuration')
23
+ .version('1.0.0')
24
+ .argument('[project-name]', 'Name of the project to create')
25
+ .option('--no-install', 'Skip dependency installation')
26
+ .option('--no-git', 'Skip git initialization')
27
+ .action(async (projectName, options) => {
28
+ console.clear()
29
+ console.log(chalk.magenta(logo))
30
+
31
+ if (!projectName || projectName.trim().length === 0) {
32
+ console.log(chalk.red('āŒ Project name is required'))
33
+ console.log(chalk.gray('Usage: ./create-fluxstack.ts my-app'))
34
+ process.exit(1)
35
+ }
36
+
37
+ const currentDir = import.meta.dir
38
+ const projectPath = resolve(projectName)
39
+
40
+ // Check if directory already exists
41
+ if (existsSync(projectPath)) {
42
+ console.log(chalk.red(`āŒ Directory ${projectName} already exists`))
43
+ process.exit(1)
44
+ }
45
+
46
+ console.log(chalk.cyan(`\\nšŸš€ Creating FluxStack project: ${chalk.bold(projectName)}`))
47
+ console.log(chalk.gray(`šŸ“ Location: ${projectPath}`))
48
+
49
+ // Create project directory
50
+ const spinner = ora('Creating project structure...').start()
51
+
52
+ try {
53
+ mkdirSync(projectPath, { recursive: true })
54
+
55
+ // Copy only essential FluxStack files (not node_modules, not test apps, etc.)
56
+ const filesToCopy = [
57
+ 'core',
58
+ 'app',
59
+ 'package.json',
60
+ 'bun.lock', // āœ… CRITICAL: Copy lockfile to maintain working versions
61
+ 'tsconfig.json',
62
+ 'vite.config.ts',
63
+ '.env',
64
+ 'README.md'
65
+ ]
66
+
67
+ for (const file of filesToCopy) {
68
+ const sourcePath = join(currentDir, file)
69
+ const destPath = join(projectPath, file)
70
+
71
+ if (existsSync(sourcePath)) {
72
+ cpSync(sourcePath, destPath, { recursive: true })
73
+ }
74
+ }
75
+
76
+ // Customize package.json
77
+ const packageJsonPath = join(projectPath, 'package.json')
78
+ if (existsSync(packageJsonPath)) {
79
+ const packageContent = readFileSync(packageJsonPath, 'utf-8')
80
+ const packageJson = JSON.parse(packageContent)
81
+
82
+ packageJson.name = projectName
83
+ packageJson.description = `${projectName} - FluxStack application`
84
+
85
+ // Remove scripts that don't make sense for user projects
86
+ delete packageJson.scripts['test:watch']
87
+ delete packageJson.scripts['test:coverage']
88
+ delete packageJson.scripts['docs:build']
89
+ delete packageJson.scripts['docs:serve']
90
+
91
+ writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))
92
+ }
93
+
94
+ // Customize .env for development mode
95
+ const envPath = join(projectPath, '.env')
96
+ if (existsSync(envPath)) {
97
+ const envContent = readFileSync(envPath, 'utf-8')
98
+ const devEnvContent = envContent.replace(
99
+ 'NODE_ENV=production',
100
+ 'NODE_ENV=development'
101
+ )
102
+ writeFileSync(envPath, devEnvContent)
103
+ }
104
+
105
+ // Customize README.md
106
+ const readmePath = join(projectPath, 'README.md')
107
+ if (existsSync(readmePath)) {
108
+ const readmeContent = `# ${projectName}
109
+
110
+ ⚔ **FluxStack Application** - Modern full-stack TypeScript framework
111
+
112
+ ## šŸš€ Getting Started
113
+
114
+ \`\`\`bash
115
+ # Start development
116
+ bun run dev
117
+
118
+ # Build for production
119
+ bun run build
120
+
121
+ # Start production server
122
+ bun run start
123
+ \`\`\`
124
+
125
+ ## šŸ“ Project Structure
126
+
127
+ \`\`\`
128
+ ${projectName}/
129
+ ā”œā”€ā”€ core/ # FluxStack framework (don't modify)
130
+ ā”œā”€ā”€ app/ # Your application code
131
+ │ ā”œā”€ā”€ server/ # Backend API routes
132
+ │ ā”œā”€ā”€ client/ # Frontend React app
133
+ │ └── shared/ # Shared types and utilities
134
+ └── package.json
135
+ \`\`\`
136
+
137
+ ## šŸ”„ Features
138
+
139
+ - **⚔ Bun Runtime** - 3x faster than Node.js
140
+ - **šŸ”’ Full Type Safety** - Eden Treaty + TypeScript
141
+ - **šŸŽØ Modern UI** - React 19 + Tailwind CSS v4
142
+ - **šŸ“‹ Auto Documentation** - Swagger UI generated
143
+ - **šŸ”„ Hot Reload** - Backend + Frontend
144
+
145
+ ## šŸ“– Learn More
146
+
147
+ Visit the [FluxStack Documentation](https://fluxstack.dev) to learn more!
148
+
149
+ ---
150
+
151
+ Built with ā¤ļø using FluxStack
152
+ `
153
+ writeFileSync(readmePath, readmeContent)
154
+ }
155
+
156
+ spinner.succeed('āœ… Project structure created!')
157
+
158
+ // Install dependencies with Bun (THE DIVINE RUNTIME)
159
+ if (options.install) {
160
+ const installSpinner = ora('šŸ“¦ Installing dependencies with Bun...').start()
161
+
162
+ try {
163
+ const proc = Bun.spawn(['bun', 'install'], {
164
+ cwd: projectPath,
165
+ stdio: ['ignore', 'pipe', 'pipe']
166
+ })
167
+
168
+ await proc.exited
169
+
170
+ if (proc.exitCode === 0) {
171
+ installSpinner.succeed('āœ… Dependencies installed!')
172
+ } else {
173
+ installSpinner.fail('āŒ Failed to install dependencies')
174
+ console.log(chalk.gray('You can install them manually with: bun install'))
175
+ }
176
+ } catch (error) {
177
+ installSpinner.fail('āŒ Failed to install dependencies')
178
+ console.log(chalk.gray('You can install them manually with: bun install'))
179
+ }
180
+ }
181
+
182
+ // Initialize git
183
+ if (options.git) {
184
+ const gitSpinner = ora('šŸ“ Initializing git repository...').start()
185
+
186
+ try {
187
+ const initProc = Bun.spawn(['git', 'init'], {
188
+ cwd: projectPath,
189
+ stdio: ['ignore', 'pipe', 'pipe']
190
+ })
191
+ await initProc.exited
192
+
193
+ // Create initial commit
194
+ const addProc = Bun.spawn(['git', 'add', '.'], {
195
+ cwd: projectPath,
196
+ stdio: ['ignore', 'pipe', 'pipe']
197
+ })
198
+ await addProc.exited
199
+
200
+ const commitProc = Bun.spawn(['git', 'commit', '-m', `feat: initial ${projectName} with FluxStack`], {
201
+ cwd: projectPath,
202
+ stdio: ['ignore', 'pipe', 'pipe']
203
+ })
204
+ await commitProc.exited
205
+
206
+ gitSpinner.succeed('āœ… Git repository initialized!')
207
+ } catch (error) {
208
+ gitSpinner.fail('āŒ Failed to initialize git')
209
+ console.log(chalk.gray('You can initialize it manually with: git init'))
210
+ }
211
+ }
212
+
213
+ // Success message
214
+ console.log(chalk.green('\\nšŸŽ‰ Project created successfully!'))
215
+ console.log(chalk.cyan('\\nNext steps:'))
216
+ console.log(chalk.white(` cd ${projectName}`))
217
+ if (!options.install) {
218
+ console.log(chalk.white(` bun install`))
219
+ }
220
+ console.log(chalk.white(` bun run dev`))
221
+ console.log(chalk.magenta('\\nHappy coding with the divine Bun runtime! āš”šŸ”„'))
222
+ console.log(chalk.gray('\\nVisit http://localhost:3000 when ready!'))
223
+
224
+ } catch (error) {
225
+ spinner.fail('āŒ Failed to create project')
226
+ console.error(error)
227
+ process.exit(1)
228
+ }
229
+ })
230
+
231
+ program.parse()