create-fluxstack 1.10.1 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (257) hide show
  1. package/.dockerignore +1 -2
  2. package/Dockerfile +8 -8
  3. package/LLMD/INDEX.md +64 -0
  4. package/LLMD/MAINTENANCE.md +197 -0
  5. package/LLMD/MIGRATION.md +156 -0
  6. package/LLMD/config/.gitkeep +1 -0
  7. package/LLMD/config/declarative-system.md +268 -0
  8. package/LLMD/config/environment-vars.md +327 -0
  9. package/LLMD/config/runtime-reload.md +401 -0
  10. package/LLMD/core/.gitkeep +1 -0
  11. package/LLMD/core/build-system.md +599 -0
  12. package/LLMD/core/framework-lifecycle.md +229 -0
  13. package/LLMD/core/plugin-system.md +451 -0
  14. package/LLMD/patterns/.gitkeep +1 -0
  15. package/LLMD/patterns/anti-patterns.md +297 -0
  16. package/LLMD/patterns/project-structure.md +264 -0
  17. package/LLMD/patterns/type-safety.md +440 -0
  18. package/LLMD/reference/.gitkeep +1 -0
  19. package/LLMD/reference/cli-commands.md +250 -0
  20. package/LLMD/reference/plugin-hooks.md +357 -0
  21. package/LLMD/reference/routing.md +39 -0
  22. package/LLMD/reference/troubleshooting.md +364 -0
  23. package/LLMD/resources/.gitkeep +1 -0
  24. package/LLMD/resources/controllers.md +465 -0
  25. package/LLMD/resources/live-components.md +703 -0
  26. package/LLMD/resources/live-rooms.md +482 -0
  27. package/LLMD/resources/live-upload.md +130 -0
  28. package/LLMD/resources/plugins-external.md +617 -0
  29. package/LLMD/resources/routes-eden.md +254 -0
  30. package/README.md +37 -17
  31. package/app/client/index.html +0 -1
  32. package/app/client/src/App.tsx +107 -150
  33. package/app/client/src/components/AppLayout.tsx +68 -0
  34. package/app/client/src/components/BackButton.tsx +13 -0
  35. package/app/client/src/components/DemoPage.tsx +20 -0
  36. package/app/client/src/components/LiveUploadWidget.tsx +204 -0
  37. package/app/client/src/lib/eden-api.ts +85 -60
  38. package/app/client/src/live/ChatDemo.tsx +107 -0
  39. package/app/client/src/live/CounterDemo.tsx +206 -0
  40. package/app/client/src/live/FormDemo.tsx +119 -0
  41. package/app/client/src/live/RoomChatDemo.tsx +242 -0
  42. package/app/client/src/live/UploadDemo.tsx +21 -0
  43. package/app/client/src/main.tsx +4 -1
  44. package/app/client/src/pages/ApiTestPage.tsx +108 -0
  45. package/app/client/src/pages/HomePage.tsx +76 -0
  46. package/app/server/app.ts +1 -4
  47. package/app/server/controllers/users.controller.ts +36 -44
  48. package/app/server/index.ts +25 -35
  49. package/app/server/live/LiveChat.ts +77 -0
  50. package/app/server/live/LiveCounter.ts +67 -0
  51. package/app/server/live/LiveForm.ts +63 -0
  52. package/app/server/live/LiveLocalCounter.ts +32 -0
  53. package/app/server/live/LiveRoomChat.ts +285 -0
  54. package/app/server/live/LiveUpload.ts +81 -0
  55. package/app/server/routes/index.ts +3 -1
  56. package/app/server/routes/room.routes.ts +117 -0
  57. package/app/server/routes/users.routes.ts +35 -27
  58. package/app/shared/types/index.ts +14 -2
  59. package/config/app.config.ts +2 -62
  60. package/config/client.config.ts +2 -95
  61. package/config/database.config.ts +2 -99
  62. package/config/fluxstack.config.ts +25 -45
  63. package/config/index.ts +57 -38
  64. package/config/monitoring.config.ts +2 -114
  65. package/config/plugins.config.ts +2 -80
  66. package/config/server.config.ts +2 -68
  67. package/config/services.config.ts +2 -130
  68. package/config/system/app.config.ts +29 -0
  69. package/config/system/build.config.ts +49 -0
  70. package/config/system/client.config.ts +68 -0
  71. package/config/system/database.config.ts +17 -0
  72. package/config/system/fluxstack.config.ts +114 -0
  73. package/config/{logger.config.ts → system/logger.config.ts} +3 -1
  74. package/config/system/monitoring.config.ts +114 -0
  75. package/config/system/plugins.config.ts +84 -0
  76. package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
  77. package/config/system/server.config.ts +68 -0
  78. package/config/system/services.config.ts +46 -0
  79. package/config/{system.config.ts → system/system.config.ts} +1 -1
  80. package/core/build/flux-plugins-generator.ts +325 -325
  81. package/core/build/index.ts +39 -27
  82. package/core/build/live-components-generator.ts +3 -3
  83. package/core/build/optimizer.ts +235 -235
  84. package/core/cli/command-registry.ts +6 -4
  85. package/core/cli/commands/build.ts +79 -0
  86. package/core/cli/commands/create.ts +54 -0
  87. package/core/cli/commands/dev.ts +101 -0
  88. package/core/cli/commands/help.ts +34 -0
  89. package/core/cli/commands/index.ts +34 -0
  90. package/core/cli/commands/make-plugin.ts +90 -0
  91. package/core/cli/commands/plugin-add.ts +197 -0
  92. package/core/cli/commands/plugin-deps.ts +2 -2
  93. package/core/cli/commands/plugin-list.ts +208 -0
  94. package/core/cli/commands/plugin-remove.ts +170 -0
  95. package/core/cli/generators/component.ts +769 -769
  96. package/core/cli/generators/controller.ts +1 -1
  97. package/core/cli/generators/index.ts +146 -146
  98. package/core/cli/generators/interactive.ts +227 -227
  99. package/core/cli/generators/plugin.ts +2 -2
  100. package/core/cli/generators/prompts.ts +82 -82
  101. package/core/cli/generators/route.ts +6 -6
  102. package/core/cli/generators/service.ts +2 -2
  103. package/core/cli/generators/template-engine.ts +4 -3
  104. package/core/cli/generators/types.ts +2 -2
  105. package/core/cli/generators/utils.ts +191 -191
  106. package/core/cli/index.ts +115 -686
  107. package/core/cli/plugin-discovery.ts +2 -2
  108. package/core/client/LiveComponentsProvider.tsx +60 -8
  109. package/core/client/api/eden.ts +183 -0
  110. package/core/client/api/index.ts +11 -0
  111. package/core/client/components/Live.tsx +104 -0
  112. package/core/client/fluxstack.ts +1 -9
  113. package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
  114. package/core/client/hooks/state-validator.ts +1 -1
  115. package/core/client/hooks/useAuth.ts +48 -48
  116. package/core/client/hooks/useChunkedUpload.ts +85 -35
  117. package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
  118. package/core/client/hooks/useLiveComponent.ts +800 -0
  119. package/core/client/hooks/useLiveUpload.ts +71 -0
  120. package/core/client/hooks/useRoom.ts +409 -0
  121. package/core/client/hooks/useRoomProxy.ts +382 -0
  122. package/core/client/index.ts +17 -68
  123. package/core/client/standalone-entry.ts +8 -0
  124. package/core/client/standalone.ts +74 -53
  125. package/core/client/state/createStore.ts +192 -192
  126. package/core/client/state/index.ts +14 -14
  127. package/core/config/index.ts +70 -291
  128. package/core/config/schema.ts +42 -723
  129. package/core/framework/client.ts +131 -131
  130. package/core/framework/index.ts +7 -7
  131. package/core/framework/server.ts +47 -40
  132. package/core/framework/types.ts +2 -2
  133. package/core/index.ts +23 -4
  134. package/core/live/ComponentRegistry.ts +3 -3
  135. package/core/live/types.ts +77 -0
  136. package/core/plugins/built-in/index.ts +134 -134
  137. package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
  138. package/core/plugins/built-in/live-components/index.ts +1 -1
  139. package/core/plugins/built-in/monitoring/index.ts +111 -47
  140. package/core/plugins/built-in/static/index.ts +1 -1
  141. package/core/plugins/built-in/swagger/index.ts +68 -265
  142. package/core/plugins/built-in/vite/index.ts +85 -185
  143. package/core/plugins/built-in/vite/vite-dev.ts +10 -16
  144. package/core/plugins/config.ts +9 -7
  145. package/core/plugins/dependency-manager.ts +31 -1
  146. package/core/plugins/discovery.ts +19 -7
  147. package/core/plugins/executor.ts +2 -2
  148. package/core/plugins/index.ts +203 -203
  149. package/core/plugins/manager.ts +27 -39
  150. package/core/plugins/module-resolver.ts +19 -8
  151. package/core/plugins/registry.ts +255 -19
  152. package/core/plugins/types.ts +20 -53
  153. package/core/server/framework.ts +66 -43
  154. package/core/server/index.ts +15 -15
  155. package/core/server/live/ComponentRegistry.ts +78 -71
  156. package/core/server/live/FileUploadManager.ts +23 -10
  157. package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
  158. package/core/server/live/LiveRoomManager.ts +261 -0
  159. package/core/server/live/RoomEventBus.ts +234 -0
  160. package/core/server/live/RoomStateManager.ts +172 -0
  161. package/core/server/live/StateSignature.ts +643 -643
  162. package/core/server/live/WebSocketConnectionManager.ts +30 -19
  163. package/core/server/live/auto-generated-components.ts +21 -9
  164. package/core/server/live/index.ts +14 -0
  165. package/core/server/live/websocket-plugin.ts +214 -67
  166. package/core/server/middleware/elysia-helpers.ts +7 -2
  167. package/core/server/middleware/errorHandling.ts +1 -1
  168. package/core/server/middleware/index.ts +31 -31
  169. package/core/server/plugins/database.ts +180 -180
  170. package/core/server/plugins/static-files-plugin.ts +69 -69
  171. package/core/server/plugins/swagger.ts +1 -1
  172. package/core/server/rooms/RoomBroadcaster.ts +357 -0
  173. package/core/server/rooms/RoomSystem.ts +463 -0
  174. package/core/server/rooms/index.ts +13 -0
  175. package/core/server/services/BaseService.ts +1 -1
  176. package/core/server/services/ServiceContainer.ts +1 -1
  177. package/core/server/services/index.ts +8 -8
  178. package/core/templates/create-project.ts +12 -12
  179. package/core/testing/index.ts +9 -9
  180. package/core/testing/setup.ts +73 -73
  181. package/core/types/api.ts +168 -168
  182. package/core/types/build.ts +219 -219
  183. package/core/types/config.ts +56 -26
  184. package/core/types/index.ts +4 -4
  185. package/core/types/plugin.ts +107 -107
  186. package/core/types/types.ts +353 -14
  187. package/core/utils/build-logger.ts +324 -324
  188. package/core/utils/config-schema.ts +480 -480
  189. package/core/utils/env.ts +2 -8
  190. package/core/utils/errors/codes.ts +114 -114
  191. package/core/utils/errors/handlers.ts +36 -1
  192. package/core/utils/errors/index.ts +49 -5
  193. package/core/utils/errors/middleware.ts +113 -113
  194. package/core/utils/helpers.ts +6 -16
  195. package/core/utils/index.ts +17 -17
  196. package/core/utils/logger/colors.ts +114 -114
  197. package/core/utils/logger/config.ts +13 -9
  198. package/core/utils/logger/formatter.ts +82 -82
  199. package/core/utils/logger/group-logger.ts +101 -101
  200. package/core/utils/logger/index.ts +6 -1
  201. package/core/utils/logger/stack-trace.ts +3 -1
  202. package/core/utils/logger/startup-banner.ts +82 -82
  203. package/core/utils/logger/winston-logger.ts +152 -152
  204. package/core/utils/monitoring/index.ts +211 -211
  205. package/core/utils/sync-version.ts +66 -66
  206. package/core/utils/version.ts +1 -1
  207. package/create-fluxstack.ts +8 -7
  208. package/package.json +12 -13
  209. package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
  210. package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
  211. package/plugins/crypto-auth/client/components/index.ts +11 -11
  212. package/plugins/crypto-auth/client/index.ts +11 -11
  213. package/plugins/crypto-auth/config/index.ts +1 -1
  214. package/plugins/crypto-auth/index.ts +4 -4
  215. package/plugins/crypto-auth/package.json +65 -65
  216. package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
  217. package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
  218. package/plugins/crypto-auth/server/index.ts +21 -21
  219. package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
  220. package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
  221. package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
  222. package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
  223. package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
  224. package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
  225. package/tsconfig.api-strict.json +16 -0
  226. package/tsconfig.json +48 -52
  227. package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
  228. package/types/global.d.ts +29 -29
  229. package/types/vitest.d.ts +8 -8
  230. package/vite.config.ts +38 -62
  231. package/vitest.config.live.ts +10 -9
  232. package/vitest.config.ts +29 -17
  233. package/app/client/README.md +0 -69
  234. package/app/client/SIMPLIFICATION.md +0 -140
  235. package/app/client/frontend-only.ts +0 -12
  236. package/app/client/src/live/FileUploadExample.tsx +0 -359
  237. package/app/client/src/live/MinimalLiveClock.tsx +0 -47
  238. package/app/client/src/live/QuickUploadTest.tsx +0 -193
  239. package/app/client/tsconfig.app.json +0 -45
  240. package/app/client/tsconfig.json +0 -7
  241. package/app/client/zustand-setup.md +0 -65
  242. package/app/server/backend-only.ts +0 -18
  243. package/app/server/live/LiveClockComponent.ts +0 -215
  244. package/app/server/live/LiveFileUploadComponent.ts +0 -77
  245. package/app/server/routes/env-test.ts +0 -110
  246. package/core/client/hooks/index.ts +0 -7
  247. package/core/client/hooks/useHybridLiveComponent.ts +0 -685
  248. package/core/client/hooks/useTypedLiveComponent.ts +0 -133
  249. package/core/client/hooks/useWebSocket.ts +0 -361
  250. package/core/config/env.ts +0 -546
  251. package/core/config/loader.ts +0 -522
  252. package/core/config/runtime-config.ts +0 -327
  253. package/core/config/validator.ts +0 -540
  254. package/core/server/backend-entry.ts +0 -51
  255. package/core/server/standalone.ts +0 -106
  256. package/core/utils/regenerate-files.ts +0 -69
  257. package/fluxstack.config.ts +0 -354
@@ -1,4 +1,4 @@
1
- import type { FluxStack, PluginContext } from '@/core/plugins/types'
1
+ import type { FluxStack, PluginContext } from '@core/plugins/types'
2
2
  import { createLiveComponentCommand } from './commands/create-live-component'
3
3
 
4
4
  type Plugin = FluxStack.Plugin
@@ -3,10 +3,9 @@
3
3
  * Provides performance monitoring, metrics collection, and system monitoring
4
4
  */
5
5
 
6
- import type { FluxStack, PluginContext, RequestContext, ResponseContext, ErrorContext } from "@/core/plugins/types"
7
- import { MetricsCollector } from "@/core/utils/monitoring"
8
- import { appConfig } from '@/config/app.config'
9
- import { monitoringConfig } from '@/config/monitoring.config'
6
+ import type { FluxStack, PluginContext, RequestContext, ResponseContext, ErrorContext } from "@core/plugins/types"
7
+ import { MetricsCollector } from "@core/utils/monitoring"
8
+ import { appConfig, monitoringConfig } from '@config'
10
9
  import * as os from 'os'
11
10
  import * as fs from 'fs'
12
11
  import * as path from 'path'
@@ -95,6 +94,71 @@ const DEFAULTS = {
95
94
  alerts: [] as AlertThreshold[]
96
95
  }
97
96
 
97
+ type MonitoringOptions = typeof DEFAULTS
98
+
99
+ function mergeMonitoringOptions(base: MonitoringOptions, overrides: Partial<MonitoringOptions>): MonitoringOptions {
100
+ const result: MonitoringOptions = { ...base }
101
+
102
+ for (const key of Object.keys(overrides) as (keyof MonitoringOptions)[]) {
103
+ const value = overrides[key]
104
+ if (value === undefined) continue
105
+
106
+ if (Array.isArray(value)) {
107
+ ;(result as any)[key] = value
108
+ } else if (value && typeof value === 'object' && !Array.isArray(value)) {
109
+ ;(result as any)[key] = { ...(base as any)[key], ...value }
110
+ } else {
111
+ ;(result as any)[key] = value
112
+ }
113
+ }
114
+
115
+ return result
116
+ }
117
+
118
+ function resolveMonitoringOptions(source: any): MonitoringOptions {
119
+ if (source?.monitoringConfig) return source.monitoringConfig
120
+ if (source?.context?.monitoringConfig) return source.context.monitoringConfig
121
+ return DEFAULTS
122
+ }
123
+
124
+ function normalizeRuntimeMonitoringConfig(runtime: any): Partial<MonitoringOptions> {
125
+ if (!runtime) return {}
126
+ const overrides: Partial<MonitoringOptions> = {}
127
+
128
+ if (typeof runtime.enabled === 'boolean') overrides.enabled = runtime.enabled
129
+ if (runtime.metrics) {
130
+ overrides.httpMetrics = runtime.metrics.httpMetrics
131
+ overrides.systemMetrics = runtime.metrics.systemMetrics
132
+ overrides.customMetrics = runtime.metrics.customMetrics
133
+ overrides.collectInterval = runtime.metrics.collectInterval
134
+ overrides.retentionPeriod = runtime.metrics.retentionPeriod
135
+ }
136
+ if (Array.isArray(runtime.exporters)) {
137
+ overrides.exporters = runtime.exporters as MetricsExporter[]
138
+ }
139
+ if (runtime.thresholds) {
140
+ overrides.thresholds = { ...DEFAULTS.thresholds, ...runtime.thresholds }
141
+ }
142
+ if (Array.isArray(runtime.alerts)) {
143
+ overrides.alerts = runtime.alerts
144
+ }
145
+
146
+ return overrides
147
+ }
148
+
149
+ const monitoringPluginConfigSchema = {
150
+ type: 'object' as const,
151
+ properties: {
152
+ enabled: { type: 'boolean' },
153
+ httpMetrics: { type: 'boolean' },
154
+ systemMetrics: { type: 'boolean' },
155
+ customMetrics: { type: 'boolean' },
156
+ collectInterval: { type: 'number' },
157
+ retentionPeriod: { type: 'number' },
158
+ exporters: { type: 'array' }
159
+ }
160
+ }
161
+
98
162
  export const monitoringPlugin: Plugin = {
99
163
  name: "monitoring",
100
164
  version: "1.0.0",
@@ -104,64 +168,64 @@ export const monitoringPlugin: Plugin = {
104
168
  category: "monitoring",
105
169
  tags: ["monitoring", "metrics", "performance", "observability"],
106
170
  dependencies: [],
171
+ configSchema: monitoringPluginConfigSchema,
172
+ defaultConfig: DEFAULTS,
107
173
 
108
174
  setup: async (context: PluginContext) => {
109
- if (!DEFAULTS.enabled) {
175
+ const runtimeOverrides = normalizeRuntimeMonitoringConfig((context as any).config?.monitoring)
176
+ const pluginOverrides = (context as any).config?.plugins?.config?.monitoring as Partial<MonitoringOptions> | undefined
177
+ const resolvedConfig = mergeMonitoringOptions(
178
+ mergeMonitoringOptions(DEFAULTS, runtimeOverrides),
179
+ pluginOverrides || {}
180
+ )
181
+
182
+ if (!resolvedConfig.enabled) {
110
183
  context.logger.info('Monitoring plugin disabled by configuration')
111
184
  return
112
185
  }
113
186
 
114
187
  context.logger.info('Initializing monitoring plugin', {
115
- httpMetrics: DEFAULTS.httpMetrics,
116
- systemMetrics: DEFAULTS.systemMetrics,
117
- customMetrics: DEFAULTS.customMetrics,
118
- exporters: DEFAULTS.exporters.length,
119
- alerts: DEFAULTS.alerts.length
188
+ httpMetrics: resolvedConfig.httpMetrics,
189
+ systemMetrics: resolvedConfig.systemMetrics,
190
+ customMetrics: resolvedConfig.customMetrics,
191
+ exporters: resolvedConfig.exporters.length,
192
+ alerts: resolvedConfig.alerts.length
120
193
  })
121
194
 
122
- // Initialize enhanced metrics registry
123
195
  const metricsRegistry: MetricsRegistry = {
124
196
  counters: new Map(),
125
197
  gauges: new Map(),
126
198
  histograms: new Map()
127
199
  }
128
200
 
129
- // Initialize metrics collector
130
201
  const metricsCollector = new MetricsCollector()
131
202
 
132
- // Store registry and collector in context for access by other hooks
133
203
  ;(context as any).metricsRegistry = metricsRegistry
134
204
  ;(context as any).metricsCollector = metricsCollector
205
+ ;(context as any).monitoringConfig = resolvedConfig
135
206
 
136
- // Initialize HTTP metrics
137
- if (DEFAULTS.httpMetrics) {
207
+ if (resolvedConfig.httpMetrics) {
138
208
  initializeHttpMetrics(metricsRegistry, metricsCollector)
139
209
  }
140
210
 
141
- // Start system metrics collection
142
- if (DEFAULTS.systemMetrics) {
143
- startSystemMetricsCollection(context, metricsCollector)
211
+ if (resolvedConfig.systemMetrics) {
212
+ startSystemMetricsCollection(context, metricsCollector, resolvedConfig)
144
213
  }
145
214
 
146
- // Setup metrics endpoint for Prometheus
147
- setupMetricsEndpoint(context, metricsRegistry, metricsCollector)
148
-
149
- // Start metrics exporters
150
- startMetricsExporters(context, metricsRegistry, metricsCollector)
151
-
152
- // Setup metrics cleanup
153
- setupMetricsCleanup(context, metricsRegistry)
215
+ setupMetricsEndpoint(context, metricsRegistry, metricsCollector, resolvedConfig)
216
+ startMetricsExporters(context, metricsRegistry, metricsCollector, resolvedConfig)
217
+ setupMetricsCleanup(context, metricsRegistry, resolvedConfig)
154
218
 
155
- // Setup alert monitoring
156
- if (DEFAULTS.alerts.length > 0) {
157
- setupAlertMonitoring(context, metricsRegistry)
219
+ if (resolvedConfig.alerts.length > 0) {
220
+ setupAlertMonitoring(context, metricsRegistry, resolvedConfig.alerts)
158
221
  }
159
222
 
160
223
  context.logger.info('Monitoring plugin initialized successfully')
161
224
  },
162
225
 
163
226
  onServerStart: async (context: PluginContext) => {
164
- if (DEFAULTS.enabled) {
227
+ const options = resolveMonitoringOptions(context)
228
+ if (options.enabled) {
165
229
  context.logger.info('Monitoring plugin: Server monitoring started', {
166
230
  pid: process.pid,
167
231
  nodeVersion: process.version,
@@ -172,14 +236,15 @@ export const monitoringPlugin: Plugin = {
172
236
  const metricsRegistry = (context as any).metricsRegistry as MetricsRegistry
173
237
  if (metricsRegistry) {
174
238
  recordCounter(metricsRegistry, 'server_starts_total', 1, {
175
- version: appConfig.version
239
+ version: appConfig.version ?? '1.0.0'
176
240
  })
177
241
  }
178
242
  }
179
243
  },
180
244
 
181
245
  onServerStop: async (context: PluginContext) => {
182
- if (DEFAULTS.enabled) {
246
+ const options = resolveMonitoringOptions(context)
247
+ if (options.enabled) {
183
248
  context.logger.info('Monitoring plugin: Server monitoring stopped')
184
249
 
185
250
  // Record server stop metric
@@ -268,14 +333,14 @@ export const monitoringPlugin: Plugin = {
268
333
  responseContext.size
269
334
  )
270
335
 
271
- // Check thresholds and log warnings
272
- if (DEFAULTS.thresholds.responseTime && duration > DEFAULTS.thresholds.responseTime) {
336
+ const options = resolveMonitoringOptions(responseContext)
337
+ if (options.thresholds.responseTime && duration > options.thresholds.responseTime) {
273
338
  const logger = (responseContext as any).logger || console
274
339
  logger.warn(`Slow request detected: ${responseContext.method} ${responseContext.path} took ${duration}ms`, {
275
340
  method: responseContext.method,
276
341
  path: responseContext.path,
277
342
  duration,
278
- threshold: DEFAULTS.thresholds.responseTime
343
+ threshold: options.thresholds.responseTime
279
344
  })
280
345
  }
281
346
  },
@@ -348,7 +413,7 @@ function initializeHttpMetrics(registry: MetricsRegistry, collector: MetricsColl
348
413
  collector.createHistogram('http_response_size_bytes', 'HTTP response size in bytes', [100, 1000, 10000, 100000, 1000000])
349
414
  }
350
415
 
351
- function startSystemMetricsCollection(context: PluginContext, collector: MetricsCollector) {
416
+ function startSystemMetricsCollection(context: PluginContext, collector: MetricsCollector, options: MonitoringOptions) {
352
417
  const intervals: NodeJS.Timeout[] = []
353
418
 
354
419
  // Initialize system metrics in collector
@@ -422,16 +487,15 @@ function startSystemMetricsCollection(context: PluginContext, collector: Metrics
422
487
 
423
488
  // Collect metrics immediately and then at intervals
424
489
  collectSystemMetrics()
425
- const interval = setInterval(collectSystemMetrics, DEFAULTS.collectInterval)
490
+ const interval = setInterval(collectSystemMetrics, options.collectInterval)
426
491
  intervals.push(interval)
427
492
 
428
493
  // Store intervals for cleanup
429
494
  ;(context as any).monitoringIntervals = intervals
430
495
  }
431
496
 
432
- function setupMetricsEndpoint(context: PluginContext, _registry: MetricsRegistry, collector: MetricsCollector) {
433
- // Find Prometheus exporter configuration
434
- const prometheusExporter = DEFAULTS.exporters.find((e: any) => e.type === 'prometheus' && e.enabled)
497
+ function setupMetricsEndpoint(context: PluginContext, _registry: MetricsRegistry, collector: MetricsCollector, options: MonitoringOptions) {
498
+ const prometheusExporter = options.exporters.find((e: any) => e.type === 'prometheus' && e.enabled)
435
499
  if (!prometheusExporter) return
436
500
 
437
501
  const endpoint = prometheusExporter.endpoint || '/metrics'
@@ -451,10 +515,10 @@ function setupMetricsEndpoint(context: PluginContext, _registry: MetricsRegistry
451
515
  }
452
516
  }
453
517
 
454
- function startMetricsExporters(context: PluginContext, registry: MetricsRegistry, collector: MetricsCollector) {
518
+ function startMetricsExporters(context: PluginContext, registry: MetricsRegistry, collector: MetricsCollector, options: MonitoringOptions) {
455
519
  const intervals: NodeJS.Timeout[] = (context as any).monitoringIntervals || []
456
520
 
457
- for (const exporterConfig of DEFAULTS.exporters) {
521
+ for (const exporterConfig of options.exporters) {
458
522
  if (!exporterConfig.enabled) continue
459
523
 
460
524
  const exportMetrics = () => {
@@ -492,11 +556,11 @@ function startMetricsExporters(context: PluginContext, registry: MetricsRegistry
492
556
  ;(context as any).monitoringIntervals = intervals
493
557
  }
494
558
 
495
- function setupAlertMonitoring(context: PluginContext, registry: MetricsRegistry) {
559
+ function setupAlertMonitoring(context: PluginContext, registry: MetricsRegistry, alerts: AlertThreshold[]) {
496
560
  const intervals: NodeJS.Timeout[] = (context as any).monitoringIntervals || []
497
561
 
498
562
  const checkAlerts = () => {
499
- for (const alert of DEFAULTS.alerts) {
563
+ for (const alert of alerts) {
500
564
  try {
501
565
  const metricValue = getMetricValue(registry, alert.metric)
502
566
  if (metricValue !== null && evaluateThreshold(metricValue, alert.operator, alert.value)) {
@@ -544,12 +608,12 @@ function setupAlertMonitoring(context: PluginContext, registry: MetricsRegistry)
544
608
  ;(context as any).monitoringIntervals = intervals
545
609
  }
546
610
 
547
- function setupMetricsCleanup(context: PluginContext, registry: MetricsRegistry) {
611
+ function setupMetricsCleanup(context: PluginContext, registry: MetricsRegistry, options: MonitoringOptions) {
548
612
  const intervals: NodeJS.Timeout[] = (context as any).monitoringIntervals || []
549
613
 
550
614
  const cleanup = () => {
551
615
  const now = Date.now()
552
- const cutoff = now - DEFAULTS.retentionPeriod
616
+ const cutoff = now - (options.retentionPeriod ?? 3600000)
553
617
 
554
618
  // Clean up old metrics
555
619
  for (const [key, metric] of registry.counters.entries()) {
@@ -815,4 +879,4 @@ export function formatPrometheusLabels(labels?: Record<string, string>): string
815
879
  return `{${labelPairs}}`
816
880
  }
817
881
 
818
- export default monitoringPlugin
882
+ export default monitoringPlugin
@@ -1,6 +1,6 @@
1
1
  import { join } from "path"
2
2
  import { statSync, existsSync } from "fs"
3
- import type { Plugin, PluginContext } from "@/core/plugins"
3
+ import type { Plugin, PluginContext } from "@core/plugins"
4
4
 
5
5
  // Default configuration values
6
6
  const DEFAULTS = {
@@ -1,62 +1,25 @@
1
1
  import { swagger } from '@elysiajs/swagger'
2
- import type { FluxStack, PluginContext } from '@/core/plugins/types'
3
- import { appConfig } from '@/config/app.config'
4
- import { serverConfig } from '@/config/server.config'
5
- import { pluginsConfig } from '@/config/plugins.config'
2
+ import type { FluxStack, PluginContext } from '@core/plugins/types'
3
+ import { appConfig, serverConfig, pluginsConfig } from '@config'
6
4
 
7
5
  type Plugin = FluxStack.Plugin
8
6
 
9
- /**
10
- * Parse servers from config string
11
- * Format: "url1|description1,url2|description2"
12
- */
13
- function parseServersFromConfig(serversString: string): Array<{ url: string; description: string }> {
14
- if (!serversString || serversString.trim() === '') {
15
- return []
16
- }
17
-
18
- return serversString.split(',').map(server => {
19
- const [url, description] = server.split('|').map(s => s.trim())
20
- return {
21
- url: url || '',
22
- description: description || 'Server'
23
- }
24
- }).filter(s => s.url !== '')
25
- }
26
-
27
- // Configuration from config/plugins.config.ts (user editable)
28
- const DEFAULTS = {
29
- enabled: pluginsConfig.swaggerEnabled,
30
- path: pluginsConfig.swaggerPath,
31
- title: pluginsConfig.swaggerTitle,
32
- description: pluginsConfig.swaggerDescription,
33
- version: pluginsConfig.swaggerVersion,
34
- excludePaths: pluginsConfig.swaggerExcludePaths,
35
- servers: parseServersFromConfig(pluginsConfig.swaggerServers),
36
-
37
- // Swagger UI options
38
- swaggerOptions: {
39
- persistAuthorization: pluginsConfig.swaggerPersistAuthorization,
40
- displayRequestDuration: pluginsConfig.swaggerDisplayRequestDuration,
41
- filter: pluginsConfig.swaggerEnableFilter,
42
- showExtensions: pluginsConfig.swaggerShowExtensions,
43
- tryItOutEnabled: pluginsConfig.swaggerTryItOutEnabled
44
- },
45
-
46
- // Authentication
47
- authEnabled: pluginsConfig.swaggerAuthEnabled,
48
- authUsername: pluginsConfig.swaggerAuthUsername,
49
- authPassword: pluginsConfig.swaggerAuthPassword,
7
+ /** Parse servers from config string format: "url1|desc1,url2|desc2" */
8
+ function parseServers(str: string): Array<{ url: string; description: string }> {
9
+ if (!str?.trim()) return []
50
10
 
51
- // Security (can be extended via env vars if needed)
52
- securitySchemes: {},
53
- globalSecurity: [] as Array<Record<string, any>>
11
+ return str.split(',')
12
+ .map(s => {
13
+ const [url, desc] = s.split('|').map(p => p.trim())
14
+ return { url: url || '', description: desc || 'Server' }
15
+ })
16
+ .filter(s => s.url)
54
17
  }
55
18
 
56
19
  export const swaggerPlugin: Plugin = {
57
20
  name: 'swagger',
58
21
  version: '1.0.0',
59
- description: 'Swagger documentation plugin for FluxStack with automatic tag discovery',
22
+ description: 'Swagger documentation plugin for FluxStack',
60
23
  author: 'FluxStack Team',
61
24
  priority: 500,
62
25
  category: 'documentation',
@@ -64,238 +27,78 @@ export const swaggerPlugin: Plugin = {
64
27
  dependencies: [],
65
28
 
66
29
  setup: async (context: PluginContext) => {
67
- if (!DEFAULTS.enabled) {
68
- context.logger.debug('Swagger plugin disabled by configuration')
30
+ if (!pluginsConfig.swaggerEnabled) {
31
+ context.logger.debug('Swagger plugin disabled')
69
32
  return
70
33
  }
71
34
 
72
- try {
73
- // Build servers list
74
- const servers = DEFAULTS.servers.length > 0 ? DEFAULTS.servers : [
75
- {
76
- url: `http://${serverConfig.server.host}:${serverConfig.server.port}`,
77
- description: 'Development server'
78
- }
79
- ]
80
-
81
- // Add production server if in production
82
- if (context.utils.isProduction()) {
83
- servers.push({
84
- url: 'https://api.example.com', // This would be configured
85
- description: 'Production server'
86
- })
87
- }
88
-
89
- // Simple Swagger configuration - all options from config/plugins.config.ts
90
- const swaggerConfig = {
91
- path: DEFAULTS.path,
92
- documentation: {
93
- info: {
94
- title: DEFAULTS.title || appConfig.name || 'FluxStack API',
95
- version: DEFAULTS.version || appConfig.version,
96
- description: DEFAULTS.description || appConfig.description || 'Modern full-stack TypeScript framework with type-safe API endpoints'
97
- },
98
- servers,
99
-
100
- // Add security schemes if defined
101
- ...(Object.keys(DEFAULTS.securitySchemes).length > 0 && {
102
- components: {
103
- securitySchemes: DEFAULTS.securitySchemes
104
- }
105
- }),
35
+ // Build servers list
36
+ const servers = parseServers(pluginsConfig.swaggerServers ?? '')
37
+ if (servers.length === 0) {
38
+ servers.push({
39
+ url: `http://${serverConfig.server.host}:${serverConfig.server.port}`,
40
+ description: 'Development server'
41
+ })
42
+ }
106
43
 
107
- // Add global security if defined
108
- ...(DEFAULTS.globalSecurity.length > 0 && {
109
- security: DEFAULTS.globalSecurity
110
- })
44
+ // Swagger configuration
45
+ const config = {
46
+ path: pluginsConfig.swaggerPath ?? '/swagger',
47
+ documentation: {
48
+ info: {
49
+ title: pluginsConfig.swaggerTitle ?? appConfig.name ?? 'FluxStack API',
50
+ version: pluginsConfig.swaggerVersion ?? appConfig.version ?? '1.0.0',
51
+ description: pluginsConfig.swaggerDescription ?? 'API documentation'
111
52
  },
112
- exclude: DEFAULTS.excludePaths,
113
- swaggerOptions: DEFAULTS.swaggerOptions
53
+ servers
54
+ },
55
+ exclude: pluginsConfig.swaggerExcludePaths ?? [],
56
+ swaggerOptions: {
57
+ persistAuthorization: pluginsConfig.swaggerPersistAuthorization,
58
+ displayRequestDuration: pluginsConfig.swaggerDisplayRequestDuration,
59
+ filter: pluginsConfig.swaggerEnableFilter,
60
+ showExtensions: pluginsConfig.swaggerShowExtensions,
61
+ tryItOutEnabled: pluginsConfig.swaggerTryItOutEnabled
114
62
  }
63
+ }
115
64
 
116
- // Add Basic Auth middleware if enabled
117
- if (DEFAULTS.authEnabled && DEFAULTS.authPassword) {
118
- context.app.onBeforeHandle({ as: 'global' }, ({ request, set, path }) => {
119
- // Only protect Swagger routes
120
- if (!path.startsWith(DEFAULTS.path)) {
121
- return
122
- }
123
-
124
- const authHeader = request.headers.get('authorization')
125
-
126
- if (!authHeader || !authHeader.startsWith('Basic ')) {
127
- set.status = 401
128
- set.headers['WWW-Authenticate'] = 'Basic realm="Swagger Documentation"'
129
- return {
130
- error: 'Authentication required',
131
- message: 'Please provide valid credentials to access Swagger documentation'
132
- }
133
- }
134
-
135
- // Decode Basic Auth credentials
136
- const base64Credentials = authHeader.substring(6)
137
- const credentials = Buffer.from(base64Credentials, 'base64').toString('utf-8')
138
- const [username, password] = credentials.split(':')
139
-
140
- // Validate credentials
141
- if (username !== DEFAULTS.authUsername || password !== DEFAULTS.authPassword) {
142
- set.status = 401
143
- set.headers['WWW-Authenticate'] = 'Basic realm="Swagger Documentation"'
144
- return {
145
- error: 'Invalid credentials',
146
- message: 'The provided username or password is incorrect'
147
- }
148
- }
149
-
150
- // Credentials valid, continue to Swagger
151
- })
65
+ // Add Basic Auth if enabled
66
+ if (pluginsConfig.swaggerAuthEnabled && pluginsConfig.swaggerAuthPassword) {
67
+ context.app.onBeforeHandle({ as: 'global' }, ({ request, set, path }: { request: Request; set: any; path: string }) => {
68
+ if (!path.startsWith(pluginsConfig.swaggerPath ?? '/swagger')) return
152
69
 
153
- context.logger.debug(`🔒 Swagger authentication enabled (username: ${DEFAULTS.authUsername})`)
154
- }
70
+ const authHeader = request.headers.get('authorization')
71
+ if (!authHeader?.startsWith('Basic ')) {
72
+ set.status = 401
73
+ set.headers['WWW-Authenticate'] = 'Basic realm="Swagger"'
74
+ return { error: 'Authentication required' }
75
+ }
155
76
 
156
- // That's it! Elysia Swagger auto-discovers everything else
157
- context.app.use(swagger(swaggerConfig))
77
+ const [username, password] = Buffer.from(authHeader.substring(6), 'base64')
78
+ .toString('utf-8')
79
+ .split(':')
158
80
 
159
- context.logger.debug(`Swagger documentation enabled at ${DEFAULTS.path}`, {
160
- title: swaggerConfig.documentation.info.title,
161
- version: swaggerConfig.documentation.info.version,
162
- servers: servers.length,
163
- authEnabled: DEFAULTS.authEnabled
81
+ if (username !== pluginsConfig.swaggerAuthUsername ||
82
+ password !== pluginsConfig.swaggerAuthPassword) {
83
+ set.status = 401
84
+ set.headers['WWW-Authenticate'] = 'Basic realm="Swagger"'
85
+ return { error: 'Invalid credentials' }
86
+ }
164
87
  })
165
- } catch (error) {
166
- context.logger.error('Failed to setup Swagger plugin', { error })
167
- throw error
88
+
89
+ context.logger.debug(`Swagger auth enabled (user: ${pluginsConfig.swaggerAuthUsername})`)
168
90
  }
91
+
92
+ context.app.use(swagger(config))
93
+ context.logger.debug(`Swagger enabled at ${pluginsConfig.swaggerPath}`)
169
94
  },
170
95
 
171
96
  onServerStart: async (context: PluginContext) => {
172
- if (DEFAULTS.enabled) {
173
- const swaggerUrl = `http://${serverConfig.server.host}:${serverConfig.server.port}${DEFAULTS.path}`
174
- context.logger.debug(`📋 Swagger documentation available at: ${swaggerUrl}`)
175
- }
97
+ if (!pluginsConfig.swaggerEnabled) return
98
+
99
+ const url = `http://${serverConfig.server.host}:${serverConfig.server.port}${pluginsConfig.swaggerPath}`
100
+ context.logger.debug(`Swagger available at: ${url}`)
176
101
  }
177
102
  }
178
103
 
179
- // ==========================================
180
- // ⚙️ SWAGGER CONFIGURATION
181
- // ==========================================
182
- //
183
- // All Swagger options are configurable via:
184
- // 📁 config/plugins.config.ts (user editable)
185
- // 🌍 Environment variables (.env file)
186
- //
187
- // Available configurations:
188
- //
189
- // 1️⃣ Basic Settings:
190
- // - SWAGGER_ENABLED (default: true)
191
- // - SWAGGER_TITLE (default: 'FluxStack API')
192
- // - SWAGGER_VERSION (default: app version)
193
- // - SWAGGER_DESCRIPTION (default: 'API documentation...')
194
- // - SWAGGER_PATH (default: '/swagger')
195
- //
196
- // 2️⃣ Advanced Options:
197
- // - SWAGGER_EXCLUDE_PATHS (array, default: [])
198
- // - SWAGGER_SERVERS (format: "url1|desc1,url2|desc2")
199
- // Example: "https://api.prod.com|Production,https://staging.com|Staging"
200
- //
201
- // 3️⃣ Swagger UI Options:
202
- // - SWAGGER_PERSIST_AUTH (default: true)
203
- // - SWAGGER_DISPLAY_DURATION (default: true)
204
- // - SWAGGER_ENABLE_FILTER (default: true)
205
- // - SWAGGER_SHOW_EXTENSIONS (default: true)
206
- // - SWAGGER_TRY_IT_OUT (default: true)
207
- //
208
- // 4️⃣ Authentication (Basic Auth):
209
- // - SWAGGER_AUTH_ENABLED (default: false)
210
- // - SWAGGER_AUTH_USERNAME (default: 'admin')
211
- // - SWAGGER_AUTH_PASSWORD (required if auth enabled)
212
- //
213
- // Example .env configuration:
214
- // ```
215
- // SWAGGER_ENABLED=true
216
- // SWAGGER_TITLE=My API
217
- // SWAGGER_PATH=/api-docs
218
- // SWAGGER_SERVERS=https://api.myapp.com|Production,https://staging.myapp.com|Staging
219
- // SWAGGER_EXCLUDE_PATHS=/internal,/admin
220
- //
221
- // # Protect Swagger with Basic Auth
222
- // SWAGGER_AUTH_ENABLED=true
223
- // SWAGGER_AUTH_USERNAME=admin
224
- // SWAGGER_AUTH_PASSWORD=super-secret-password
225
- // ```
226
- //
227
- // 🔒 When authentication is enabled:
228
- // - Browser will prompt for username/password
229
- // - All Swagger routes are protected (UI + JSON spec)
230
- // - Credentials are validated using Basic Auth
231
- // - Perfect for staging/production environments
232
- //
233
- // ==========================================
234
- // 🏷️ AUTOMATIC TAG DISCOVERY
235
- // ==========================================
236
- //
237
- // ✨ 100% AUTOMATIC - Just add tags to your routes!
238
- //
239
- // Example 1 - Simple route:
240
- // app.get('/products', handler, {
241
- // detail: {
242
- // summary: 'Get all products',
243
- // tags: ['Products', 'Catalog'] // ✅ Auto-discovered!
244
- // }
245
- // })
246
- //
247
- // Example 2 - Grouped routes:
248
- // export const ordersRoutes = new Elysia({ prefix: '/orders', tags: ['Orders'] })
249
- // .get('/', handler, {
250
- // detail: {
251
- // summary: 'List orders',
252
- // tags: ['Orders', 'Management'] // ✅ Auto-discovered!
253
- // }
254
- // })
255
- //
256
- // Elysia Swagger automatically:
257
- // - Discovers all tags from routes
258
- // - Organizes endpoints by tag
259
- // - Generates OpenAPI spec
260
- // - Creates interactive UI
261
- //
262
- // ==========================================
263
- // 🔐 SECURITY CONFIGURATION
264
- // ==========================================
265
- //
266
- // To enable security in your FluxStack app, configure like this:
267
- //
268
- // plugins: {
269
- // config: {
270
- // swagger: {
271
- // securitySchemes: {
272
- // bearerAuth: {
273
- // type: 'http',
274
- // scheme: 'bearer',
275
- // bearerFormat: 'JWT'
276
- // },
277
- // apiKeyAuth: {
278
- // type: 'apiKey',
279
- // in: 'header',
280
- // name: 'X-API-Key'
281
- // }
282
- // },
283
- // globalSecurity: [
284
- // { bearerAuth: [] } // Apply JWT auth globally
285
- // ]
286
- // }
287
- // }
288
- // }
289
- //
290
- // Then in your routes, you can override per endpoint:
291
- // app.get('/public', handler, {
292
- // detail: { security: [] } // No auth required
293
- // })
294
- //
295
- // app.get('/private', handler, {
296
- // detail: {
297
- // security: [{ apiKeyAuth: [] }] // API key required
298
- // }
299
- // })
300
-
301
104
  export default swaggerPlugin