create-fluxstack 1.15.0 → 1.17.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.
- package/CHANGELOG.md +80 -0
- package/LLMD/INDEX.md +4 -3
- package/LLMD/resources/live-binary-delta.md +507 -0
- package/LLMD/resources/live-components.md +1 -0
- package/LLMD/resources/live-rooms.md +731 -333
- package/app/client/src/App.tsx +23 -14
- package/app/client/src/components/AppLayout.tsx +4 -4
- package/app/client/src/live/AuthDemo.tsx +4 -4
- package/app/client/src/live/PingPongDemo.tsx +199 -0
- package/app/client/src/live/RoomChatDemo.tsx +187 -22
- package/app/client/src/live/SharedCounterDemo.tsx +142 -0
- package/app/server/live/LivePingPong.ts +61 -0
- package/app/server/live/LiveRoomChat.ts +106 -38
- package/app/server/live/LiveSharedCounter.ts +73 -0
- package/app/server/live/rooms/ChatRoom.ts +68 -0
- package/app/server/live/rooms/CounterRoom.ts +51 -0
- package/app/server/live/rooms/DirectoryRoom.ts +42 -0
- package/app/server/live/rooms/PingRoom.ts +40 -0
- package/core/build/bundler.ts +40 -26
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +92 -21
- package/core/cli/command-registry.ts +44 -46
- package/core/cli/commands/build.ts +11 -6
- package/core/cli/commands/create.ts +7 -5
- package/core/cli/commands/dev.ts +6 -5
- package/core/cli/commands/help.ts +3 -2
- package/core/cli/commands/make-plugin.ts +8 -7
- package/core/cli/commands/plugin-add.ts +60 -43
- package/core/cli/commands/plugin-deps.ts +73 -57
- package/core/cli/commands/plugin-list.ts +44 -41
- package/core/cli/commands/plugin-remove.ts +33 -22
- package/core/cli/generators/component.ts +770 -769
- package/core/cli/generators/controller.ts +9 -8
- package/core/cli/generators/index.ts +148 -146
- package/core/cli/generators/interactive.ts +228 -227
- package/core/cli/generators/plugin.ts +11 -10
- package/core/cli/generators/prompts.ts +83 -82
- package/core/cli/generators/route.ts +7 -6
- package/core/cli/generators/service.ts +10 -9
- package/core/cli/generators/template-engine.ts +2 -1
- package/core/cli/generators/types.ts +7 -7
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +9 -8
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/index.ts +0 -16
- package/core/client/standalone.ts +18 -17
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +1 -0
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +72 -112
- package/core/framework/types.ts +2 -2
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +6 -3
- package/core/plugins/built-in/monitoring/index.ts +110 -68
- package/core/plugins/built-in/static/index.ts +2 -2
- package/core/plugins/built-in/swagger/index.ts +9 -9
- package/core/plugins/built-in/vite/index.ts +3 -3
- package/core/plugins/built-in/vite/vite-dev.ts +3 -3
- package/core/plugins/config.ts +50 -47
- package/core/plugins/discovery.ts +10 -4
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +206 -203
- package/core/plugins/manager.ts +21 -20
- package/core/plugins/registry.ts +76 -12
- package/core/plugins/types.ts +14 -14
- package/core/server/framework.ts +3 -189
- package/core/server/live/auto-generated-components.ts +11 -35
- package/core/server/live/index.ts +41 -36
- package/core/server/live/websocket-plugin.ts +48 -3
- package/core/server/middleware/elysia-helpers.ts +16 -15
- package/core/server/middleware/errorHandling.ts +14 -14
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +181 -180
- package/core/server/plugins/static-files-plugin.ts +4 -3
- package/core/server/plugins/swagger.ts +11 -8
- package/core/server/rooms/RoomBroadcaster.ts +11 -10
- package/core/server/rooms/RoomSystem.ts +14 -11
- package/core/server/services/BaseService.ts +7 -7
- package/core/server/services/ServiceContainer.ts +5 -5
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +28 -27
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/config.ts +5 -5
- package/core/types/index.ts +1 -1
- package/core/types/plugin.ts +2 -2
- package/core/types/types.ts +3 -3
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +10 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +30 -20
- package/core/utils/errors/index.ts +54 -46
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +19 -16
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +2 -2
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +13 -3
- package/core/utils/logger/startup-banner.ts +2 -2
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +67 -66
- package/core/utils/version.ts +1 -1
- package/package.json +11 -6
- package/playwright-report/index.html +85 -0
- package/playwright.config.ts +31 -0
- 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/package.json +65 -65
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +6 -5
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +6 -5
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/plugins/crypto-auth/server/middlewares.ts +19 -19
- package/tsconfig.json +4 -1
- package/vite.config.ts +13 -0
- package/app/client/.live-stubs/LiveAdminPanel.js +0 -5
- package/app/client/.live-stubs/LiveChat.js +0 -7
- package/app/client/.live-stubs/LiveCounter.js +0 -9
- package/app/client/.live-stubs/LiveForm.js +0 -11
- package/app/client/.live-stubs/LiveLocalCounter.js +0 -8
- package/app/client/.live-stubs/LiveRoomChat.js +0 -10
- package/app/client/.live-stubs/LiveTodoList.js +0 -9
- package/app/client/.live-stubs/LiveUpload.js +0 -15
- package/app/client/src/live/ChatDemo.tsx +0 -107
- package/app/client/src/live/LiveDebuggerPanel.tsx +0 -779
- package/app/client/src/live/TodoListDemo.tsx +0 -158
- package/app/server/live/LiveChat.ts +0 -78
- package/app/server/live/LiveTodoList.ts +0 -110
- package/app/server/live/register-components.ts +0 -19
- package/core/build/live-components-generator.ts +0 -312
- package/core/client/components/LiveDebugger.tsx +0 -1324
- package/core/live/ComponentRegistry.ts +0 -403
- package/core/live/types.ts +0 -241
- package/workspace.json +0 -6
package/core/plugins/registry.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
import type { FluxStack, PluginManifest, PluginLoadResult, PluginDiscoveryOptions } from "./types"
|
|
1
|
+
import type { FluxStack, PluginManifest, PluginLoadResult, PluginDiscoveryOptions, PluginPriority } from "./types"
|
|
2
2
|
|
|
3
3
|
type FluxStackPlugin = FluxStack.Plugin
|
|
4
|
+
|
|
5
|
+
const PRIORITY_MAP: Record<string, number> = {
|
|
6
|
+
highest: 1000,
|
|
7
|
+
high: 750,
|
|
8
|
+
normal: 500,
|
|
9
|
+
low: 250,
|
|
10
|
+
lowest: 0
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizePriority(priority?: number | PluginPriority): number {
|
|
14
|
+
if (typeof priority === 'number') return priority
|
|
15
|
+
if (typeof priority === 'string' && priority in PRIORITY_MAP) return PRIORITY_MAP[priority]
|
|
16
|
+
return 500 // default to normal
|
|
17
|
+
}
|
|
4
18
|
import type { FluxStackConfig } from "@config"
|
|
5
19
|
import type { Logger } from "@core/utils/logger"
|
|
6
20
|
import { FluxStackError } from "@core/utils/errors"
|
|
@@ -223,6 +237,56 @@ export class PluginRegistry {
|
|
|
223
237
|
return this.plugins.has(name)
|
|
224
238
|
}
|
|
225
239
|
|
|
240
|
+
/**
|
|
241
|
+
* Register a plugin synchronously (no async hooks).
|
|
242
|
+
*
|
|
243
|
+
* Used by the framework to add plugins via .use() and during automatic
|
|
244
|
+
* plugin discovery, where the full async register() flow (which fires
|
|
245
|
+
* onPluginRegister hooks) is not needed — setup hooks run later in start().
|
|
246
|
+
*/
|
|
247
|
+
registerSync(plugin: FluxStackPlugin): void {
|
|
248
|
+
if (this.plugins.has(plugin.name)) {
|
|
249
|
+
throw new FluxStackError(
|
|
250
|
+
`Plugin '${plugin.name}' is already registered`,
|
|
251
|
+
'PLUGIN_ALREADY_REGISTERED',
|
|
252
|
+
400
|
|
253
|
+
)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
this.validatePlugin(plugin)
|
|
257
|
+
this.plugins.set(plugin.name, plugin)
|
|
258
|
+
|
|
259
|
+
if (plugin.dependencies) {
|
|
260
|
+
this.dependencies.set(plugin.name, plugin.dependencies)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
this.updateLoadOrder()
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Refresh the load order.
|
|
268
|
+
*
|
|
269
|
+
* Falls back to insertion-order if the topological sort fails
|
|
270
|
+
* (e.g. unresolvable external dependency listed but not yet registered).
|
|
271
|
+
*/
|
|
272
|
+
refreshLoadOrder(): void {
|
|
273
|
+
try {
|
|
274
|
+
this.updateLoadOrder()
|
|
275
|
+
} catch {
|
|
276
|
+
this.loadOrder = Array.from(this.plugins.keys())
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Return a read-only snapshot of the internal plugin map.
|
|
282
|
+
*
|
|
283
|
+
* Allows the framework to iterate over registered plugins for dependency
|
|
284
|
+
* validation without reaching into private fields.
|
|
285
|
+
*/
|
|
286
|
+
getPluginsMap(): ReadonlyMap<string, FluxStackPlugin> {
|
|
287
|
+
return this.plugins
|
|
288
|
+
}
|
|
289
|
+
|
|
226
290
|
/**
|
|
227
291
|
* Check which dependencies are missing from main package.json
|
|
228
292
|
*/
|
|
@@ -278,12 +342,12 @@ export class PluginRegistry {
|
|
|
278
342
|
return true
|
|
279
343
|
}
|
|
280
344
|
|
|
281
|
-
if (allowedPlugins.length === 0) {
|
|
345
|
+
if ((allowedPlugins as string[]).length === 0) {
|
|
282
346
|
this.logger?.warn(`NPM plugin '${pluginName}' blocked: No plugins in whitelist (PLUGINS_ALLOWED is empty)`)
|
|
283
347
|
return false
|
|
284
348
|
}
|
|
285
349
|
|
|
286
|
-
if (!allowedPlugins.includes(pluginName)) {
|
|
350
|
+
if (!(allowedPlugins as string[]).includes(pluginName)) {
|
|
287
351
|
this.logger?.warn(`NPM plugin '${pluginName}' blocked: Not in whitelist (PLUGINS_ALLOWED)`, {
|
|
288
352
|
pluginName,
|
|
289
353
|
allowedPlugins
|
|
@@ -299,8 +363,8 @@ export class PluginRegistry {
|
|
|
299
363
|
getStats() {
|
|
300
364
|
return {
|
|
301
365
|
totalPlugins: this.plugins.size,
|
|
302
|
-
enabledPlugins: this.config?.plugins.enabled?.length ?? 0,
|
|
303
|
-
disabledPlugins: this.config?.plugins.disabled?.length ?? 0,
|
|
366
|
+
enabledPlugins: (this.config?.plugins.enabled as string[] | undefined)?.length ?? 0,
|
|
367
|
+
disabledPlugins: (this.config?.plugins.disabled as string[] | undefined)?.length ?? 0,
|
|
304
368
|
conflicts: this.conflicts.length,
|
|
305
369
|
loadOrder: this.loadOrder.length
|
|
306
370
|
}
|
|
@@ -644,9 +708,11 @@ export class PluginRegistry {
|
|
|
644
708
|
)
|
|
645
709
|
}
|
|
646
710
|
|
|
647
|
-
if (plugin.priority
|
|
711
|
+
if (plugin.priority !== undefined
|
|
712
|
+
&& typeof plugin.priority !== 'number'
|
|
713
|
+
&& !(typeof plugin.priority === 'string' && plugin.priority in PRIORITY_MAP)) {
|
|
648
714
|
throw new FluxStackError(
|
|
649
|
-
|
|
715
|
+
`Plugin priority must be a number or one of: ${Object.keys(PRIORITY_MAP).join(', ')}`,
|
|
650
716
|
'INVALID_PLUGIN_STRUCTURE',
|
|
651
717
|
400
|
|
652
718
|
)
|
|
@@ -656,7 +722,7 @@ export class PluginRegistry {
|
|
|
656
722
|
/**
|
|
657
723
|
* Validate plugin configuration against schema
|
|
658
724
|
*/
|
|
659
|
-
private validatePluginConfig(plugin: FluxStackPlugin, config:
|
|
725
|
+
private validatePluginConfig(plugin: FluxStackPlugin, config: unknown): void {
|
|
660
726
|
if (!plugin.configSchema) {
|
|
661
727
|
return
|
|
662
728
|
}
|
|
@@ -664,7 +730,7 @@ export class PluginRegistry {
|
|
|
664
730
|
// Basic validation - in a real implementation, you'd use a proper JSON schema validator
|
|
665
731
|
if (plugin.configSchema.required) {
|
|
666
732
|
for (const requiredField of plugin.configSchema.required) {
|
|
667
|
-
if (!(requiredField in config)) {
|
|
733
|
+
if (!(requiredField in (config as Record<string, unknown>))) {
|
|
668
734
|
throw new FluxStackError(
|
|
669
735
|
`Plugin '${plugin.name}' configuration missing required field: ${requiredField}`,
|
|
670
736
|
'INVALID_PLUGIN_CONFIG',
|
|
@@ -741,9 +807,7 @@ export class PluginRegistry {
|
|
|
741
807
|
ready.sort((a, b) => {
|
|
742
808
|
const pluginA = this.plugins.get(a)
|
|
743
809
|
const pluginB = this.plugins.get(b)
|
|
744
|
-
|
|
745
|
-
const priorityB = typeof pluginB?.priority === 'number' ? pluginB.priority : 0
|
|
746
|
-
return priorityB - priorityA
|
|
810
|
+
return normalizePriority(pluginB?.priority) - normalizePriority(pluginA?.priority)
|
|
747
811
|
})
|
|
748
812
|
|
|
749
813
|
for (const name of ready) {
|
package/core/plugins/types.ts
CHANGED
|
@@ -36,9 +36,9 @@ export type PluginPriority = 'highest' | 'high' | 'normal' | 'low' | 'lowest' |
|
|
|
36
36
|
export interface PluginContext {
|
|
37
37
|
config: FluxStackConfig
|
|
38
38
|
logger: Logger
|
|
39
|
-
app:
|
|
39
|
+
app: unknown // Elysia app
|
|
40
40
|
utils: PluginUtils
|
|
41
|
-
registry?:
|
|
41
|
+
registry?: unknown // Plugin registry reference
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
export interface PluginUtils {
|
|
@@ -49,8 +49,8 @@ export interface PluginUtils {
|
|
|
49
49
|
isDevelopment: () => boolean
|
|
50
50
|
getEnvironment: () => string
|
|
51
51
|
createHash: (data: string) => string
|
|
52
|
-
deepMerge: (target:
|
|
53
|
-
validateSchema: (data:
|
|
52
|
+
deepMerge: (target: Record<string, unknown>, source: Record<string, unknown>) => Record<string, unknown>
|
|
53
|
+
validateSchema: (data: Record<string, unknown>, schema: PluginConfigSchema) => { valid: boolean; errors: string[] }
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
export interface RequestContext {
|
|
@@ -60,8 +60,8 @@ export interface RequestContext {
|
|
|
60
60
|
headers: Record<string, string>
|
|
61
61
|
query: Record<string, string>
|
|
62
62
|
params: Record<string, string>
|
|
63
|
-
body?:
|
|
64
|
-
user?:
|
|
63
|
+
body?: unknown
|
|
64
|
+
user?: unknown
|
|
65
65
|
startTime: number
|
|
66
66
|
handled?: boolean
|
|
67
67
|
response?: Response
|
|
@@ -127,12 +127,12 @@ export interface PluginEventContext {
|
|
|
127
127
|
pluginName: string
|
|
128
128
|
pluginVersion?: string
|
|
129
129
|
timestamp: number
|
|
130
|
-
data?:
|
|
130
|
+
data?: unknown
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
export interface PluginConfigSchema {
|
|
134
134
|
type: 'object'
|
|
135
|
-
properties: Record<string,
|
|
135
|
+
properties: Record<string, unknown>
|
|
136
136
|
required?: string[]
|
|
137
137
|
additionalProperties?: boolean
|
|
138
138
|
}
|
|
@@ -203,7 +203,7 @@ export namespace FluxStack {
|
|
|
203
203
|
* This property will be removed in a future major version.
|
|
204
204
|
* Use the config/ folder structure for automatic type inference.
|
|
205
205
|
*/
|
|
206
|
-
defaultConfig?:
|
|
206
|
+
defaultConfig?: unknown
|
|
207
207
|
|
|
208
208
|
// CLI commands
|
|
209
209
|
commands?: CliCommand[]
|
|
@@ -252,7 +252,7 @@ export interface PluginHookResult {
|
|
|
252
252
|
duration: number
|
|
253
253
|
plugin: string
|
|
254
254
|
hook: PluginHook
|
|
255
|
-
context?:
|
|
255
|
+
context?: unknown
|
|
256
256
|
}
|
|
257
257
|
|
|
258
258
|
export interface PluginMetrics {
|
|
@@ -319,7 +319,7 @@ export interface CliArgument {
|
|
|
319
319
|
description: string
|
|
320
320
|
required?: boolean
|
|
321
321
|
type?: 'string' | 'number' | 'boolean'
|
|
322
|
-
default?:
|
|
322
|
+
default?: unknown
|
|
323
323
|
choices?: string[]
|
|
324
324
|
}
|
|
325
325
|
|
|
@@ -328,7 +328,7 @@ export interface CliOption {
|
|
|
328
328
|
short?: string
|
|
329
329
|
description: string
|
|
330
330
|
type?: 'string' | 'number' | 'boolean' | 'array'
|
|
331
|
-
default?:
|
|
331
|
+
default?: unknown
|
|
332
332
|
required?: boolean
|
|
333
333
|
choices?: string[]
|
|
334
334
|
}
|
|
@@ -343,7 +343,7 @@ export interface CliCommand {
|
|
|
343
343
|
aliases?: string[]
|
|
344
344
|
category?: string
|
|
345
345
|
hidden?: boolean
|
|
346
|
-
handler: (args:
|
|
346
|
+
handler: (args: unknown[], options: Record<string, unknown>, context: CliContext) => Promise<void> | void
|
|
347
347
|
}
|
|
348
348
|
|
|
349
349
|
export interface CliContext {
|
|
@@ -369,7 +369,7 @@ export interface ActiveUpload {
|
|
|
369
369
|
fileType?: string
|
|
370
370
|
fileSize?: number
|
|
371
371
|
totalChunks: number
|
|
372
|
-
receivedChunks: Map<number,
|
|
372
|
+
receivedChunks: Map<number, unknown>
|
|
373
373
|
startTime: number
|
|
374
374
|
lastChunkTime?: number
|
|
375
375
|
}
|
package/core/server/framework.ts
CHANGED
|
@@ -1,189 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import type { PluginContext, PluginUtils } from "../plugins/types"
|
|
5
|
-
import { PluginManager } from "../plugins/manager"
|
|
6
|
-
import { fluxStackConfig } from "@config"
|
|
7
|
-
import { getEnvironmentInfo } from "../config"
|
|
8
|
-
import { logger, type Logger } from "../utils/logger/index"
|
|
9
|
-
import { createTimer, formatBytes, isProduction, isDevelopment } from "../utils/helpers"
|
|
10
|
-
|
|
11
|
-
export class FluxStackFramework {
|
|
12
|
-
private app: Elysia
|
|
13
|
-
private context: FluxStackContext
|
|
14
|
-
private pluginContext: PluginContext
|
|
15
|
-
private plugins: Plugin[] = []
|
|
16
|
-
private pluginManager: PluginManager
|
|
17
|
-
private initialized = false
|
|
18
|
-
|
|
19
|
-
constructor(config?: Partial<FluxStackConfig>) {
|
|
20
|
-
// Load the full configuration
|
|
21
|
-
const fullConfig = config ? { ...fluxStackConfig, ...config } : fluxStackConfig
|
|
22
|
-
const envInfo = getEnvironmentInfo()
|
|
23
|
-
|
|
24
|
-
this.context = {
|
|
25
|
-
config: fullConfig,
|
|
26
|
-
isDevelopment: envInfo.isDevelopment,
|
|
27
|
-
isProduction: envInfo.isProduction,
|
|
28
|
-
isTest: envInfo.isTest,
|
|
29
|
-
environment: envInfo.name
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
this.app = new Elysia()
|
|
33
|
-
|
|
34
|
-
// Create plugin utilities
|
|
35
|
-
const pluginUtils: PluginUtils = {
|
|
36
|
-
createTimer,
|
|
37
|
-
formatBytes,
|
|
38
|
-
isProduction,
|
|
39
|
-
isDevelopment,
|
|
40
|
-
getEnvironment: () => envInfo.name,
|
|
41
|
-
createHash: (data: string) => nodeCreateHash('sha256').update(data).digest('hex'),
|
|
42
|
-
deepMerge: (target: any, source: any) => {
|
|
43
|
-
const result = { ...target }
|
|
44
|
-
for (const key in source) {
|
|
45
|
-
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
46
|
-
result[key] = pluginUtils.deepMerge(result[key] || {}, source[key])
|
|
47
|
-
} else {
|
|
48
|
-
result[key] = source[key]
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
return result
|
|
52
|
-
},
|
|
53
|
-
validateSchema: (_data: any, _schema: any) => {
|
|
54
|
-
// Simple validation - in a real implementation you'd use a proper schema validator
|
|
55
|
-
try {
|
|
56
|
-
// Basic validation logic
|
|
57
|
-
return { valid: true, errors: [] }
|
|
58
|
-
} catch (error) {
|
|
59
|
-
return { valid: false, errors: [error instanceof Error ? error.message : 'Validation failed'] }
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Create plugin context
|
|
65
|
-
this.pluginContext = {
|
|
66
|
-
config: fullConfig,
|
|
67
|
-
logger: logger as any,
|
|
68
|
-
app: this.app,
|
|
69
|
-
utils: pluginUtils
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Initialize plugin manager
|
|
73
|
-
this.pluginManager = new PluginManager({
|
|
74
|
-
config: fullConfig,
|
|
75
|
-
logger: logger as any,
|
|
76
|
-
app: this.app
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
this.setupCors()
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
private setupCors() {
|
|
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
|
-
}
|
|
96
|
-
|
|
97
|
-
this.app
|
|
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"
|
|
104
|
-
if (cors.credentials) {
|
|
105
|
-
set.headers["Access-Control-Allow-Credentials"] = "true"
|
|
106
|
-
}
|
|
107
|
-
if (cors.maxAge) {
|
|
108
|
-
set.headers["Access-Control-Max-Age"] = cors.maxAge.toString()
|
|
109
|
-
}
|
|
110
|
-
})
|
|
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
|
-
return ""
|
|
125
|
-
})
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
use(plugin: Plugin) {
|
|
129
|
-
this.plugins.push(plugin)
|
|
130
|
-
if (plugin.setup) {
|
|
131
|
-
plugin.setup(this.pluginContext)
|
|
132
|
-
}
|
|
133
|
-
return this
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
routes(routeModule: any) {
|
|
137
|
-
this.app.use(routeModule)
|
|
138
|
-
return this
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
getApp() {
|
|
142
|
-
return this.app
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
getContext() {
|
|
146
|
-
return this.context
|
|
147
|
-
}
|
|
148
|
-
|
|
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
|
-
|
|
171
|
-
const port = this.context.config.server.port
|
|
172
|
-
const apiPrefix = this.context.config.server.apiPrefix
|
|
173
|
-
|
|
174
|
-
this.app.listen(port, () => {
|
|
175
|
-
logger.info('[FluxStack] Server started on port ' + port, {
|
|
176
|
-
apiPrefix,
|
|
177
|
-
environment: this.context.environment,
|
|
178
|
-
manualPlugins: this.plugins.length,
|
|
179
|
-
automaticPlugins: this.pluginManager.getRegistry().getStats().totalPlugins
|
|
180
|
-
})
|
|
181
|
-
console.log(`🚀 API ready at http://localhost:${port}${apiPrefix}`)
|
|
182
|
-
console.log(`📋 Health check: http://localhost:${port}${apiPrefix}/health`)
|
|
183
|
-
console.log()
|
|
184
|
-
callback?.()
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
return this
|
|
188
|
-
}
|
|
189
|
-
}
|
|
1
|
+
// Re-export from canonical location to maintain backwards compatibility
|
|
2
|
+
// The single source of truth is core/framework/server.ts
|
|
3
|
+
export { FluxStackFramework } from "../framework/server"
|
|
@@ -1,50 +1,26 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
// Generated at: 2026-03-
|
|
1
|
+
// Auto-generated Live Components Registration
|
|
2
|
+
// Generated by @fluxstack/live — DO NOT EDIT MANUALLY
|
|
3
|
+
// Generated at: 2026-03-21T23:18:32.007Z
|
|
4
4
|
|
|
5
5
|
import { LiveAdminPanel } from "@app/server/live/LiveAdminPanel"
|
|
6
|
-
import { LiveChat } from "@app/server/live/LiveChat"
|
|
7
6
|
import { LiveCounter } from "@app/server/live/LiveCounter"
|
|
8
7
|
import { LiveForm } from "@app/server/live/LiveForm"
|
|
9
8
|
import { LiveLocalCounter } from "@app/server/live/LiveLocalCounter"
|
|
9
|
+
import { LivePingPong } from "@app/server/live/LivePingPong"
|
|
10
10
|
import { LiveProtectedChat } from "@app/server/live/LiveProtectedChat"
|
|
11
11
|
import { LiveRoomChat } from "@app/server/live/LiveRoomChat"
|
|
12
|
-
import {
|
|
12
|
+
import { LiveSharedCounter } from "@app/server/live/LiveSharedCounter"
|
|
13
13
|
import { LiveUpload } from "@app/server/live/LiveUpload"
|
|
14
|
-
import { componentRegistry } from "@core/server/live"
|
|
15
14
|
|
|
16
|
-
//
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
// Auto-generated component registrations
|
|
20
|
-
componentRegistry.registerComponentClass('LiveAdminPanel', LiveAdminPanel)
|
|
21
|
-
componentRegistry.registerComponentClass('LiveChat', LiveChat)
|
|
22
|
-
componentRegistry.registerComponentClass('LiveCounter', LiveCounter)
|
|
23
|
-
componentRegistry.registerComponentClass('LiveForm', LiveForm)
|
|
24
|
-
componentRegistry.registerComponentClass('LiveLocalCounter', LiveLocalCounter)
|
|
25
|
-
componentRegistry.registerComponentClass('LiveProtectedChat', LiveProtectedChat)
|
|
26
|
-
componentRegistry.registerComponentClass('LiveRoomChat', LiveRoomChat)
|
|
27
|
-
componentRegistry.registerComponentClass('LiveTodoList', LiveTodoList)
|
|
28
|
-
componentRegistry.registerComponentClass('LiveUpload', LiveUpload)
|
|
29
|
-
|
|
30
|
-
console.log('📝 Live components registered successfully! (9 components)')
|
|
31
|
-
} catch (error) {
|
|
32
|
-
console.warn('⚠️ Error registering components:', error)
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Auto-register components
|
|
37
|
-
registerAllComponents()
|
|
38
|
-
|
|
39
|
-
// Export all components to ensure they're included in the bundle
|
|
40
|
-
export {
|
|
15
|
+
// Component classes array for LiveServer({ components }) option
|
|
16
|
+
export const liveComponentClasses = [
|
|
41
17
|
LiveAdminPanel,
|
|
42
|
-
LiveChat,
|
|
43
18
|
LiveCounter,
|
|
44
19
|
LiveForm,
|
|
45
20
|
LiveLocalCounter,
|
|
21
|
+
LivePingPong,
|
|
46
22
|
LiveProtectedChat,
|
|
47
23
|
LiveRoomChat,
|
|
48
|
-
|
|
49
|
-
LiveUpload
|
|
50
|
-
|
|
24
|
+
LiveSharedCounter,
|
|
25
|
+
LiveUpload,
|
|
26
|
+
]
|
|
@@ -36,6 +36,25 @@ export type {
|
|
|
36
36
|
// These lazily access the LiveServer instance created by the plugin
|
|
37
37
|
import { liveServer, pendingAuthProviders } from './websocket-plugin'
|
|
38
38
|
import type { LiveAuthProvider as _LiveAuthProvider } from '@fluxstack/live'
|
|
39
|
+
import type { ComponentRegistry as _ComponentRegistry } from '@fluxstack/live'
|
|
40
|
+
import type { WebSocketConnectionManager as _WebSocketConnectionManager } from '@fluxstack/live'
|
|
41
|
+
import type { RoomStateManager as _RoomStateManager } from '@fluxstack/live'
|
|
42
|
+
import type { LiveRoomManager as _LiveRoomManager } from '@fluxstack/live'
|
|
43
|
+
import type { RoomEventBus as _RoomEventBus } from '@fluxstack/live'
|
|
44
|
+
import type { FileUploadManager as _FileUploadManager } from '@fluxstack/live'
|
|
45
|
+
import type { PerformanceMonitor as _PerformanceMonitor } from '@fluxstack/live'
|
|
46
|
+
import type { StateSignatureManager as _StateSignatureManager } from '@fluxstack/live'
|
|
47
|
+
import type { LiveAuthManager as _LiveAuthManager } from '@fluxstack/live'
|
|
48
|
+
|
|
49
|
+
function requireLiveServer() {
|
|
50
|
+
if (!liveServer) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
'LiveComponents plugin not initialized. ' +
|
|
53
|
+
'Ensure the live-components plugin is loaded before accessing Live singletons.'
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
return liveServer
|
|
57
|
+
}
|
|
39
58
|
|
|
40
59
|
/**
|
|
41
60
|
* Backward-compatible liveAuthManager.
|
|
@@ -43,7 +62,7 @@ import type { LiveAuthProvider as _LiveAuthProvider } from '@fluxstack/live'
|
|
|
43
62
|
* then delegates to liveServer.authManager once available.
|
|
44
63
|
* @deprecated Access via liveServer.authManager instead
|
|
45
64
|
*/
|
|
46
|
-
export const liveAuthManager = {
|
|
65
|
+
export const liveAuthManager: Pick<_LiveAuthManager, 'authenticate' | 'hasProviders' | 'authorizeRoom' | 'authorizeAction' | 'authorizeComponent'> & { register: (provider: _LiveAuthProvider) => void } = {
|
|
47
66
|
register(provider: _LiveAuthProvider) {
|
|
48
67
|
if (liveServer) {
|
|
49
68
|
liveServer.useAuth(provider)
|
|
@@ -51,54 +70,40 @@ export const liveAuthManager = {
|
|
|
51
70
|
pendingAuthProviders.push(provider)
|
|
52
71
|
}
|
|
53
72
|
},
|
|
54
|
-
get authenticate() { return
|
|
55
|
-
get hasProviders() { return
|
|
56
|
-
get authorizeRoom() { return
|
|
57
|
-
get authorizeAction() { return
|
|
58
|
-
get authorizeComponent() { return
|
|
59
|
-
}
|
|
73
|
+
get authenticate() { return requireLiveServer().authManager.authenticate.bind(requireLiveServer().authManager) },
|
|
74
|
+
get hasProviders() { return requireLiveServer().authManager.hasProviders.bind(requireLiveServer().authManager) },
|
|
75
|
+
get authorizeRoom() { return requireLiveServer().authManager.authorizeRoom.bind(requireLiveServer().authManager) },
|
|
76
|
+
get authorizeAction() { return requireLiveServer().authManager.authorizeAction.bind(requireLiveServer().authManager) },
|
|
77
|
+
get authorizeComponent() { return requireLiveServer().authManager.authorizeComponent.bind(requireLiveServer().authManager) },
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Helper to create a typed lazy proxy that delegates to a LiveServer property */
|
|
81
|
+
function createLazyProxy<T extends object>(accessor: () => T): T {
|
|
82
|
+
return new Proxy({} as T, {
|
|
83
|
+
get(_, prop) { return (accessor() as Record<string | symbol, unknown>)[prop] }
|
|
84
|
+
})
|
|
85
|
+
}
|
|
60
86
|
|
|
61
87
|
/** @deprecated Access via liveServer.registry instead */
|
|
62
|
-
export const componentRegistry =
|
|
63
|
-
get(_, prop) { return (liveServer!.registry as any)[prop] }
|
|
64
|
-
})
|
|
88
|
+
export const componentRegistry = createLazyProxy<_ComponentRegistry>(() => requireLiveServer().registry)
|
|
65
89
|
|
|
66
90
|
/** @deprecated Access via liveServer.connectionManager instead */
|
|
67
|
-
export const connectionManager =
|
|
68
|
-
get(_, prop) { return (liveServer!.connectionManager as any)[prop] }
|
|
69
|
-
})
|
|
91
|
+
export const connectionManager = createLazyProxy<_WebSocketConnectionManager>(() => requireLiveServer().connectionManager)
|
|
70
92
|
|
|
71
93
|
/** @deprecated Access via liveServer.roomManager instead */
|
|
72
|
-
export const liveRoomManager =
|
|
73
|
-
get(_, prop) { return (liveServer!.roomManager as any)[prop] }
|
|
74
|
-
})
|
|
94
|
+
export const liveRoomManager = createLazyProxy<_LiveRoomManager>(() => requireLiveServer().roomManager)
|
|
75
95
|
|
|
76
96
|
/** @deprecated Access via liveServer.roomEvents instead */
|
|
77
|
-
export const roomEvents =
|
|
78
|
-
get(_, prop) { return (liveServer!.roomEvents as any)[prop] }
|
|
79
|
-
})
|
|
97
|
+
export const roomEvents = createLazyProxy<_RoomEventBus>(() => requireLiveServer().roomEvents)
|
|
80
98
|
|
|
81
99
|
/** @deprecated Access via liveServer.fileUploadManager instead */
|
|
82
|
-
export const fileUploadManager =
|
|
83
|
-
get(_, prop) { return (liveServer!.fileUploadManager as any)[prop] }
|
|
84
|
-
})
|
|
100
|
+
export const fileUploadManager = createLazyProxy<_FileUploadManager>(() => requireLiveServer().fileUploadManager)
|
|
85
101
|
|
|
86
102
|
/** @deprecated Access via liveServer.performanceMonitor instead */
|
|
87
|
-
export const performanceMonitor =
|
|
88
|
-
get(_, prop) { return (liveServer!.performanceMonitor as any)[prop] }
|
|
89
|
-
})
|
|
103
|
+
export const performanceMonitor = createLazyProxy<_PerformanceMonitor>(() => requireLiveServer().performanceMonitor)
|
|
90
104
|
|
|
91
105
|
/** @deprecated Access via liveServer.stateSignature instead */
|
|
92
|
-
export const stateSignature =
|
|
93
|
-
get(_, prop) { return (liveServer!.stateSignature as any)[prop] }
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
/** @deprecated Access via liveServer.debugger instead */
|
|
97
|
-
export const liveDebugger = new Proxy({} as any, {
|
|
98
|
-
get(_, prop) { return (liveServer!.debugger as any)[prop] }
|
|
99
|
-
})
|
|
106
|
+
export const stateSignature = createLazyProxy<_StateSignatureManager>(() => requireLiveServer().stateSignature)
|
|
100
107
|
|
|
101
108
|
// Room state backward compat
|
|
102
|
-
export const roomState =
|
|
103
|
-
get(_, prop) { return (liveServer!.roomManager as any)[prop] }
|
|
104
|
-
})
|
|
109
|
+
export const roomState = createLazyProxy<_LiveRoomManager>(() => requireLiveServer().roomManager)
|