create-fluxstack 1.0.0 → 1.0.2

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/create-fluxstack.ts +32 -17
  2. package/package-template.json +51 -0
  3. package/package.json +2 -1
  4. package/.env +0 -30
  5. package/LICENSE +0 -21
  6. package/app/client/README.md +0 -69
  7. package/app/client/frontend-only.ts +0 -12
  8. package/app/client/index.html +0 -13
  9. package/app/client/public/vite.svg +0 -1
  10. package/app/client/src/App.css +0 -883
  11. package/app/client/src/App.tsx +0 -669
  12. package/app/client/src/assets/react.svg +0 -1
  13. package/app/client/src/components/TestPage.tsx +0 -453
  14. package/app/client/src/index.css +0 -51
  15. package/app/client/src/lib/eden-api.ts +0 -110
  16. package/app/client/src/main.tsx +0 -10
  17. package/app/client/src/vite-env.d.ts +0 -1
  18. package/app/client/tsconfig.app.json +0 -43
  19. package/app/client/tsconfig.json +0 -7
  20. package/app/client/tsconfig.node.json +0 -25
  21. package/app/server/app.ts +0 -10
  22. package/app/server/backend-only.ts +0 -15
  23. package/app/server/controllers/users.controller.ts +0 -69
  24. package/app/server/index.ts +0 -104
  25. package/app/server/routes/index.ts +0 -25
  26. package/app/server/routes/users.routes.ts +0 -121
  27. package/app/server/types/index.ts +0 -1
  28. package/app/shared/types/index.ts +0 -18
  29. package/bun.lock +0 -1053
  30. package/core/__tests__/integration.test.ts +0 -227
  31. package/core/build/index.ts +0 -186
  32. package/core/cli/command-registry.ts +0 -334
  33. package/core/cli/index.ts +0 -394
  34. package/core/cli/plugin-discovery.ts +0 -200
  35. package/core/client/standalone.ts +0 -57
  36. package/core/config/__tests__/config-loader.test.ts +0 -591
  37. package/core/config/__tests__/config-merger.test.ts +0 -657
  38. package/core/config/__tests__/env-converter.test.ts +0 -372
  39. package/core/config/__tests__/env-processor.test.ts +0 -431
  40. package/core/config/__tests__/env.test.ts +0 -452
  41. package/core/config/__tests__/integration.test.ts +0 -418
  42. package/core/config/__tests__/loader.test.ts +0 -331
  43. package/core/config/__tests__/schema.test.ts +0 -129
  44. package/core/config/__tests__/validator.test.ts +0 -318
  45. package/core/config/env-dynamic.ts +0 -326
  46. package/core/config/env.ts +0 -597
  47. package/core/config/index.ts +0 -317
  48. package/core/config/loader.ts +0 -546
  49. package/core/config/runtime-config.ts +0 -322
  50. package/core/config/schema.ts +0 -694
  51. package/core/config/validator.ts +0 -540
  52. package/core/framework/__tests__/server.test.ts +0 -233
  53. package/core/framework/client.ts +0 -132
  54. package/core/framework/index.ts +0 -8
  55. package/core/framework/server.ts +0 -501
  56. package/core/framework/types.ts +0 -63
  57. package/core/plugins/__tests__/built-in.test.ts.disabled +0 -366
  58. package/core/plugins/__tests__/manager.test.ts +0 -398
  59. package/core/plugins/__tests__/monitoring.test.ts +0 -401
  60. package/core/plugins/__tests__/registry.test.ts +0 -335
  61. package/core/plugins/built-in/index.ts +0 -142
  62. package/core/plugins/built-in/logger/index.ts +0 -180
  63. package/core/plugins/built-in/monitoring/README.md +0 -193
  64. package/core/plugins/built-in/monitoring/index.ts +0 -912
  65. package/core/plugins/built-in/static/index.ts +0 -289
  66. package/core/plugins/built-in/swagger/index.ts +0 -229
  67. package/core/plugins/built-in/vite/index.ts +0 -316
  68. package/core/plugins/config.ts +0 -348
  69. package/core/plugins/discovery.ts +0 -350
  70. package/core/plugins/executor.ts +0 -351
  71. package/core/plugins/index.ts +0 -195
  72. package/core/plugins/manager.ts +0 -583
  73. package/core/plugins/registry.ts +0 -424
  74. package/core/plugins/types.ts +0 -254
  75. package/core/server/framework.ts +0 -123
  76. package/core/server/index.ts +0 -8
  77. package/core/server/plugins/database.ts +0 -182
  78. package/core/server/plugins/logger.ts +0 -47
  79. package/core/server/plugins/swagger.ts +0 -34
  80. package/core/server/standalone.ts +0 -91
  81. package/core/templates/create-project.ts +0 -455
  82. package/core/types/api.ts +0 -169
  83. package/core/types/build.ts +0 -174
  84. package/core/types/config.ts +0 -68
  85. package/core/types/index.ts +0 -127
  86. package/core/types/plugin.ts +0 -94
  87. package/core/utils/__tests__/errors.test.ts +0 -139
  88. package/core/utils/__tests__/helpers.test.ts +0 -297
  89. package/core/utils/__tests__/logger.test.ts +0 -141
  90. package/core/utils/env-runtime-v2.ts +0 -232
  91. package/core/utils/env-runtime.ts +0 -252
  92. package/core/utils/errors/codes.ts +0 -115
  93. package/core/utils/errors/handlers.ts +0 -63
  94. package/core/utils/errors/index.ts +0 -81
  95. package/core/utils/helpers.ts +0 -180
  96. package/core/utils/index.ts +0 -18
  97. package/core/utils/logger/index.ts +0 -161
  98. package/core/utils/logger.ts +0 -106
  99. package/core/utils/monitoring/index.ts +0 -212
  100. package/tsconfig.json +0 -51
  101. package/vite.config.ts +0 -42
@@ -1,501 +0,0 @@
1
- import { Elysia } from "elysia"
2
- import type { FluxStackConfig, FluxStackContext } from "../types"
3
- import type { Plugin, PluginContext, PluginUtils } from "../plugins/types"
4
- import { PluginRegistry } from "../plugins/registry"
5
- import { getConfigSync, getEnvironmentInfo } from "../config"
6
- import { logger } from "../utils/logger"
7
- import { createErrorHandler } from "../utils/errors/handlers"
8
- import { createTimer, formatBytes, isProduction, isDevelopment } from "../utils/helpers"
9
-
10
- export class FluxStackFramework {
11
- private app: Elysia
12
- private context: FluxStackContext
13
- private pluginRegistry: PluginRegistry
14
- private pluginContext: PluginContext
15
- private isStarted: boolean = false
16
-
17
- constructor(config?: Partial<FluxStackConfig>) {
18
- // Load the full configuration
19
- const fullConfig = config ? { ...getConfigSync(), ...config } : getConfigSync()
20
- const envInfo = getEnvironmentInfo()
21
-
22
- this.context = {
23
- config: fullConfig,
24
- isDevelopment: envInfo.isDevelopment,
25
- isProduction: envInfo.isProduction,
26
- isTest: envInfo.isTest,
27
- environment: envInfo.name
28
- }
29
-
30
- this.app = new Elysia()
31
- this.pluginRegistry = new PluginRegistry()
32
-
33
- // Create plugin utilities
34
- const pluginUtils: PluginUtils = {
35
- createTimer,
36
- formatBytes,
37
- isProduction,
38
- isDevelopment,
39
- getEnvironment: () => envInfo.name,
40
- createHash: (data: string) => {
41
- const crypto = require('crypto')
42
- return crypto.createHash('sha256').update(data).digest('hex')
43
- },
44
- deepMerge: (target: any, source: any) => {
45
- const result = { ...target }
46
- for (const key in source) {
47
- if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
48
- result[key] = pluginUtils.deepMerge(result[key] || {}, source[key])
49
- } else {
50
- result[key] = source[key]
51
- }
52
- }
53
- return result
54
- },
55
- validateSchema: (_data: any, _schema: any) => {
56
- // Simple validation - in a real implementation you'd use a proper schema validator
57
- try {
58
- // Basic validation logic
59
- return { valid: true, errors: [] }
60
- } catch (error) {
61
- return { valid: false, errors: [error instanceof Error ? error.message : 'Validation failed'] }
62
- }
63
- }
64
- }
65
-
66
- // Create a logger wrapper that implements the full Logger interface
67
- const pluginLogger = {
68
- debug: (message: string, meta?: any) => logger.debug(message, meta),
69
- info: (message: string, meta?: any) => logger.info(message, meta),
70
- warn: (message: string, meta?: any) => logger.warn(message, meta),
71
- error: (message: string, meta?: any) => logger.error(message, meta),
72
- child: (context: any) => (logger as any).child(context),
73
- time: (label: string) => (logger as any).time(label),
74
- timeEnd: (label: string) => (logger as any).timeEnd(label),
75
- request: (method: string, path: string, status?: number, duration?: number) =>
76
- logger.request(method, path, status, duration)
77
- }
78
-
79
- this.pluginContext = {
80
- config: fullConfig,
81
- logger: pluginLogger,
82
- app: this.app,
83
- utils: pluginUtils
84
- }
85
-
86
- this.setupCors()
87
- this.setupHooks()
88
- this.setupErrorHandling()
89
-
90
- logger.framework('FluxStack framework initialized', {
91
- environment: envInfo.name,
92
- port: fullConfig.server.port
93
- })
94
- }
95
-
96
- private setupCors() {
97
- const { cors } = this.context.config.server
98
-
99
- this.app
100
- .onRequest(({ set }) => {
101
- set.headers["Access-Control-Allow-Origin"] = cors.origins.join(", ") || "*"
102
- set.headers["Access-Control-Allow-Methods"] = cors.methods.join(", ") || "*"
103
- set.headers["Access-Control-Allow-Headers"] = cors.headers.join(", ") || "*"
104
- if (cors.credentials) {
105
- set.headers["Access-Control-Allow-Credentials"] = "true"
106
- }
107
- })
108
- .options("*", ({ set }) => {
109
- set.status = 200
110
- return ""
111
- })
112
- }
113
-
114
- private setupHooks() {
115
- // Setup onRequest hook and onBeforeRoute hook
116
- this.app.onRequest(async ({ request, set }) => {
117
- const startTime = Date.now()
118
- const url = new URL(request.url)
119
-
120
- const requestContext = {
121
- request,
122
- path: url.pathname,
123
- method: request.method,
124
- headers: (() => {
125
- const headers: Record<string, string> = {}
126
- request.headers.forEach((value: string, key: string) => {
127
- headers[key] = value
128
- })
129
- return headers
130
- })(),
131
- query: Object.fromEntries(url.searchParams.entries()),
132
- params: {},
133
- startTime,
134
- handled: false,
135
- response: undefined
136
- }
137
-
138
- // Execute onRequest hooks for all plugins first (logging, auth, etc.)
139
- await this.executePluginHooks('onRequest', requestContext)
140
-
141
- // Execute onBeforeRoute hooks - allow plugins to handle requests before routing
142
- const handledResponse = await this.executePluginBeforeRouteHooks(requestContext)
143
-
144
- // If a plugin handled the request, return the response
145
- if (handledResponse) {
146
- return handledResponse
147
- }
148
- })
149
-
150
- // Setup onResponse hook
151
- this.app.onAfterHandle(async ({ request, response, set }) => {
152
- const startTime = Date.now()
153
- const url = new URL(request.url)
154
-
155
- const responseContext = {
156
- request,
157
- path: url.pathname,
158
- method: request.method,
159
- headers: (() => {
160
- const headers: Record<string, string> = {}
161
- request.headers.forEach((value: string, key: string) => {
162
- headers[key] = value
163
- })
164
- return headers
165
- })(),
166
- query: Object.fromEntries(url.searchParams.entries()),
167
- params: {},
168
- response,
169
- statusCode: (response as any)?.status || set.status || 200,
170
- duration: Date.now() - startTime,
171
- startTime
172
- }
173
-
174
- // Execute onResponse hooks for all plugins
175
- await this.executePluginHooks('onResponse', responseContext)
176
- })
177
- }
178
-
179
- private setupErrorHandling() {
180
- const errorHandler = createErrorHandler({
181
- logger: this.pluginContext.logger,
182
- isDevelopment: this.context.isDevelopment
183
- })
184
-
185
- this.app.onError(async ({ error, request, path, set }) => {
186
- const startTime = Date.now()
187
- const url = new URL(request.url)
188
-
189
- const errorContext = {
190
- request,
191
- path: url.pathname,
192
- method: request.method,
193
- headers: (() => {
194
- const headers: Record<string, string> = {}
195
- request.headers.forEach((value: string, key: string) => {
196
- headers[key] = value
197
- })
198
- return headers
199
- })(),
200
- query: Object.fromEntries(url.searchParams.entries()),
201
- params: {},
202
- error: error instanceof Error ? error : new Error(String(error)),
203
- duration: Date.now() - startTime,
204
- handled: false,
205
- startTime
206
- }
207
-
208
- // Execute onError hooks for all plugins - allow them to handle the error
209
- const handledResponse = await this.executePluginErrorHooks(errorContext)
210
-
211
- // If a plugin handled the error, return the response
212
- if (handledResponse) {
213
- return handledResponse
214
- }
215
-
216
- // Vite proxy logic is now handled by the Vite plugin via onBeforeRoute hook
217
-
218
- // Convert Elysia error to standard Error if needed
219
- const standardError = error instanceof Error ? error : new Error(String(error))
220
- return errorHandler(standardError, request, path)
221
- })
222
- }
223
-
224
- private async executePluginHooks(hookName: string, context: any): Promise<void> {
225
- const loadOrder = this.pluginRegistry.getLoadOrder()
226
-
227
- for (const pluginName of loadOrder) {
228
- const plugin = this.pluginRegistry.get(pluginName)
229
- if (!plugin) continue
230
-
231
- const hookFn = (plugin as any)[hookName]
232
- if (typeof hookFn === 'function') {
233
- try {
234
- await hookFn(context)
235
- } catch (error) {
236
- logger.error(`Plugin '${pluginName}' ${hookName} hook failed`, {
237
- error: (error as Error).message
238
- })
239
- }
240
- }
241
- }
242
- }
243
-
244
- private async executePluginBeforeRouteHooks(requestContext: any): Promise<Response | null> {
245
- const loadOrder = this.pluginRegistry.getLoadOrder()
246
-
247
- for (const pluginName of loadOrder) {
248
- const plugin = this.pluginRegistry.get(pluginName)
249
- if (!plugin) continue
250
-
251
- const onBeforeRouteFn = (plugin as any).onBeforeRoute
252
- if (typeof onBeforeRouteFn === 'function') {
253
- try {
254
- await onBeforeRouteFn(requestContext)
255
-
256
- // If this plugin handled the request, return the response
257
- if (requestContext.handled && requestContext.response) {
258
- return requestContext.response
259
- }
260
- } catch (error) {
261
- logger.error(`Plugin '${pluginName}' onBeforeRoute hook failed`, {
262
- error: (error as Error).message
263
- })
264
- }
265
- }
266
- }
267
-
268
- return null
269
- }
270
-
271
- private async executePluginErrorHooks(errorContext: any): Promise<Response | null> {
272
- const loadOrder = this.pluginRegistry.getLoadOrder()
273
-
274
- for (const pluginName of loadOrder) {
275
- const plugin = this.pluginRegistry.get(pluginName)
276
- if (!plugin) continue
277
-
278
- const onErrorFn = (plugin as any).onError
279
- if (typeof onErrorFn === 'function') {
280
- try {
281
- await onErrorFn(errorContext)
282
-
283
- // If this plugin handled the error, check if it provides a response
284
- if (errorContext.handled) {
285
- // For Vite plugin, we'll handle the proxy here
286
- if (pluginName === 'vite' && errorContext.error.constructor.name === 'NotFoundError') {
287
- return await this.handleViteProxy(errorContext)
288
- }
289
-
290
- // For other plugins, return a basic success response
291
- return new Response('OK', { status: 200 })
292
- }
293
- } catch (error) {
294
- logger.error(`Plugin '${pluginName}' onError hook failed`, {
295
- error: (error as Error).message
296
- })
297
- }
298
- }
299
- }
300
-
301
- return null
302
- }
303
-
304
- private async handleViteProxy(errorContext: any): Promise<Response> {
305
- const vitePort = this.context.config.client?.port || 5173
306
- const url = new URL(errorContext.request.url)
307
-
308
- try {
309
- const viteUrl = `http://localhost:${vitePort}${url.pathname}${url.search}`
310
-
311
- // Forward request to Vite
312
- const response = await fetch(viteUrl, {
313
- method: errorContext.method,
314
- headers: errorContext.headers
315
- })
316
-
317
- // Return a proper Response object with all headers and status
318
- const body = await response.arrayBuffer()
319
-
320
- return new Response(body, {
321
- status: response.status,
322
- statusText: response.statusText,
323
- headers: response.headers
324
- })
325
-
326
- } catch (viteError) {
327
- // If Vite fails, return error response
328
- return new Response(`Vite server not ready on port ${vitePort}. Error: ${viteError}`, {
329
- status: 503,
330
- headers: { 'Content-Type': 'text/plain' }
331
- })
332
- }
333
- }
334
-
335
- use(plugin: Plugin) {
336
- try {
337
- // Use the registry's public register method, but don't await it since we need sync operation
338
- if (this.pluginRegistry.has(plugin.name)) {
339
- throw new Error(`Plugin '${plugin.name}' is already registered`)
340
- }
341
-
342
- // Store plugin without calling setup - setup will be called in start()
343
- // We need to manually set the plugin since register() is async but we need sync
344
- (this.pluginRegistry as any).plugins.set(plugin.name, plugin)
345
-
346
- // Update dependencies tracking
347
- if (plugin.dependencies) {
348
- (this.pluginRegistry as any).dependencies.set(plugin.name, plugin.dependencies)
349
- }
350
-
351
- // Update load order by calling the private method
352
- try {
353
- (this.pluginRegistry as any).updateLoadOrder()
354
- } catch (error) {
355
- // Fallback: create basic load order
356
- const plugins = (this.pluginRegistry as any).plugins as Map<string, Plugin>
357
- const loadOrder = Array.from(plugins.keys())
358
- ;(this.pluginRegistry as any).loadOrder = loadOrder
359
- }
360
-
361
- logger.framework(`Plugin '${plugin.name}' registered`, {
362
- version: plugin.version,
363
- dependencies: plugin.dependencies
364
- })
365
- return this
366
- } catch (error) {
367
- logger.error(`Failed to register plugin '${plugin.name}'`, { error: (error as Error).message })
368
- throw error
369
- }
370
- }
371
-
372
- routes(routeModule: any) {
373
- this.app.use(routeModule)
374
- return this
375
- }
376
-
377
- async start(): Promise<void> {
378
- if (this.isStarted) {
379
- logger.warn('Framework is already started')
380
- return
381
- }
382
-
383
- try {
384
- // Validate plugin dependencies before starting
385
- const plugins = (this.pluginRegistry as any).plugins as Map<string, Plugin>
386
- for (const [pluginName, plugin] of plugins) {
387
- if (plugin.dependencies) {
388
- for (const depName of plugin.dependencies) {
389
- if (!plugins.has(depName)) {
390
- throw new Error(`Plugin '${pluginName}' depends on '${depName}' which is not registered`)
391
- }
392
- }
393
- }
394
- }
395
-
396
- // Get load order
397
- const loadOrder = this.pluginRegistry.getLoadOrder()
398
-
399
- // Call setup hooks for all plugins
400
- for (const pluginName of loadOrder) {
401
- const plugin = this.pluginRegistry.get(pluginName)!
402
-
403
- // Call setup hook if it exists and hasn't been called
404
- if (plugin.setup) {
405
- await plugin.setup(this.pluginContext)
406
- }
407
- }
408
-
409
- // Call onServerStart hooks
410
- for (const pluginName of loadOrder) {
411
- const plugin = this.pluginRegistry.get(pluginName)!
412
-
413
- if (plugin.onServerStart) {
414
- await plugin.onServerStart(this.pluginContext)
415
- }
416
- }
417
-
418
- this.isStarted = true
419
- logger.framework('All plugins loaded successfully', {
420
- pluginCount: loadOrder.length
421
- })
422
-
423
- } catch (error) {
424
- logger.error('Failed to start framework', { error: (error as Error).message })
425
- throw error
426
- }
427
- }
428
-
429
- async stop(): Promise<void> {
430
- if (!this.isStarted) {
431
- return
432
- }
433
-
434
- try {
435
- // Call onServerStop hooks in reverse order
436
- const loadOrder = this.pluginRegistry.getLoadOrder().reverse()
437
-
438
- for (const pluginName of loadOrder) {
439
- const plugin = this.pluginRegistry.get(pluginName)!
440
-
441
- if (plugin.onServerStop) {
442
- await plugin.onServerStop(this.pluginContext)
443
- logger.framework(`Plugin '${pluginName}' server stop hook completed`)
444
- }
445
- }
446
-
447
- this.isStarted = false
448
- logger.framework('Framework stopped successfully')
449
-
450
- } catch (error) {
451
- logger.error('Error during framework shutdown', { error: (error as Error).message })
452
- throw error
453
- }
454
- }
455
-
456
- getApp() {
457
- return this.app
458
- }
459
-
460
- getContext() {
461
- return this.context
462
- }
463
-
464
- getPluginRegistry() {
465
- return this.pluginRegistry
466
- }
467
-
468
- async listen(callback?: () => void) {
469
- // Start the framework (load plugins)
470
- await this.start()
471
-
472
- const port = this.context.config.server.port
473
- const apiPrefix = this.context.config.server.apiPrefix
474
-
475
- this.app.listen(port, () => {
476
- logger.framework(`Server started on port ${port}`, {
477
- apiPrefix,
478
- environment: this.context.environment,
479
- pluginCount: this.pluginRegistry.getAll().length
480
- })
481
-
482
- console.log(`🚀 API ready at http://localhost:${port}${apiPrefix}`)
483
- console.log(`📋 Health check: http://localhost:${port}${apiPrefix}/health`)
484
- console.log()
485
- callback?.()
486
- })
487
-
488
- // Handle graceful shutdown
489
- process.on('SIGTERM', async () => {
490
- logger.framework('Received SIGTERM, shutting down gracefully')
491
- await this.stop()
492
- process.exit(0)
493
- })
494
-
495
- process.on('SIGINT', async () => {
496
- logger.framework('Received SIGINT, shutting down gracefully')
497
- await this.stop()
498
- process.exit(0)
499
- })
500
- }
501
- }
@@ -1,63 +0,0 @@
1
- /**
2
- * Core Framework Types
3
- * Defines the main interfaces and types for the FluxStack framework
4
- */
5
-
6
- import type { FluxStackConfig } from "../types"
7
- import type { Logger } from "../utils/logger/index"
8
-
9
- export interface FluxStackFrameworkOptions {
10
- config?: Partial<FluxStackConfig>
11
- plugins?: string[]
12
- autoStart?: boolean
13
- }
14
-
15
- export interface FrameworkContext {
16
- config: FluxStackConfig
17
- isDevelopment: boolean
18
- isProduction: boolean
19
- isTest: boolean
20
- environment: string
21
- logger: Logger
22
- startTime: Date
23
- }
24
-
25
- export interface FrameworkStats {
26
- uptime: number
27
- pluginCount: number
28
- requestCount: number
29
- errorCount: number
30
- memoryUsage: NodeJS.MemoryUsage
31
- }
32
-
33
- export interface FrameworkHooks {
34
- beforeStart?: () => void | Promise<void>
35
- afterStart?: () => void | Promise<void>
36
- beforeStop?: () => void | Promise<void>
37
- afterStop?: () => void | Promise<void>
38
- onError?: (error: Error) => void | Promise<void>
39
- }
40
-
41
- export interface RouteDefinition {
42
- method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD'
43
- path: string
44
- handler: Function
45
- schema?: any
46
- middleware?: Function[]
47
- description?: string
48
- tags?: string[]
49
- }
50
-
51
- export interface MiddlewareDefinition {
52
- name: string
53
- handler: Function
54
- priority?: number
55
- routes?: string[]
56
- }
57
-
58
- export interface ServiceDefinition {
59
- name: string
60
- instance: any
61
- dependencies?: string[]
62
- singleton?: boolean
63
- }