create-fluxstack 1.10.1 → 1.12.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 (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 +161 -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 +127 -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,12 +1,12 @@
1
1
  import type { FluxStack, PluginManifest, PluginLoadResult, PluginDiscoveryOptions } from "./types"
2
2
 
3
3
  type FluxStackPlugin = FluxStack.Plugin
4
- import type { FluxStackConfig } from "@/core/config/schema"
5
- import type { Logger } from "@/core/utils/logger"
6
- import { FluxStackError } from "@/core/utils/errors"
4
+ import type { FluxStackConfig } from "@config"
5
+ import type { Logger } from "@core/utils/logger"
6
+ import { FluxStackError } from "@core/utils/errors"
7
7
  import { PluginDependencyManager } from "./dependency-manager"
8
8
  import { readdir, readFile } from "fs/promises"
9
- import { join, resolve } from "path"
9
+ import { join, resolve, sep } from "path"
10
10
  import { existsSync } from "fs"
11
11
 
12
12
  export interface PluginRegistryConfig {
@@ -51,8 +51,9 @@ export class PluginRegistry {
51
51
  this.validatePlugin(plugin)
52
52
 
53
53
  // Validate plugin configuration if schema is provided
54
- if (plugin.configSchema && this.config?.plugins.config[plugin.name]) {
55
- this.validatePluginConfig(plugin, this.config.plugins.config[plugin.name])
54
+ const pluginConfigs = this.config?.plugins.config as Record<string, unknown> | undefined
55
+ if (plugin.configSchema && pluginConfigs?.[plugin.name]) {
56
+ this.validatePluginConfig(plugin, pluginConfigs[plugin.name])
56
57
  }
57
58
 
58
59
  this.plugins.set(plugin.name, plugin)
@@ -222,14 +223,84 @@ export class PluginRegistry {
222
223
  return this.plugins.has(name)
223
224
  }
224
225
 
226
+ /**
227
+ * Check which dependencies are missing from main package.json
228
+ */
229
+ private checkMissingDependencies(pluginDeps: Record<string, string>): string[] {
230
+ try {
231
+ const mainPackageJsonPath = join(process.cwd(), 'package.json')
232
+ if (!existsSync(mainPackageJsonPath)) {
233
+ return Object.keys(pluginDeps)
234
+ }
235
+
236
+ const mainPackageJson = JSON.parse(
237
+ require('fs').readFileSync(mainPackageJsonPath, 'utf-8')
238
+ )
239
+
240
+ const allDeps = {
241
+ ...mainPackageJson.dependencies,
242
+ ...mainPackageJson.devDependencies
243
+ }
244
+
245
+ return Object.keys(pluginDeps).filter(dep => !allDeps[dep])
246
+ } catch (error) {
247
+ // If we can't read package.json, assume all deps are missing
248
+ return Object.keys(pluginDeps)
249
+ }
250
+ }
251
+
252
+ /**
253
+ * 🔒 Check if a plugin is allowed to be loaded (whitelist check)
254
+ *
255
+ * @param pluginName - Name of the plugin (e.g., "fluxstack-plugin-auth", "@acme/fplugin-payments")
256
+ * @param isNpmPlugin - Whether this is an npm plugin (requires whitelist) or project plugin (trusted)
257
+ * @returns true if plugin is allowed, false otherwise
258
+ */
259
+ /**
260
+ * Check if a plugin is allowed to be loaded (whitelist enforcement)
261
+ *
262
+ * Security model:
263
+ * - Project plugins (plugins/) are ALWAYS trusted (developer put them there)
264
+ * - NPM plugins (node_modules/) REQUIRE whitelist (supply chain protection)
265
+ */
266
+ private isPluginAllowed(pluginName: string, source: 'npm' | 'project'): boolean {
267
+ const allowedPlugins = this.config?.plugins.allowedPlugins || []
268
+
269
+ // Project plugins are always trusted - developer explicitly added them
270
+ if (source === 'project') {
271
+ if (!this.config?.plugins.discoverProjectPlugins) {
272
+ this.logger?.debug(`Project plugin '${pluginName}' skipped: discovery disabled`)
273
+ return false
274
+ }
275
+
276
+ // ✅ Project plugins bypass whitelist - they're trusted by design
277
+ this.logger?.debug(`Project plugin '${pluginName}' allowed (trusted source)`)
278
+ return true
279
+ }
280
+
281
+ if (allowedPlugins.length === 0) {
282
+ this.logger?.warn(`NPM plugin '${pluginName}' blocked: No plugins in whitelist (PLUGINS_ALLOWED is empty)`)
283
+ return false
284
+ }
285
+
286
+ if (!allowedPlugins.includes(pluginName)) {
287
+ this.logger?.warn(`NPM plugin '${pluginName}' blocked: Not in whitelist (PLUGINS_ALLOWED)`, {
288
+ pluginName,
289
+ allowedPlugins
290
+ })
291
+ return false
292
+ }
293
+
294
+ return true
295
+ }
225
296
  /**
226
297
  * Get registry statistics
227
298
  */
228
299
  getStats() {
229
300
  return {
230
301
  totalPlugins: this.plugins.size,
231
- enabledPlugins: this.config?.plugins.enabled.length || 0,
232
- disabledPlugins: this.config?.plugins.disabled.length || 0,
302
+ enabledPlugins: this.config?.plugins.enabled?.length ?? 0,
303
+ disabledPlugins: this.config?.plugins.disabled?.length ?? 0,
233
304
  conflicts: this.conflicts.length,
234
305
  loadOrder: this.loadOrder.length
235
306
  }
@@ -263,8 +334,136 @@ export class PluginRegistry {
263
334
  }
264
335
  }
265
336
 
337
+ /**
338
+ * Discover FluxStack plugins from node_modules
339
+ * Looks for packages with naming pattern:
340
+ * - fluxstack-plugin-*
341
+ * - fplugin-*
342
+ * - @fluxstack/plugin-*
343
+ * - @fplugin/*
344
+ * - @org/fluxstack-plugin-*
345
+ * - @org/fplugin-*
346
+ *
347
+ * 🔒 SECURITY: Respects config.plugins.discoverNpmPlugins and config.plugins.allowedPlugins
348
+ */
349
+ async discoverNpmPlugins(): Promise<PluginLoadResult[]> {
350
+ const results: PluginLoadResult[] = []
351
+ const nodeModulesDir = 'node_modules'
352
+
353
+ // 🔒 Check if npm plugin discovery is enabled
354
+ if (!this.config?.plugins.discoverNpmPlugins) {
355
+ this.logger?.debug('NPM plugin discovery is disabled (PLUGINS_DISCOVER_NPM=false)')
356
+ return results
357
+ }
358
+
359
+ if (!existsSync(nodeModulesDir)) {
360
+ this.logger?.debug('node_modules directory not found')
361
+ return results
362
+ }
363
+
364
+ try {
365
+ const entries = await readdir(nodeModulesDir, { withFileTypes: true })
366
+
367
+ for (const entry of entries) {
368
+ if (entry.isDirectory()) {
369
+ // Check scoped packages (@org/package)
370
+ if (entry.name.startsWith('@')) {
371
+ const scopeDir = join(nodeModulesDir, entry.name)
372
+ const scopedEntries = await readdir(scopeDir, { withFileTypes: true })
373
+
374
+ for (const scopedEntry of scopedEntries) {
375
+ if (scopedEntry.isDirectory()) {
376
+ const packageName = `${entry.name}/${scopedEntry.name}`
377
+ let isFluxStackPlugin = false
378
+
379
+ // Match patterns:
380
+ // @fluxstack/plugin-*
381
+ if (entry.name === '@fluxstack' && scopedEntry.name.startsWith('plugin-')) {
382
+ isFluxStackPlugin = true
383
+ }
384
+ // @fplugin/*
385
+ else if (entry.name === '@fplugin') {
386
+ isFluxStackPlugin = true
387
+ }
388
+ // @org/fluxstack-plugin-*
389
+ else if (scopedEntry.name.startsWith('fluxstack-plugin-')) {
390
+ isFluxStackPlugin = true
391
+ }
392
+ // @org/fplugin-*
393
+ else if (scopedEntry.name.startsWith('fplugin-')) {
394
+ isFluxStackPlugin = true
395
+ }
396
+
397
+ if (isFluxStackPlugin) {
398
+ // 🔒 Security check: Verify plugin is in whitelist
399
+ if (!this.isPluginAllowed(packageName, 'npm')) {
400
+ this.logger?.debug(`Skipping npm plugin (not in whitelist): ${packageName}`)
401
+ results.push({
402
+ success: false,
403
+ error: `Plugin '${packageName}' is not in the allowed plugins whitelist (PLUGINS_ALLOWED)`
404
+ })
405
+ continue
406
+ }
407
+
408
+ const pluginPath = join(scopeDir, scopedEntry.name)
409
+ this.logger?.debug(`Loading whitelisted npm plugin: ${packageName}`)
410
+
411
+ const result = await this.loadPlugin(pluginPath)
412
+ results.push(result)
413
+ }
414
+ }
415
+ }
416
+ }
417
+ // Check non-scoped packages
418
+ else if (
419
+ entry.name.startsWith('fluxstack-plugin-') ||
420
+ entry.name.startsWith('fplugin-')
421
+ ) {
422
+ // 🔒 Security check: Verify plugin is in whitelist
423
+ if (!this.isPluginAllowed(entry.name, 'npm')) {
424
+ this.logger?.debug(`Skipping npm plugin (not in whitelist): ${entry.name}`)
425
+ results.push({
426
+ success: false,
427
+ error: `Plugin '${entry.name}' is not in the allowed plugins whitelist (PLUGINS_ALLOWED)`
428
+ })
429
+ continue
430
+ }
431
+
432
+ const pluginPath = join(nodeModulesDir, entry.name)
433
+ this.logger?.debug(`Loading whitelisted npm plugin: ${entry.name}`)
434
+
435
+ const result = await this.loadPlugin(pluginPath)
436
+ results.push(result)
437
+ }
438
+ }
439
+ }
440
+
441
+ // 🔒 Security summary
442
+ const successful = results.filter(r => r.success).length
443
+ const blocked = results.filter(r => !r.success && r.error?.includes('whitelist')).length
444
+ const failed = results.filter(r => !r.success && !r.error?.includes('whitelist')).length
445
+
446
+ if (blocked > 0) {
447
+ this.logger?.warn(`🔒 Security: Blocked ${blocked} npm plugin(s) not in whitelist (PLUGINS_ALLOWED)`)
448
+ }
449
+
450
+ this.logger?.info(`Discovered ${successful} allowed npm plugin(s)`, {
451
+ total: results.length,
452
+ successful,
453
+ blocked,
454
+ failed
455
+ })
456
+ } catch (error) {
457
+ this.logger?.error('Failed to discover npm plugins', { error })
458
+ }
459
+
460
+ return results
461
+ }
462
+
266
463
  /**
267
464
  * Discover plugins from filesystem
465
+ *
466
+ * 🔒 SECURITY: Respects config.plugins.discoverProjectPlugins for project plugins
268
467
  */
269
468
  async discoverPlugins(options: PluginDiscoveryOptions = {}): Promise<PluginLoadResult[]> {
270
469
  const results: PluginLoadResult[] = []
@@ -275,6 +474,12 @@ export class PluginRegistry {
275
474
  includeExternal: _includeExternal = true
276
475
  } = options
277
476
 
477
+ // 🔒 Check if project plugin discovery is enabled
478
+ if (!this.config?.plugins.discoverProjectPlugins) {
479
+ this.logger?.debug('Project plugin discovery is disabled (PLUGINS_DISCOVER_PROJECT=false)')
480
+ return results
481
+ }
482
+
278
483
  // Descobrir plugins
279
484
  for (const directory of directories) {
280
485
  this.logger?.debug(`Scanning directory: ${directory}`)
@@ -286,7 +491,20 @@ export class PluginRegistry {
286
491
  try {
287
492
  const pluginResults = await this.discoverPluginsInDirectory(directory, _patterns)
288
493
  this.logger?.debug(`Found ${pluginResults.length} plugins in ${directory}`)
289
- results.push(...pluginResults)
494
+
495
+ for (const pluginResult of pluginResults) {
496
+ if (pluginResult.success && pluginResult.plugin) {
497
+ if (!this.isPluginAllowed(pluginResult.plugin.name, 'project')) {
498
+ results.push({
499
+ success: false,
500
+ error: `Plugin '${pluginResult.plugin.name}' is not in the allowed plugins whitelist (PLUGINS_ALLOWED)`
501
+ })
502
+ continue
503
+ }
504
+ }
505
+
506
+ results.push(pluginResult)
507
+ }
290
508
  } catch (error) {
291
509
  this.logger?.warn(`Failed to discover plugins in directory '${directory}'`, { error })
292
510
  results.push({
@@ -343,17 +561,31 @@ export class PluginRegistry {
343
561
  }
344
562
  }
345
563
 
346
- // Install dependencies BEFORE importing the plugin
564
+ // Check and install plugin dependencies
347
565
  if (manifest && manifest.dependencies && Object.keys(manifest.dependencies).length > 0) {
348
- try {
349
- const resolution = await this.dependencyManager.resolvePluginDependencies(pluginPath)
350
- if (resolution.dependencies.length > 0) {
351
- // Install dependencies directly in the plugin directory
352
- await this.dependencyManager.installPluginDependenciesLocally(pluginPath, resolution.dependencies)
353
- this.logger?.debug(`Dependencies installed for plugin at: ${pluginPath}`)
566
+ const isProjectPlugin = pluginPath.includes('plugins' + sep)
567
+
568
+ if (isProjectPlugin) {
569
+ // Install dependencies locally in plugin directory
570
+ this.logger?.debug(
571
+ `Installing dependencies for plugin '${manifest.name}' in ${pluginPath}`,
572
+ { dependencies: Object.keys(manifest.dependencies).length }
573
+ )
574
+
575
+ try {
576
+ await this.dependencyManager.installDependenciesInPath(
577
+ pluginPath,
578
+ manifest.dependencies
579
+ )
580
+ } catch (error) {
581
+ this.logger?.warn(
582
+ `Failed to install dependencies for plugin '${manifest.name}'. ` +
583
+ `You can install manually with: cd ${pluginPath} && bun install`
584
+ )
354
585
  }
355
- } catch (error) {
356
- this.logger?.warn(`Failed to install dependencies for plugin at '${pluginPath}'`, { error })
586
+ } else {
587
+ // NPM plugins always show warning
588
+ this.logger?.warn(`Plugin '${manifest.name}' declares dependencies. Run 'bun run flux plugin:deps install ${manifest.name}' to review and install them manually.`)
357
589
  }
358
590
  }
359
591
 
@@ -582,4 +814,8 @@ export class PluginRegistry {
582
814
 
583
815
  return results
584
816
  }
585
- }
817
+ }
818
+
819
+
820
+
821
+
@@ -1,5 +1,5 @@
1
- import type { FluxStackConfig } from "@/core/config/schema"
2
- import type { Logger } from "@/core/utils/logger/index"
1
+ import type { FluxStackConfig } from "@config"
2
+ import type { Logger } from "@core/utils/logger/index"
3
3
 
4
4
  export type PluginHook =
5
5
  // Lifecycle hooks
@@ -190,7 +190,7 @@ export namespace FluxStack {
190
190
  * @example
191
191
  * // ✅ New way (recommended):
192
192
  * // plugins/my-plugin/config/index.ts
193
- * import { defineConfig, config } from '@/core/utils/config-schema'
193
+ * import { defineConfig, config } from '@core/utils/config-schema'
194
194
  * export const myConfig = defineConfig({ ... })
195
195
  *
196
196
  * // ❌ Old way (deprecated):
@@ -357,55 +357,9 @@ export interface CliContext {
357
357
  }
358
358
  }
359
359
 
360
- // Live Components Types
361
- export interface LiveComponent<TState = any> {
362
- id: string
363
- name: string
364
- state: TState
365
- mounted: boolean
366
- socket?: any
367
- userId?: string
368
- destroy?: () => void
369
- executeAction?: (action: string, payload?: any) => Promise<any>
370
- setState?: (newState: Partial<TState>) => void
371
- getSerializableState?: () => TState
372
- }
373
-
374
- export interface LiveMessage {
375
- type: string
376
- componentId: string
377
- data?: any
378
- payload?: any
379
- action?: string
380
- property?: string
381
- userId?: string
382
- expectResponse?: boolean
383
- timestamp?: number
384
- requestId?: string
385
- room?: string
386
- }
387
-
388
- export interface BroadcastMessage {
389
- type: string
390
- data?: any
391
- channel?: string
392
- room?: string
393
- payload?: any
394
- excludeUser?: string
395
- }
396
-
397
- export interface ComponentDefinition<TState = any> {
398
- name: string
399
- initialState: TState
400
- handlers?: Record<string, Function>
401
- component?: any
402
- }
403
-
404
- export interface WebSocketData {
405
- componentId?: string
406
- userId?: string
407
- sessionId?: string
408
- }
360
+ // Live Components Types - Re-exported from core/types/types.ts
361
+ // See core/types/types.ts for full implementation
362
+ // Note: These are re-exported at the end of this file
409
363
 
410
364
  // File Upload Types
411
365
  export interface ActiveUpload {
@@ -477,4 +431,17 @@ export interface FileUploadCompleteResponse {
477
431
  }
478
432
 
479
433
  // Plugin Type Export
480
- export type Plugin = FluxStack.Plugin
434
+ export type Plugin = FluxStack.Plugin
435
+
436
+ // Re-export all WebSocket and LiveComponent types from core/types/types.ts
437
+ export {
438
+ LiveComponent,
439
+ type FluxStackWebSocket,
440
+ type FluxStackWSData,
441
+ type FluxStackServerWebSocket,
442
+ type LiveMessage,
443
+ type BroadcastMessage,
444
+ type ComponentState,
445
+ type ComponentDefinition,
446
+ type WebSocketData
447
+ } from '@core/types/types'
@@ -1,8 +1,10 @@
1
1
  import { Elysia } from "elysia"
2
+ import { createHash as nodeCreateHash } from "crypto"
2
3
  import type { FluxStackConfig, FluxStackContext, Plugin } from "../types"
3
4
  import type { PluginContext, PluginUtils } from "../plugins/types"
4
5
  import { PluginManager } from "../plugins/manager"
5
- import { getConfigSync, getEnvironmentInfo } from "../config"
6
+ import { fluxStackConfig } from "@config"
7
+ import { getEnvironmentInfo } from "../config"
6
8
  import { logger, type Logger } from "../utils/logger/index"
7
9
  import { createTimer, formatBytes, isProduction, isDevelopment } from "../utils/helpers"
8
10
 
@@ -12,11 +14,11 @@ export class FluxStackFramework {
12
14
  private pluginContext: PluginContext
13
15
  private plugins: Plugin[] = []
14
16
  private pluginManager: PluginManager
17
+ private initialized = false
15
18
 
16
19
  constructor(config?: Partial<FluxStackConfig>) {
17
- console.log('🚀 [DEBUG] FluxStackFramework constructor called!')
18
20
  // Load the full configuration
19
- const fullConfig = config ? { ...getConfigSync(), ...config } : getConfigSync()
21
+ const fullConfig = config ? { ...fluxStackConfig, ...config } : fluxStackConfig
20
22
  const envInfo = getEnvironmentInfo()
21
23
 
22
24
  this.context = {
@@ -36,10 +38,7 @@ export class FluxStackFramework {
36
38
  isProduction,
37
39
  isDevelopment,
38
40
  getEnvironment: () => envInfo.name,
39
- createHash: (data: string) => {
40
- const crypto = require('crypto')
41
- return crypto.createHash('sha256').update(data).digest('hex')
42
- },
41
+ createHash: (data: string) => nodeCreateHash('sha256').update(data).digest('hex'),
43
42
  deepMerge: (target: any, source: any) => {
44
43
  const result = { ...target }
45
44
  for (const key in source) {
@@ -78,49 +77,50 @@ export class FluxStackFramework {
78
77
  })
79
78
 
80
79
  this.setupCors()
81
-
82
- console.log('🔍 [DEBUG] About to call initializePluginsAsync()...')
83
- // Initialize plugins automatically in the background
84
- this.initializePluginsAsync().catch(error => {
85
- console.error('❌ [DEBUG] Failed to initialize plugins async:', error)
86
- })
87
- console.log('🔍 [DEBUG] initializePluginsAsync() call dispatched')
88
- }
89
-
90
- private async initializePluginsAsync() {
91
- console.log('🚀 [DEBUG] initializePluginsAsync() CALLED!')
92
- try {
93
- console.log('🔍 [DEBUG] Starting automatic plugin discovery...')
94
- console.log('🔍 [DEBUG] Current working directory:', process.cwd())
95
- logger.info('[FluxStack] Initializing automatic plugin discovery...')
96
- await this.pluginManager.initialize()
97
- const stats = this.pluginManager.getRegistry().getStats()
98
- console.log('🔍 [DEBUG] Plugin discovery completed:', stats)
99
- logger.info('[FluxStack] Automatic plugins loaded successfully', {
100
- pluginCount: stats.totalPlugins,
101
- enabledPlugins: stats.enabledPlugins,
102
- disabledPlugins: stats.disabledPlugins
103
- })
104
- } catch (error) {
105
- console.error('❌ [DEBUG] Plugin discovery error:', error)
106
- logger.error('[FluxStack] Failed to initialize automatic plugins', { error })
107
- }
108
80
  }
109
81
 
110
82
  private setupCors() {
111
- const { cors } = this.context.config.server
83
+ const cors = this.context.config.cors
84
+
85
+ const allowedOrigins = cors.origins.length > 0 ? cors.origins : ['*']
86
+ const allowAllMethods = cors.methods.length > 0 ? cors.methods.join(", ") : "GET,POST,PUT,DELETE,OPTIONS"
87
+ const allowAllHeaders = cors.headers.length > 0 ? cors.headers.join(", ") : "Content-Type, Authorization"
88
+
89
+ const resolveOrigin = (requestOrigin?: string | null) => {
90
+ const origin = requestOrigin || allowedOrigins[0] || "*"
91
+ if (allowedOrigins.includes("*")) {
92
+ return cors.credentials ? origin : "*"
93
+ }
94
+ return allowedOrigins.includes(origin) ? origin : allowedOrigins[0] || origin
95
+ }
112
96
 
113
97
  this.app
114
- .onRequest(({ set }) => {
115
- set.headers["Access-Control-Allow-Origin"] = cors.origins.join(", ") || "*"
116
- set.headers["Access-Control-Allow-Methods"] = cors.methods.join(", ") || "*"
117
- set.headers["Access-Control-Allow-Headers"] = cors.headers.join(", ") || "*"
98
+ .onRequest(({ request, set }) => {
99
+ const origin = resolveOrigin(request.headers.get("origin"))
100
+ set.headers["Access-Control-Allow-Origin"] = origin
101
+ set.headers["Access-Control-Allow-Methods"] = allowAllMethods
102
+ set.headers["Access-Control-Allow-Headers"] = allowAllHeaders
103
+ set.headers["Vary"] = "Origin"
118
104
  if (cors.credentials) {
119
105
  set.headers["Access-Control-Allow-Credentials"] = "true"
120
106
  }
107
+ if (cors.maxAge) {
108
+ set.headers["Access-Control-Max-Age"] = cors.maxAge.toString()
109
+ }
121
110
  })
122
- .options("*", ({ set }) => {
123
- set.status = 200
111
+ .options("*", ({ request, set }) => {
112
+ set.status = 204
113
+ const origin = resolveOrigin(request.headers.get("origin"))
114
+ set.headers["Access-Control-Allow-Origin"] = origin
115
+ set.headers["Access-Control-Allow-Methods"] = allowAllMethods
116
+ set.headers["Access-Control-Allow-Headers"] = allowAllHeaders
117
+ set.headers["Vary"] = "Origin"
118
+ if (cors.credentials) {
119
+ set.headers["Access-Control-Allow-Credentials"] = "true"
120
+ }
121
+ if (cors.maxAge) {
122
+ set.headers["Access-Control-Max-Age"] = cors.maxAge.toString()
123
+ }
124
124
  return ""
125
125
  })
126
126
  }
@@ -146,7 +146,28 @@ export class FluxStackFramework {
146
146
  return this.context
147
147
  }
148
148
 
149
- listen(callback?: () => void) {
149
+ async listen(callback?: () => void) {
150
+ // Initialize plugins synchronously before starting server
151
+ if (!this.initialized) {
152
+ logger.debug('[FluxStack] Initializing automatic plugin discovery...')
153
+
154
+ try {
155
+ await this.pluginManager.initialize()
156
+ const stats = this.pluginManager.getRegistry().getStats()
157
+
158
+ logger.info('[FluxStack] Automatic plugins loaded successfully', {
159
+ pluginCount: stats.totalPlugins,
160
+ enabledPlugins: stats.enabledPlugins,
161
+ disabledPlugins: stats.disabledPlugins
162
+ })
163
+
164
+ this.initialized = true
165
+ } catch (error) {
166
+ logger.error('[FluxStack] Failed to initialize automatic plugins', { error })
167
+ throw error
168
+ }
169
+ }
170
+
150
171
  const port = this.context.config.server.port
151
172
  const apiPrefix = this.context.config.server.apiPrefix
152
173
 
@@ -162,5 +183,7 @@ export class FluxStackFramework {
162
183
  console.log()
163
184
  callback?.()
164
185
  })
186
+
187
+ return this
165
188
  }
166
- }
189
+ }
@@ -1,16 +1,16 @@
1
- // FluxStack framework exports
2
- export { FluxStackFramework } from "../framework/server"
3
- export { vitePlugin, staticPlugin } from "../plugins/built-in"
4
- export { swaggerPlugin } from "../plugins/built-in/swagger"
5
- export { PluginRegistry } from "../plugins/registry"
6
- export * from "../types"
7
-
8
- // Live Components exports
9
- export { liveComponentsPlugin } from "./live/websocket-plugin"
10
- export { componentRegistry } from "./live/ComponentRegistry"
11
- export { LiveComponent } from "../types/types"
12
-
13
- // Static Files Plugin
14
- export { staticFilesPlugin } from "./plugins/static-files-plugin"
15
-
1
+ // FluxStack framework exports
2
+ export { FluxStackFramework } from "../framework/server"
3
+ export { vitePlugin, staticPlugin } from "../plugins/built-in"
4
+ export { swaggerPlugin } from "../plugins/built-in/swagger"
5
+ export { PluginRegistry } from "../plugins/registry"
6
+ export * from "../types"
7
+
8
+ // Live Components exports
9
+ export { liveComponentsPlugin } from "./live/websocket-plugin"
10
+ export { componentRegistry } from "./live/ComponentRegistry"
11
+ export { LiveComponent } from "../types/types"
12
+
13
+ // Static Files Plugin
14
+ export { staticFilesPlugin } from "./plugins/static-files-plugin"
15
+
16
16
  export * from "../types/types"