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.
- package/.dockerignore +1 -2
- package/Dockerfile +8 -8
- package/LLMD/INDEX.md +64 -0
- package/LLMD/MAINTENANCE.md +197 -0
- package/LLMD/MIGRATION.md +156 -0
- package/LLMD/config/.gitkeep +1 -0
- package/LLMD/config/declarative-system.md +268 -0
- package/LLMD/config/environment-vars.md +327 -0
- package/LLMD/config/runtime-reload.md +401 -0
- package/LLMD/core/.gitkeep +1 -0
- package/LLMD/core/build-system.md +599 -0
- package/LLMD/core/framework-lifecycle.md +229 -0
- package/LLMD/core/plugin-system.md +451 -0
- package/LLMD/patterns/.gitkeep +1 -0
- package/LLMD/patterns/anti-patterns.md +297 -0
- package/LLMD/patterns/project-structure.md +264 -0
- package/LLMD/patterns/type-safety.md +440 -0
- package/LLMD/reference/.gitkeep +1 -0
- package/LLMD/reference/cli-commands.md +250 -0
- package/LLMD/reference/plugin-hooks.md +357 -0
- package/LLMD/reference/routing.md +39 -0
- package/LLMD/reference/troubleshooting.md +364 -0
- package/LLMD/resources/.gitkeep +1 -0
- package/LLMD/resources/controllers.md +465 -0
- package/LLMD/resources/live-components.md +703 -0
- package/LLMD/resources/live-rooms.md +482 -0
- package/LLMD/resources/live-upload.md +130 -0
- package/LLMD/resources/plugins-external.md +617 -0
- package/LLMD/resources/routes-eden.md +254 -0
- package/README.md +37 -17
- package/app/client/index.html +0 -1
- package/app/client/src/App.tsx +107 -150
- package/app/client/src/components/AppLayout.tsx +68 -0
- package/app/client/src/components/BackButton.tsx +13 -0
- package/app/client/src/components/DemoPage.tsx +20 -0
- package/app/client/src/components/LiveUploadWidget.tsx +204 -0
- package/app/client/src/lib/eden-api.ts +85 -60
- package/app/client/src/live/ChatDemo.tsx +107 -0
- package/app/client/src/live/CounterDemo.tsx +206 -0
- package/app/client/src/live/FormDemo.tsx +119 -0
- package/app/client/src/live/RoomChatDemo.tsx +161 -0
- package/app/client/src/live/UploadDemo.tsx +21 -0
- package/app/client/src/main.tsx +4 -1
- package/app/client/src/pages/ApiTestPage.tsx +108 -0
- package/app/client/src/pages/HomePage.tsx +76 -0
- package/app/server/app.ts +1 -4
- package/app/server/controllers/users.controller.ts +36 -44
- package/app/server/index.ts +25 -35
- package/app/server/live/LiveChat.ts +77 -0
- package/app/server/live/LiveCounter.ts +67 -0
- package/app/server/live/LiveForm.ts +63 -0
- package/app/server/live/LiveLocalCounter.ts +32 -0
- package/app/server/live/LiveRoomChat.ts +127 -0
- package/app/server/live/LiveUpload.ts +81 -0
- package/app/server/routes/index.ts +3 -1
- package/app/server/routes/room.routes.ts +117 -0
- package/app/server/routes/users.routes.ts +35 -27
- package/app/shared/types/index.ts +14 -2
- package/config/app.config.ts +2 -62
- package/config/client.config.ts +2 -95
- package/config/database.config.ts +2 -99
- package/config/fluxstack.config.ts +25 -45
- package/config/index.ts +57 -38
- package/config/monitoring.config.ts +2 -114
- package/config/plugins.config.ts +2 -80
- package/config/server.config.ts +2 -68
- package/config/services.config.ts +2 -130
- package/config/system/app.config.ts +29 -0
- package/config/system/build.config.ts +49 -0
- package/config/system/client.config.ts +68 -0
- package/config/system/database.config.ts +17 -0
- package/config/system/fluxstack.config.ts +114 -0
- package/config/{logger.config.ts → system/logger.config.ts} +3 -1
- package/config/system/monitoring.config.ts +114 -0
- package/config/system/plugins.config.ts +84 -0
- package/config/{runtime.config.ts → system/runtime.config.ts} +1 -1
- package/config/system/server.config.ts +68 -0
- package/config/system/services.config.ts +46 -0
- package/config/{system.config.ts → system/system.config.ts} +1 -1
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +39 -27
- package/core/build/live-components-generator.ts +3 -3
- package/core/build/optimizer.ts +235 -235
- package/core/cli/command-registry.ts +6 -4
- package/core/cli/commands/build.ts +79 -0
- package/core/cli/commands/create.ts +54 -0
- package/core/cli/commands/dev.ts +101 -0
- package/core/cli/commands/help.ts +34 -0
- package/core/cli/commands/index.ts +34 -0
- package/core/cli/commands/make-plugin.ts +90 -0
- package/core/cli/commands/plugin-add.ts +197 -0
- package/core/cli/commands/plugin-deps.ts +2 -2
- package/core/cli/commands/plugin-list.ts +208 -0
- package/core/cli/commands/plugin-remove.ts +170 -0
- package/core/cli/generators/component.ts +769 -769
- package/core/cli/generators/controller.ts +1 -1
- package/core/cli/generators/index.ts +146 -146
- package/core/cli/generators/interactive.ts +227 -227
- package/core/cli/generators/plugin.ts +2 -2
- package/core/cli/generators/prompts.ts +82 -82
- package/core/cli/generators/route.ts +6 -6
- package/core/cli/generators/service.ts +2 -2
- package/core/cli/generators/template-engine.ts +4 -3
- package/core/cli/generators/types.ts +2 -2
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +115 -686
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/LiveComponentsProvider.tsx +60 -8
- package/core/client/api/eden.ts +183 -0
- package/core/client/api/index.ts +11 -0
- package/core/client/components/Live.tsx +104 -0
- package/core/client/fluxstack.ts +1 -9
- package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/hooks/useChunkedUpload.ts +85 -35
- package/core/client/hooks/useLiveChunkedUpload.ts +87 -0
- package/core/client/hooks/useLiveComponent.ts +800 -0
- package/core/client/hooks/useLiveUpload.ts +71 -0
- package/core/client/hooks/useRoom.ts +409 -0
- package/core/client/hooks/useRoomProxy.ts +382 -0
- package/core/client/index.ts +17 -68
- package/core/client/standalone-entry.ts +8 -0
- package/core/client/standalone.ts +74 -53
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +70 -291
- package/core/config/schema.ts +42 -723
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +47 -40
- package/core/framework/types.ts +2 -2
- package/core/index.ts +23 -4
- package/core/live/ComponentRegistry.ts +3 -3
- package/core/live/types.ts +77 -0
- package/core/plugins/built-in/index.ts +134 -134
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +242 -1066
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +111 -47
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +68 -265
- package/core/plugins/built-in/vite/index.ts +85 -185
- package/core/plugins/built-in/vite/vite-dev.ts +10 -16
- package/core/plugins/config.ts +9 -7
- package/core/plugins/dependency-manager.ts +31 -1
- package/core/plugins/discovery.ts +19 -7
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +203 -203
- package/core/plugins/manager.ts +27 -39
- package/core/plugins/module-resolver.ts +19 -8
- package/core/plugins/registry.ts +255 -19
- package/core/plugins/types.ts +20 -53
- package/core/server/framework.ts +66 -43
- package/core/server/index.ts +15 -15
- package/core/server/live/ComponentRegistry.ts +78 -71
- package/core/server/live/FileUploadManager.ts +23 -10
- package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
- package/core/server/live/LiveRoomManager.ts +261 -0
- package/core/server/live/RoomEventBus.ts +234 -0
- package/core/server/live/RoomStateManager.ts +172 -0
- package/core/server/live/StateSignature.ts +643 -643
- package/core/server/live/WebSocketConnectionManager.ts +30 -19
- package/core/server/live/auto-generated-components.ts +21 -9
- package/core/server/live/index.ts +14 -0
- package/core/server/live/websocket-plugin.ts +214 -67
- package/core/server/middleware/elysia-helpers.ts +7 -2
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +180 -180
- package/core/server/plugins/static-files-plugin.ts +69 -69
- package/core/server/plugins/swagger.ts +1 -1
- package/core/server/rooms/RoomBroadcaster.ts +357 -0
- package/core/server/rooms/RoomSystem.ts +463 -0
- package/core/server/rooms/index.ts +13 -0
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +12 -12
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/build.ts +219 -219
- package/core/types/config.ts +56 -26
- package/core/types/index.ts +4 -4
- package/core/types/plugin.ts +107 -107
- package/core/types/types.ts +353 -14
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +2 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +36 -1
- package/core/utils/errors/index.ts +49 -5
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +6 -16
- package/core/utils/index.ts +17 -17
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +13 -9
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +6 -1
- package/core/utils/logger/stack-trace.ts +3 -1
- package/core/utils/logger/startup-banner.ts +82 -82
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +66 -66
- package/core/utils/version.ts +1 -1
- package/create-fluxstack.ts +8 -7
- package/package.json +12 -13
- package/plugins/crypto-auth/cli/make-protected-route.command.ts +1 -1
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
- package/plugins/crypto-auth/client/components/index.ts +11 -11
- package/plugins/crypto-auth/client/index.ts +11 -11
- package/plugins/crypto-auth/config/index.ts +1 -1
- package/plugins/crypto-auth/index.ts +4 -4
- package/plugins/crypto-auth/package.json +65 -65
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/index.ts +21 -21
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +2 -2
- package/plugins/crypto-auth/server/middlewares/helpers.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/tsconfig.api-strict.json +16 -0
- package/tsconfig.json +48 -52
- package/{app/client/tsconfig.node.json → tsconfig.node.json} +25 -25
- package/types/global.d.ts +29 -29
- package/types/vitest.d.ts +8 -8
- package/vite.config.ts +38 -62
- package/vitest.config.live.ts +10 -9
- package/vitest.config.ts +29 -17
- package/app/client/README.md +0 -69
- package/app/client/SIMPLIFICATION.md +0 -140
- package/app/client/frontend-only.ts +0 -12
- package/app/client/src/live/FileUploadExample.tsx +0 -359
- package/app/client/src/live/MinimalLiveClock.tsx +0 -47
- package/app/client/src/live/QuickUploadTest.tsx +0 -193
- package/app/client/tsconfig.app.json +0 -45
- package/app/client/tsconfig.json +0 -7
- package/app/client/zustand-setup.md +0 -65
- package/app/server/backend-only.ts +0 -18
- package/app/server/live/LiveClockComponent.ts +0 -215
- package/app/server/live/LiveFileUploadComponent.ts +0 -77
- package/app/server/routes/env-test.ts +0 -110
- package/core/client/hooks/index.ts +0 -7
- package/core/client/hooks/useHybridLiveComponent.ts +0 -685
- package/core/client/hooks/useTypedLiveComponent.ts +0 -133
- package/core/client/hooks/useWebSocket.ts +0 -361
- package/core/config/env.ts +0 -546
- package/core/config/loader.ts +0 -522
- package/core/config/runtime-config.ts +0 -327
- package/core/config/validator.ts +0 -540
- package/core/server/backend-entry.ts +0 -51
- package/core/server/standalone.ts +0 -106
- package/core/utils/regenerate-files.ts +0 -69
- package/fluxstack.config.ts +0 -354
package/core/plugins/registry.ts
CHANGED
|
@@ -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 "
|
|
5
|
-
import type { Logger } from "
|
|
6
|
-
import { FluxStackError } from "
|
|
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
|
-
|
|
55
|
-
|
|
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
|
|
232
|
-
disabledPlugins: this.config?.plugins.disabled
|
|
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
|
-
|
|
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
|
-
//
|
|
564
|
+
// Check and install plugin dependencies
|
|
347
565
|
if (manifest && manifest.dependencies && Object.keys(manifest.dependencies).length > 0) {
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
}
|
|
356
|
-
|
|
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
|
+
|
package/core/plugins/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { FluxStackConfig } from "
|
|
2
|
-
import type { Logger } from "
|
|
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 '
|
|
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
|
-
|
|
362
|
-
|
|
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'
|
package/core/server/framework.ts
CHANGED
|
@@ -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 {
|
|
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 ? { ...
|
|
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
|
|
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
|
-
|
|
116
|
-
set.headers["Access-Control-Allow-
|
|
117
|
-
set.headers["Access-Control-Allow-
|
|
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 =
|
|
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
|
+
}
|
package/core/server/index.ts
CHANGED
|
@@ -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"
|