create-fluxstack 1.0.0 → 1.0.2

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 (101) hide show
  1. package/create-fluxstack.ts +32 -17
  2. package/package-template.json +51 -0
  3. package/package.json +2 -1
  4. package/.env +0 -30
  5. package/LICENSE +0 -21
  6. package/app/client/README.md +0 -69
  7. package/app/client/frontend-only.ts +0 -12
  8. package/app/client/index.html +0 -13
  9. package/app/client/public/vite.svg +0 -1
  10. package/app/client/src/App.css +0 -883
  11. package/app/client/src/App.tsx +0 -669
  12. package/app/client/src/assets/react.svg +0 -1
  13. package/app/client/src/components/TestPage.tsx +0 -453
  14. package/app/client/src/index.css +0 -51
  15. package/app/client/src/lib/eden-api.ts +0 -110
  16. package/app/client/src/main.tsx +0 -10
  17. package/app/client/src/vite-env.d.ts +0 -1
  18. package/app/client/tsconfig.app.json +0 -43
  19. package/app/client/tsconfig.json +0 -7
  20. package/app/client/tsconfig.node.json +0 -25
  21. package/app/server/app.ts +0 -10
  22. package/app/server/backend-only.ts +0 -15
  23. package/app/server/controllers/users.controller.ts +0 -69
  24. package/app/server/index.ts +0 -104
  25. package/app/server/routes/index.ts +0 -25
  26. package/app/server/routes/users.routes.ts +0 -121
  27. package/app/server/types/index.ts +0 -1
  28. package/app/shared/types/index.ts +0 -18
  29. package/bun.lock +0 -1053
  30. package/core/__tests__/integration.test.ts +0 -227
  31. package/core/build/index.ts +0 -186
  32. package/core/cli/command-registry.ts +0 -334
  33. package/core/cli/index.ts +0 -394
  34. package/core/cli/plugin-discovery.ts +0 -200
  35. package/core/client/standalone.ts +0 -57
  36. package/core/config/__tests__/config-loader.test.ts +0 -591
  37. package/core/config/__tests__/config-merger.test.ts +0 -657
  38. package/core/config/__tests__/env-converter.test.ts +0 -372
  39. package/core/config/__tests__/env-processor.test.ts +0 -431
  40. package/core/config/__tests__/env.test.ts +0 -452
  41. package/core/config/__tests__/integration.test.ts +0 -418
  42. package/core/config/__tests__/loader.test.ts +0 -331
  43. package/core/config/__tests__/schema.test.ts +0 -129
  44. package/core/config/__tests__/validator.test.ts +0 -318
  45. package/core/config/env-dynamic.ts +0 -326
  46. package/core/config/env.ts +0 -597
  47. package/core/config/index.ts +0 -317
  48. package/core/config/loader.ts +0 -546
  49. package/core/config/runtime-config.ts +0 -322
  50. package/core/config/schema.ts +0 -694
  51. package/core/config/validator.ts +0 -540
  52. package/core/framework/__tests__/server.test.ts +0 -233
  53. package/core/framework/client.ts +0 -132
  54. package/core/framework/index.ts +0 -8
  55. package/core/framework/server.ts +0 -501
  56. package/core/framework/types.ts +0 -63
  57. package/core/plugins/__tests__/built-in.test.ts.disabled +0 -366
  58. package/core/plugins/__tests__/manager.test.ts +0 -398
  59. package/core/plugins/__tests__/monitoring.test.ts +0 -401
  60. package/core/plugins/__tests__/registry.test.ts +0 -335
  61. package/core/plugins/built-in/index.ts +0 -142
  62. package/core/plugins/built-in/logger/index.ts +0 -180
  63. package/core/plugins/built-in/monitoring/README.md +0 -193
  64. package/core/plugins/built-in/monitoring/index.ts +0 -912
  65. package/core/plugins/built-in/static/index.ts +0 -289
  66. package/core/plugins/built-in/swagger/index.ts +0 -229
  67. package/core/plugins/built-in/vite/index.ts +0 -316
  68. package/core/plugins/config.ts +0 -348
  69. package/core/plugins/discovery.ts +0 -350
  70. package/core/plugins/executor.ts +0 -351
  71. package/core/plugins/index.ts +0 -195
  72. package/core/plugins/manager.ts +0 -583
  73. package/core/plugins/registry.ts +0 -424
  74. package/core/plugins/types.ts +0 -254
  75. package/core/server/framework.ts +0 -123
  76. package/core/server/index.ts +0 -8
  77. package/core/server/plugins/database.ts +0 -182
  78. package/core/server/plugins/logger.ts +0 -47
  79. package/core/server/plugins/swagger.ts +0 -34
  80. package/core/server/standalone.ts +0 -91
  81. package/core/templates/create-project.ts +0 -455
  82. package/core/types/api.ts +0 -169
  83. package/core/types/build.ts +0 -174
  84. package/core/types/config.ts +0 -68
  85. package/core/types/index.ts +0 -127
  86. package/core/types/plugin.ts +0 -94
  87. package/core/utils/__tests__/errors.test.ts +0 -139
  88. package/core/utils/__tests__/helpers.test.ts +0 -297
  89. package/core/utils/__tests__/logger.test.ts +0 -141
  90. package/core/utils/env-runtime-v2.ts +0 -232
  91. package/core/utils/env-runtime.ts +0 -252
  92. package/core/utils/errors/codes.ts +0 -115
  93. package/core/utils/errors/handlers.ts +0 -63
  94. package/core/utils/errors/index.ts +0 -81
  95. package/core/utils/helpers.ts +0 -180
  96. package/core/utils/index.ts +0 -18
  97. package/core/utils/logger/index.ts +0 -161
  98. package/core/utils/logger.ts +0 -106
  99. package/core/utils/monitoring/index.ts +0 -212
  100. package/tsconfig.json +0 -51
  101. package/vite.config.ts +0 -42
@@ -1,316 +0,0 @@
1
- import type { Plugin, PluginContext, RequestContext } from "../../types"
2
- import { createServer, type ViteDevServer } from 'vite'
3
-
4
- let viteServer: ViteDevServer | null = null
5
-
6
- export const vitePlugin: Plugin = {
7
- name: "vite",
8
- version: "1.0.0",
9
- description: "Enhanced Vite integration plugin for FluxStack with improved error handling and monitoring",
10
- author: "FluxStack Team",
11
- priority: "high", // Should run early to setup proxying
12
- category: "development",
13
- tags: ["vite", "development", "hot-reload"],
14
- dependencies: [], // No dependencies
15
-
16
- configSchema: {
17
- type: "object",
18
- properties: {
19
- enabled: {
20
- type: "boolean",
21
- description: "Enable Vite integration"
22
- },
23
- port: {
24
- type: "number",
25
- minimum: 1,
26
- maximum: 65535,
27
- description: "Vite development server port"
28
- },
29
- host: {
30
- type: "string",
31
- description: "Vite development server host"
32
- },
33
- checkInterval: {
34
- type: "number",
35
- minimum: 100,
36
- description: "Interval to check if Vite is running (ms)"
37
- },
38
- maxRetries: {
39
- type: "number",
40
- minimum: 1,
41
- description: "Maximum retries to connect to Vite"
42
- },
43
- timeout: {
44
- type: "number",
45
- minimum: 100,
46
- description: "Timeout for Vite requests (ms)"
47
- },
48
- proxyPaths: {
49
- type: "array",
50
- items: { type: "string" },
51
- description: "Paths to proxy to Vite (defaults to all non-API paths)"
52
- },
53
- excludePaths: {
54
- type: "array",
55
- items: { type: "string" },
56
- description: "Paths to exclude from Vite proxying"
57
- }
58
- },
59
- additionalProperties: false
60
- },
61
-
62
- defaultConfig: {
63
- enabled: true,
64
- port: 5173,
65
- host: "localhost",
66
- checkInterval: 2000,
67
- maxRetries: 10,
68
- timeout: 5000,
69
- proxyPaths: [],
70
- excludePaths: []
71
- },
72
-
73
- setup: async (context: PluginContext) => {
74
- const config = getPluginConfig(context)
75
-
76
- if (!config.enabled || !context.config.client) {
77
- context.logger.info('Vite plugin disabled or no client configuration found')
78
- return
79
- }
80
-
81
- const vitePort = config.port || context.config.client.port || 5173
82
- const viteHost = config.host || "localhost"
83
-
84
- context.logger.info(`🎨 Starting Vite dev server programmatically on ${viteHost}:${vitePort}`)
85
-
86
- try {
87
- // Start Vite dev server programmatically
88
- viteServer = await createServer({
89
- configFile: './vite.config.ts',
90
- // Don't override root - let vite.config.ts handle it
91
- server: {
92
- port: vitePort,
93
- host: viteHost
94
- }
95
- })
96
-
97
- await viteServer.listen()
98
- viteServer.printUrls()
99
-
100
- context.logger.info(`✅ Vite server started successfully on ${viteHost}:${vitePort}`)
101
- context.logger.info('🔄 Hot reload coordination active - Zero órfãos!')
102
-
103
- // Store Vite config in context for later use
104
- ;(context as any).viteConfig = {
105
- port: vitePort,
106
- host: viteHost,
107
- ...config,
108
- server: viteServer
109
- }
110
-
111
- // Setup cleanup on process exit
112
- const cleanup = async () => {
113
- if (viteServer) {
114
- context.logger.info('🛑 Stopping Vite server...')
115
- await viteServer.close()
116
- viteServer = null
117
- }
118
- }
119
-
120
- process.on('SIGINT', cleanup)
121
- process.on('SIGTERM', cleanup)
122
- process.on('exit', cleanup)
123
-
124
- } catch (error) {
125
- context.logger.error('❌ Failed to start Vite server programmatically:', error)
126
- context.logger.info('⚠️ Falling back to monitoring mode...')
127
-
128
- // Fallback to monitoring if programmatic start fails
129
- ;(context as any).viteConfig = {
130
- port: vitePort,
131
- host: viteHost,
132
- ...config
133
- }
134
- monitorVite(context, viteHost, vitePort, config)
135
- }
136
- },
137
-
138
- onServerStart: async (context: PluginContext) => {
139
- const config = getPluginConfig(context)
140
- const viteConfig = (context as any).viteConfig
141
-
142
- if (config.enabled && viteConfig) {
143
- context.logger.info(`Vite integration active - monitoring ${viteConfig.host}:${viteConfig.port}`)
144
- }
145
- },
146
-
147
- onBeforeRoute: async (requestContext: RequestContext) => {
148
- // Skip API routes and swagger - let them be handled by backend
149
- if (requestContext.path.startsWith("/api") || requestContext.path.startsWith("/swagger")) {
150
- return
151
- }
152
-
153
- // Use fixed configuration for simplicity - Vite should be running on port 5173
154
- const viteHost = "localhost"
155
- const vitePort = 5173
156
-
157
- try {
158
- const url = new URL(requestContext.request.url)
159
- const viteUrl = `http://${viteHost}:${vitePort}${requestContext.path}${url.search}`
160
-
161
- // Forward request to Vite
162
- const response = await fetch(viteUrl, {
163
- method: requestContext.method,
164
- headers: requestContext.headers,
165
- body: requestContext.method !== 'GET' && requestContext.method !== 'HEAD' ? requestContext.request.body : undefined
166
- })
167
-
168
- // If Vite responds successfully, handle the request
169
- if (response.ok || response.status < 500) {
170
- // Return a proper Response object with all headers and status
171
- const body = await response.arrayBuffer()
172
-
173
- requestContext.handled = true
174
- requestContext.response = new Response(body, {
175
- status: response.status,
176
- statusText: response.statusText,
177
- headers: response.headers
178
- })
179
- }
180
-
181
- } catch (viteError) {
182
- // If Vite fails, let the request continue to normal routing (will become 404)
183
- // Only log if explicitly enabled for debugging
184
- if (process.env.ENABLE_VITE_PROXY_LOGS === 'true') {
185
- console.warn(`Vite proxy error: ${viteError}`)
186
- }
187
- }
188
- }
189
- }
190
-
191
- // Helper function to get plugin config
192
- function getPluginConfig(context: PluginContext) {
193
- const pluginConfig = context.config.plugins.config?.vite || {}
194
- return { ...vitePlugin.defaultConfig, ...pluginConfig }
195
- }
196
-
197
- // Monitor Vite server status with automatic port detection
198
- async function monitorVite(
199
- context: PluginContext,
200
- host: string,
201
- initialPort: number,
202
- config: any
203
- ) {
204
- let retries = 0
205
- let isConnected = false
206
- let actualPort = initialPort
207
- let portDetected = false
208
-
209
- const checkVite = async () => {
210
- try {
211
- // If we haven't found the correct port yet, try to detect it
212
- if (!portDetected) {
213
- const detectedPort = await detectVitePort(host, initialPort)
214
- if (detectedPort !== null) {
215
- actualPort = detectedPort
216
- portDetected = true
217
- // Update the context with the detected port
218
- if ((context as any).viteConfig) {
219
- ;(context as any).viteConfig.port = actualPort
220
- }
221
- }
222
- }
223
-
224
- const isRunning = await checkViteRunning(host, actualPort, config.timeout)
225
-
226
- if (isRunning && !isConnected) {
227
- isConnected = true
228
- retries = 0
229
- if (actualPort !== initialPort) {
230
- context.logger.info(`✓ Vite server detected on ${host}:${actualPort} (auto-detected from port ${initialPort})`)
231
- } else {
232
- context.logger.info(`✓ Vite server detected on ${host}:${actualPort}`)
233
- }
234
- context.logger.info("Hot reload coordination active")
235
- } else if (!isRunning && isConnected) {
236
- isConnected = false
237
- context.logger.warn(`✗ Vite server disconnected from ${host}:${actualPort}`)
238
- // Reset port detection when disconnected
239
- portDetected = false
240
- actualPort = initialPort
241
- } else if (!isRunning) {
242
- retries++
243
- if (retries <= config.maxRetries) {
244
- if (portDetected) {
245
- context.logger.debug(`Waiting for Vite server on ${host}:${actualPort}... (${retries}/${config.maxRetries})`)
246
- } else {
247
- context.logger.debug(`Detecting Vite server port... (${retries}/${config.maxRetries})`)
248
- }
249
- } else if (retries === config.maxRetries + 1) {
250
- context.logger.warn(`Vite server not found after ${config.maxRetries} attempts. Development features may be limited.`)
251
- }
252
- }
253
- } catch (error) {
254
- if (isConnected) {
255
- context.logger.error('Error checking Vite server status', { error })
256
- }
257
- }
258
-
259
- // Continue monitoring
260
- setTimeout(checkVite, config.checkInterval)
261
- }
262
-
263
- // Start monitoring after a brief delay
264
- setTimeout(checkVite, 1000)
265
- }
266
-
267
- // Auto-detect Vite port by trying common ports
268
- async function detectVitePort(host: string, startPort: number): Promise<number | null> {
269
- // Try the initial port first, then common alternatives
270
- const portsToTry = [
271
- startPort,
272
- startPort + 1,
273
- startPort + 2,
274
- startPort + 3,
275
- 5174, // Common Vite alternative
276
- 5175,
277
- 5176,
278
- 3000, // Sometimes Vite might use this
279
- 4173 // Another common alternative
280
- ]
281
-
282
- for (const port of portsToTry) {
283
- try {
284
- const isRunning = await checkViteRunning(host, port, 1000)
285
- if (isRunning) {
286
- return port
287
- }
288
- } catch (error) {
289
- // Continue trying other ports
290
- }
291
- }
292
-
293
- return null
294
- }
295
-
296
- // Check if Vite is running
297
- async function checkViteRunning(host: string, port: number, timeout: number = 1000): Promise<boolean> {
298
- try {
299
- const controller = new AbortController()
300
- const timeoutId = setTimeout(() => controller.abort(), timeout)
301
-
302
- const response = await fetch(`http://${host}:${port}`, {
303
- signal: controller.signal,
304
- method: 'HEAD' // Use HEAD to minimize data transfer
305
- })
306
-
307
- clearTimeout(timeoutId)
308
- return response.status >= 200 && response.status < 500
309
- } catch (error) {
310
- return false
311
- }
312
- }
313
-
314
- // Note: Proxy logic is now handled directly in the onBeforeRoute hook above
315
-
316
- export default vitePlugin
@@ -1,348 +0,0 @@
1
- /**
2
- * Plugin Configuration Management
3
- * Handles plugin-specific configuration validation and management
4
- */
5
-
6
- import type { Plugin, PluginConfigSchema, PluginValidationResult } from "./types"
7
- import type { FluxStackConfig } from "../config/schema"
8
- import type { Logger } from "../utils/logger/index"
9
-
10
- export interface PluginConfigManager {
11
- validatePluginConfig(plugin: Plugin, config: any): PluginValidationResult
12
- mergePluginConfig(plugin: Plugin, userConfig: any): any
13
- getPluginConfig(pluginName: string, config: FluxStackConfig): any
14
- setPluginConfig(pluginName: string, pluginConfig: any, config: FluxStackConfig): void
15
- }
16
-
17
- export class DefaultPluginConfigManager implements PluginConfigManager {
18
- constructor(_logger?: Logger) {
19
- // Logger stored but not used in current implementation
20
- }
21
-
22
- /**
23
- * Validate plugin configuration against its schema
24
- */
25
- validatePluginConfig(plugin: Plugin, config: any): PluginValidationResult {
26
- const result: PluginValidationResult = {
27
- valid: true,
28
- errors: [],
29
- warnings: []
30
- }
31
-
32
- if (!plugin.configSchema) {
33
- // No schema means any config is valid
34
- return result
35
- }
36
-
37
- try {
38
- this.validateAgainstSchema(config, plugin.configSchema, plugin.name, result)
39
- } catch (error) {
40
- result.valid = false
41
- result.errors.push(`Configuration validation failed: ${error instanceof Error ? error.message : String(error)}`)
42
- }
43
-
44
- return result
45
- }
46
-
47
- /**
48
- * Merge user configuration with plugin defaults
49
- */
50
- mergePluginConfig(plugin: Plugin, userConfig: any): any {
51
- const defaultConfig = plugin.defaultConfig || {}
52
-
53
- if (!userConfig) {
54
- return defaultConfig
55
- }
56
-
57
- return this.deepMerge(defaultConfig, userConfig)
58
- }
59
-
60
- /**
61
- * Get plugin configuration from main config
62
- */
63
- getPluginConfig(pluginName: string, config: FluxStackConfig): any {
64
- return config.plugins.config[pluginName] || {}
65
- }
66
-
67
- /**
68
- * Set plugin configuration in main config
69
- */
70
- setPluginConfig(pluginName: string, pluginConfig: any, config: FluxStackConfig): void {
71
- if (!config.plugins.config) {
72
- config.plugins.config = {}
73
- }
74
- config.plugins.config[pluginName] = pluginConfig
75
- }
76
-
77
- /**
78
- * Validate configuration against JSON schema
79
- */
80
- private validateAgainstSchema(
81
- data: any,
82
- schema: PluginConfigSchema,
83
- pluginName: string,
84
- result: PluginValidationResult
85
- ): void {
86
- if (schema.type === 'object' && typeof data !== 'object') {
87
- result.valid = false
88
- result.errors.push(`Plugin '${pluginName}' configuration must be an object`)
89
- return
90
- }
91
-
92
- // Check required properties
93
- if (schema.required && Array.isArray(schema.required)) {
94
- for (const requiredProp of schema.required) {
95
- if (!(requiredProp in data)) {
96
- result.valid = false
97
- result.errors.push(`Plugin '${pluginName}' configuration missing required property: ${requiredProp}`)
98
- }
99
- }
100
- }
101
-
102
- // Validate properties
103
- if (schema.properties) {
104
- for (const [propName, propSchema] of Object.entries(schema.properties)) {
105
- if (propName in data) {
106
- this.validateProperty(data[propName], propSchema, `${pluginName}.${propName}`, result)
107
- }
108
- }
109
- }
110
-
111
- // Check for additional properties
112
- if (schema.additionalProperties === false) {
113
- const allowedProps = Object.keys(schema.properties || {})
114
- const actualProps = Object.keys(data)
115
-
116
- for (const prop of actualProps) {
117
- if (!allowedProps.includes(prop)) {
118
- result.warnings.push(`Plugin '${pluginName}' configuration has unexpected property: ${prop}`)
119
- }
120
- }
121
- }
122
- }
123
-
124
- /**
125
- * Validate individual property
126
- */
127
- private validateProperty(value: any, schema: any, path: string, result: PluginValidationResult): void {
128
- if (schema.type) {
129
- const actualType = Array.isArray(value) ? 'array' : typeof value
130
- if (actualType !== schema.type) {
131
- result.valid = false
132
- result.errors.push(`Property '${path}' must be of type ${schema.type}, got ${actualType}`)
133
- return
134
- }
135
- }
136
-
137
- // Type-specific validations
138
- switch (schema.type) {
139
- case 'string':
140
- this.validateStringProperty(value, schema, path, result)
141
- break
142
- case 'number':
143
- this.validateNumberProperty(value, schema, path, result)
144
- break
145
- case 'array':
146
- this.validateArrayProperty(value, schema, path, result)
147
- break
148
- case 'object':
149
- if (schema.properties) {
150
- this.validateObjectProperty(value, schema, path, result)
151
- }
152
- break
153
- }
154
-
155
- // Enum validation
156
- if (schema.enum && !schema.enum.includes(value)) {
157
- result.valid = false
158
- result.errors.push(`Property '${path}' must be one of: ${schema.enum.join(', ')}`)
159
- }
160
- }
161
-
162
- /**
163
- * Validate string property
164
- */
165
- private validateStringProperty(value: string, schema: any, path: string, result: PluginValidationResult): void {
166
- if (schema.minLength && value.length < schema.minLength) {
167
- result.valid = false
168
- result.errors.push(`Property '${path}' must be at least ${schema.minLength} characters long`)
169
- }
170
-
171
- if (schema.maxLength && value.length > schema.maxLength) {
172
- result.valid = false
173
- result.errors.push(`Property '${path}' must be at most ${schema.maxLength} characters long`)
174
- }
175
-
176
- if (schema.pattern) {
177
- const regex = new RegExp(schema.pattern)
178
- if (!regex.test(value)) {
179
- result.valid = false
180
- result.errors.push(`Property '${path}' does not match required pattern: ${schema.pattern}`)
181
- }
182
- }
183
- }
184
-
185
- /**
186
- * Validate number property
187
- */
188
- private validateNumberProperty(value: number, schema: any, path: string, result: PluginValidationResult): void {
189
- if (schema.minimum !== undefined && value < schema.minimum) {
190
- result.valid = false
191
- result.errors.push(`Property '${path}' must be at least ${schema.minimum}`)
192
- }
193
-
194
- if (schema.maximum !== undefined && value > schema.maximum) {
195
- result.valid = false
196
- result.errors.push(`Property '${path}' must be at most ${schema.maximum}`)
197
- }
198
-
199
- if (schema.multipleOf && value % schema.multipleOf !== 0) {
200
- result.valid = false
201
- result.errors.push(`Property '${path}' must be a multiple of ${schema.multipleOf}`)
202
- }
203
- }
204
-
205
- /**
206
- * Validate array property
207
- */
208
- private validateArrayProperty(value: any[], schema: any, path: string, result: PluginValidationResult): void {
209
- if (schema.minItems && value.length < schema.minItems) {
210
- result.valid = false
211
- result.errors.push(`Property '${path}' must have at least ${schema.minItems} items`)
212
- }
213
-
214
- if (schema.maxItems && value.length > schema.maxItems) {
215
- result.valid = false
216
- result.errors.push(`Property '${path}' must have at most ${schema.maxItems} items`)
217
- }
218
-
219
- if (schema.items) {
220
- value.forEach((item, index) => {
221
- this.validateProperty(item, schema.items, `${path}[${index}]`, result)
222
- })
223
- }
224
- }
225
-
226
- /**
227
- * Validate object property
228
- */
229
- private validateObjectProperty(value: any, schema: any, path: string, result: PluginValidationResult): void {
230
- if (schema.required) {
231
- for (const requiredProp of schema.required) {
232
- if (!(requiredProp in value)) {
233
- result.valid = false
234
- result.errors.push(`Property '${path}' missing required property: ${requiredProp}`)
235
- }
236
- }
237
- }
238
-
239
- if (schema.properties) {
240
- for (const [propName, propSchema] of Object.entries(schema.properties)) {
241
- if (propName in value) {
242
- this.validateProperty(value[propName], propSchema, `${path}.${propName}`, result)
243
- }
244
- }
245
- }
246
- }
247
-
248
- /**
249
- * Deep merge two objects
250
- */
251
- private deepMerge(target: any, source: any): any {
252
- if (source === null || source === undefined) {
253
- return target
254
- }
255
-
256
- if (target === null || target === undefined) {
257
- return source
258
- }
259
-
260
- if (typeof target !== 'object' || typeof source !== 'object') {
261
- return source
262
- }
263
-
264
- if (Array.isArray(source)) {
265
- return [...source]
266
- }
267
-
268
- const result = { ...target }
269
-
270
- for (const key in source) {
271
- if (source.hasOwnProperty(key)) {
272
- if (typeof source[key] === 'object' && !Array.isArray(source[key]) && source[key] !== null) {
273
- result[key] = this.deepMerge(target[key], source[key])
274
- } else {
275
- result[key] = source[key]
276
- }
277
- }
278
- }
279
-
280
- return result
281
- }
282
- }
283
-
284
- /**
285
- * Create plugin configuration utilities
286
- */
287
- export function createPluginUtils(logger?: Logger): PluginUtils {
288
- return {
289
- createTimer: (label: string) => {
290
- const start = Date.now()
291
- return {
292
- end: () => {
293
- const duration = Date.now() - start
294
- logger?.debug(`Timer '${label}' completed`, { duration })
295
- return duration
296
- }
297
- }
298
- },
299
-
300
- formatBytes: (bytes: number): string => {
301
- if (bytes === 0) return '0 Bytes'
302
- const k = 1024
303
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']
304
- const i = Math.floor(Math.log(bytes) / Math.log(k))
305
- return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
306
- },
307
-
308
- isProduction: (): boolean => {
309
- return process.env.NODE_ENV === 'production'
310
- },
311
-
312
- isDevelopment: (): boolean => {
313
- return process.env.NODE_ENV === 'development'
314
- },
315
-
316
- getEnvironment: (): string => {
317
- return process.env.NODE_ENV || 'development'
318
- },
319
-
320
- createHash: (data: string): string => {
321
- // Simple hash function - in production, use crypto
322
- let hash = 0
323
- for (let i = 0; i < data.length; i++) {
324
- const char = data.charCodeAt(i)
325
- hash = ((hash << 5) - hash) + char
326
- hash = hash & hash // Convert to 32-bit integer
327
- }
328
- return hash.toString(36)
329
- },
330
-
331
- deepMerge: (target: any, source: any): any => {
332
- const manager = new DefaultPluginConfigManager()
333
- return (manager as any).deepMerge(target, source)
334
- },
335
-
336
- validateSchema: (data: any, schema: any): { valid: boolean; errors: string[] } => {
337
- const manager = new DefaultPluginConfigManager()
338
- const result = manager.validatePluginConfig({ name: 'temp', configSchema: schema }, data)
339
- return {
340
- valid: result.valid,
341
- errors: result.errors
342
- }
343
- }
344
- }
345
- }
346
-
347
- // Export types for plugin utilities
348
- import type { PluginUtils } from "./types"