create-fluxstack 1.18.1 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +132 -0
  2. package/app/client/src/App.tsx +7 -7
  3. package/app/client/src/components/AppLayout.tsx +60 -23
  4. package/app/client/src/components/ColorWheel.tsx +195 -0
  5. package/app/client/src/components/DemoPage.tsx +5 -3
  6. package/app/client/src/components/LiveUploadWidget.tsx +1 -1
  7. package/app/client/src/components/ThemePicker.tsx +307 -0
  8. package/app/client/src/config/theme.config.ts +127 -0
  9. package/app/client/src/hooks/useThemeClock.ts +66 -0
  10. package/app/client/src/index.css +193 -0
  11. package/app/client/src/lib/theme-clock.ts +201 -0
  12. package/app/client/src/live/AuthDemo.tsx +9 -9
  13. package/app/client/src/live/CounterDemo.tsx +10 -10
  14. package/app/client/src/live/FormDemo.tsx +8 -8
  15. package/app/client/src/live/PingPongDemo.tsx +10 -10
  16. package/app/client/src/live/RoomChatDemo.tsx +10 -10
  17. package/app/client/src/live/SharedCounterDemo.tsx +5 -5
  18. package/app/client/src/pages/ApiTestPage.tsx +5 -5
  19. package/app/client/src/pages/HomePage.tsx +12 -12
  20. package/app/server/index.ts +8 -0
  21. package/app/server/live/auto-generated-components.ts +1 -1
  22. package/core/build/index.ts +1 -1
  23. package/core/cli/command-registry.ts +1 -1
  24. package/core/cli/commands/build.ts +25 -6
  25. package/core/cli/commands/plugin-deps.ts +1 -2
  26. package/core/cli/generators/plugin.ts +433 -581
  27. package/core/framework/server.ts +22 -8
  28. package/core/index.ts +6 -5
  29. package/core/plugins/index.ts +71 -199
  30. package/core/plugins/types.ts +76 -461
  31. package/core/server/index.ts +1 -1
  32. package/core/utils/logger/startup-banner.ts +26 -4
  33. package/core/utils/version.ts +6 -6
  34. package/create-fluxstack.ts +216 -107
  35. package/package.json +6 -5
  36. package/tsconfig.json +2 -1
  37. package/app/client/.live-stubs/LiveAdminPanel.js +0 -15
  38. package/app/client/.live-stubs/LiveCounter.js +0 -9
  39. package/app/client/.live-stubs/LiveForm.js +0 -11
  40. package/app/client/.live-stubs/LiveLocalCounter.js +0 -8
  41. package/app/client/.live-stubs/LivePingPong.js +0 -10
  42. package/app/client/.live-stubs/LiveRoomChat.js +0 -11
  43. package/app/client/.live-stubs/LiveSharedCounter.js +0 -10
  44. package/app/client/.live-stubs/LiveUpload.js +0 -15
  45. package/core/plugins/config.ts +0 -356
  46. package/core/plugins/dependency-manager.ts +0 -481
  47. package/core/plugins/discovery.ts +0 -379
  48. package/core/plugins/executor.ts +0 -353
  49. package/core/plugins/manager.ts +0 -645
  50. package/core/plugins/module-resolver.ts +0 -227
  51. package/core/plugins/registry.ts +0 -913
  52. package/vitest.config.live.ts +0 -69
@@ -1,356 +0,0 @@
1
- /**
2
- * Plugin Configuration Management
3
- * Handles plugin-specific configuration validation and management
4
- */
5
-
6
- import type { FluxStack, PluginConfigSchema, PluginValidationResult } from "./types"
7
- import type { FluxStackConfig } from "@config"
8
- import type { Logger } from "@core/utils/logger/index"
9
-
10
- type Plugin = FluxStack.Plugin
11
-
12
- export interface PluginConfigManager {
13
- validatePluginConfig(plugin: Plugin, config: unknown): PluginValidationResult
14
- mergePluginConfig(plugin: Plugin, userConfig: unknown): unknown
15
- getPluginConfig(pluginName: string, config: FluxStackConfig): unknown
16
- setPluginConfig(pluginName: string, pluginConfig: unknown, config: FluxStackConfig): void
17
- }
18
-
19
- export class DefaultPluginConfigManager implements PluginConfigManager {
20
- constructor(_logger?: Logger) {
21
- // Logger stored but not used in current implementation
22
- }
23
-
24
- /**
25
- * Validate plugin configuration against its schema
26
- */
27
- validatePluginConfig(plugin: Plugin, config: unknown): PluginValidationResult {
28
- const result: PluginValidationResult = {
29
- valid: true,
30
- errors: [],
31
- warnings: []
32
- }
33
-
34
- if (!plugin.configSchema) {
35
- // No schema means any config is valid
36
- return result
37
- }
38
-
39
- try {
40
- this.validateAgainstSchema(config, plugin.configSchema, plugin.name, result)
41
- } catch (error) {
42
- result.valid = false
43
- result.errors.push(`Configuration validation failed: ${error instanceof Error ? error.message : String(error)}`)
44
- }
45
-
46
- return result
47
- }
48
-
49
- /**
50
- * Merge user configuration with plugin defaults
51
- */
52
- mergePluginConfig(plugin: Plugin, userConfig: unknown): unknown {
53
- const defaultConfig = (plugin.defaultConfig || {}) as Record<string, unknown>
54
-
55
- if (!userConfig) {
56
- return defaultConfig
57
- }
58
-
59
- return this.deepMerge(defaultConfig, userConfig as Record<string, unknown>)
60
- }
61
-
62
- /**
63
- * Get plugin configuration from main config
64
- * @deprecated Plugin configs are now directly accessed from config.plugins
65
- */
66
- getPluginConfig(pluginName: string, config: FluxStackConfig): unknown {
67
- // Plugin configs are now accessed directly from config.plugins
68
- // Example: config.plugins.swaggerEnabled
69
- return {}
70
- }
71
-
72
- /**
73
- * Set plugin configuration in main config
74
- * @deprecated Plugin configs are now set via environment variables and config files
75
- */
76
- setPluginConfig(pluginName: string, pluginConfig: unknown, config: FluxStackConfig): void {
77
- // Plugin configs are now set via environment variables and config files
78
- // This function is deprecated and does nothing
79
- }
80
-
81
- /**
82
- * Validate configuration against JSON schema
83
- */
84
- private validateAgainstSchema(
85
- data: unknown,
86
- schema: PluginConfigSchema,
87
- pluginName: string,
88
- result: PluginValidationResult
89
- ): void {
90
- if (schema.type === 'object' && typeof data !== 'object') {
91
- result.valid = false
92
- result.errors.push(`Plugin '${pluginName}' configuration must be an object`)
93
- return
94
- }
95
-
96
- const dataObj = data as Record<string, unknown>
97
-
98
- // Check required properties
99
- if (schema.required && Array.isArray(schema.required)) {
100
- for (const requiredProp of schema.required) {
101
- if (!(requiredProp in dataObj)) {
102
- result.valid = false
103
- result.errors.push(`Plugin '${pluginName}' configuration missing required property: ${requiredProp}`)
104
- }
105
- }
106
- }
107
-
108
- // Validate properties
109
- if (schema.properties) {
110
- for (const [propName, propSchema] of Object.entries(schema.properties)) {
111
- if (propName in dataObj) {
112
- this.validateProperty(dataObj[propName], propSchema as Record<string, unknown>, `${pluginName}.${propName}`, result)
113
- }
114
- }
115
- }
116
-
117
- // Check for additional properties
118
- if (schema.additionalProperties === false) {
119
- const allowedProps = Object.keys(schema.properties || {})
120
- const actualProps = Object.keys(dataObj)
121
-
122
- for (const prop of actualProps) {
123
- if (!allowedProps.includes(prop)) {
124
- result.warnings.push(`Plugin '${pluginName}' configuration has unexpected property: ${prop}`)
125
- }
126
- }
127
- }
128
- }
129
-
130
- /**
131
- * Validate individual property
132
- */
133
- private validateProperty(value: unknown, schema: Record<string, unknown>, path: string, result: PluginValidationResult): void {
134
- if (schema.type) {
135
- const actualType = Array.isArray(value) ? 'array' : typeof value
136
- if (actualType !== schema.type) {
137
- result.valid = false
138
- result.errors.push(`Property '${path}' must be of type ${schema.type}, got ${actualType}`)
139
- return
140
- }
141
- }
142
-
143
- // Type-specific validations
144
- switch (schema.type) {
145
- case 'string':
146
- this.validateStringProperty(value as string, schema, path, result)
147
- break
148
- case 'number':
149
- this.validateNumberProperty(value as number, schema, path, result)
150
- break
151
- case 'array':
152
- this.validateArrayProperty(value as unknown[], schema, path, result)
153
- break
154
- case 'object':
155
- if (schema.properties) {
156
- this.validateObjectProperty(value, schema, path, result)
157
- }
158
- break
159
- }
160
-
161
- // Enum validation
162
- if (schema.enum && !(schema.enum as unknown[]).includes(value)) {
163
- result.valid = false
164
- result.errors.push(`Property '${path}' must be one of: ${(schema.enum as unknown[]).join(', ')}`)
165
- }
166
- }
167
-
168
- /**
169
- * Validate string property
170
- */
171
- private validateStringProperty(value: string, schema: Record<string, unknown>, path: string, result: PluginValidationResult): void {
172
- if (schema.minLength && value.length < (schema.minLength as number)) {
173
- result.valid = false
174
- result.errors.push(`Property '${path}' must be at least ${schema.minLength} characters long`)
175
- }
176
-
177
- if (schema.maxLength && value.length > (schema.maxLength as number)) {
178
- result.valid = false
179
- result.errors.push(`Property '${path}' must be at most ${schema.maxLength} characters long`)
180
- }
181
-
182
- if (schema.pattern) {
183
- const regex = new RegExp(schema.pattern as string)
184
- if (!regex.test(value)) {
185
- result.valid = false
186
- result.errors.push(`Property '${path}' does not match required pattern: ${schema.pattern}`)
187
- }
188
- }
189
- }
190
-
191
- /**
192
- * Validate number property
193
- */
194
- private validateNumberProperty(value: number, schema: Record<string, unknown>, path: string, result: PluginValidationResult): void {
195
- if (schema.minimum !== undefined && value < (schema.minimum as number)) {
196
- result.valid = false
197
- result.errors.push(`Property '${path}' must be at least ${schema.minimum}`)
198
- }
199
-
200
- if (schema.maximum !== undefined && value > (schema.maximum as number)) {
201
- result.valid = false
202
- result.errors.push(`Property '${path}' must be at most ${schema.maximum}`)
203
- }
204
-
205
- if (schema.multipleOf && value % (schema.multipleOf as number) !== 0) {
206
- result.valid = false
207
- result.errors.push(`Property '${path}' must be a multiple of ${schema.multipleOf}`)
208
- }
209
- }
210
-
211
- /**
212
- * Validate array property
213
- */
214
- private validateArrayProperty(value: unknown[], schema: Record<string, unknown>, path: string, result: PluginValidationResult): void {
215
- if (schema.minItems && value.length < (schema.minItems as number)) {
216
- result.valid = false
217
- result.errors.push(`Property '${path}' must have at least ${schema.minItems} items`)
218
- }
219
-
220
- if (schema.maxItems && value.length > (schema.maxItems as number)) {
221
- result.valid = false
222
- result.errors.push(`Property '${path}' must have at most ${schema.maxItems} items`)
223
- }
224
-
225
- if (schema.items) {
226
- value.forEach((item, index) => {
227
- this.validateProperty(item, schema.items as Record<string, unknown>, `${path}[${index}]`, result)
228
- })
229
- }
230
- }
231
-
232
- /**
233
- * Validate object property
234
- */
235
- private validateObjectProperty(value: unknown, schema: Record<string, unknown>, path: string, result: PluginValidationResult): void {
236
- const valueObj = value as Record<string, unknown>
237
- if (schema.required) {
238
- for (const requiredProp of (schema.required as string[])) {
239
- if (!(requiredProp in valueObj)) {
240
- result.valid = false
241
- result.errors.push(`Property '${path}' missing required property: ${requiredProp}`)
242
- }
243
- }
244
- }
245
-
246
- if (schema.properties) {
247
- for (const [propName, propSchema] of Object.entries(schema.properties as Record<string, unknown>)) {
248
- if (propName in valueObj) {
249
- this.validateProperty(valueObj[propName], propSchema as Record<string, unknown>, `${path}.${propName}`, result)
250
- }
251
- }
252
- }
253
- }
254
-
255
- /**
256
- * Deep merge two objects
257
- */
258
- deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {
259
- if (source === null || source === undefined) {
260
- return target
261
- }
262
-
263
- if (target === null || target === undefined) {
264
- return source
265
- }
266
-
267
- if (typeof target !== 'object' || typeof source !== 'object') {
268
- return source
269
- }
270
-
271
- if (Array.isArray(source)) {
272
- return [...source] as unknown as Record<string, unknown>
273
- }
274
-
275
- const result = { ...target }
276
-
277
- for (const key in source) {
278
- if (source.hasOwnProperty(key)) {
279
- if (typeof source[key] === 'object' && !Array.isArray(source[key]) && source[key] !== null) {
280
- result[key] = this.deepMerge(target[key] as Record<string, unknown>, source[key] as Record<string, unknown>)
281
- } else {
282
- result[key] = source[key]
283
- }
284
- }
285
- }
286
-
287
- return result
288
- }
289
- }
290
-
291
- /** Shared instance — stateless, safe to reuse across all plugin utils */
292
- const sharedConfigManager = new DefaultPluginConfigManager()
293
-
294
- /**
295
- * Create plugin configuration utilities
296
- */
297
- export function createPluginUtils(logger?: Logger): PluginUtils {
298
- return {
299
- createTimer: (label: string) => {
300
- const start = Date.now()
301
- return {
302
- end: () => {
303
- const duration = Date.now() - start
304
- logger?.debug(`Timer '${label}' completed`, { duration })
305
- return duration
306
- }
307
- }
308
- },
309
-
310
- formatBytes: (bytes: number): string => {
311
- if (bytes === 0) return '0 Bytes'
312
- const k = 1024
313
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
314
- const i = Math.floor(Math.log(bytes) / Math.log(k))
315
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
316
- },
317
-
318
- isProduction: (): boolean => {
319
- return process.env.NODE_ENV === 'production'
320
- },
321
-
322
- isDevelopment: (): boolean => {
323
- return process.env.NODE_ENV === 'development'
324
- },
325
-
326
- getEnvironment: (): string => {
327
- return process.env.NODE_ENV || 'development'
328
- },
329
-
330
- createHash: (data: string): string => {
331
- // Simple hash function - in production, use crypto
332
- let hash = 0
333
- for (let i = 0; i < data.length; i++) {
334
- const char = data.charCodeAt(i)
335
- hash = ((hash << 5) - hash) + char
336
- hash = hash & hash // Convert to 32-bit integer
337
- }
338
- return hash.toString(36)
339
- },
340
-
341
- deepMerge: (target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> => {
342
- return sharedConfigManager.deepMerge(target, source)
343
- },
344
-
345
- validateSchema: (data: Record<string, unknown>, schema: PluginConfigSchema): { valid: boolean; errors: string[] } => {
346
- const result = sharedConfigManager.validatePluginConfig({ name: 'temp', configSchema: schema }, data)
347
- return {
348
- valid: result.valid,
349
- errors: result.errors
350
- }
351
- }
352
- }
353
- }
354
-
355
- // Export types for plugin utilities
356
- import type { PluginUtils } from "./types"