create-fluxstack 1.12.1 โ†’ 1.14.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 (116) hide show
  1. package/LLMD/INDEX.md +8 -1
  2. package/LLMD/agent.md +867 -0
  3. package/LLMD/config/environment-vars.md +30 -0
  4. package/LLMD/patterns/anti-patterns.md +100 -0
  5. package/LLMD/reference/routing.md +39 -39
  6. package/LLMD/resources/live-auth.md +465 -0
  7. package/LLMD/resources/live-components.md +168 -26
  8. package/LLMD/resources/live-logging.md +220 -0
  9. package/LLMD/resources/live-upload.md +59 -8
  10. package/LLMD/resources/rest-auth.md +290 -0
  11. package/README.md +520 -340
  12. package/app/client/index.html +2 -2
  13. package/app/client/public/favicon.svg +46 -0
  14. package/app/client/src/App.tsx +13 -1
  15. package/app/client/src/assets/fluxstack-static.svg +46 -0
  16. package/app/client/src/assets/fluxstack.svg +183 -0
  17. package/app/client/src/components/AppLayout.tsx +139 -9
  18. package/app/client/src/components/BackButton.tsx +13 -13
  19. package/app/client/src/components/DemoPage.tsx +4 -4
  20. package/app/client/src/live/AuthDemo.tsx +334 -0
  21. package/app/client/src/live/ChatDemo.tsx +2 -2
  22. package/app/client/src/live/CounterDemo.tsx +12 -12
  23. package/app/client/src/live/FormDemo.tsx +2 -2
  24. package/app/client/src/live/LiveDebuggerPanel.tsx +779 -0
  25. package/app/client/src/live/RoomChatDemo.tsx +24 -16
  26. package/app/client/src/main.tsx +13 -13
  27. package/app/client/src/pages/ApiTestPage.tsx +6 -6
  28. package/app/client/src/pages/HomePage.tsx +80 -52
  29. package/app/server/auth/AuthManager.ts +213 -0
  30. package/app/server/auth/DevAuthProvider.ts +66 -0
  31. package/app/server/auth/HashManager.ts +123 -0
  32. package/app/server/auth/JWTAuthProvider.example.ts +101 -0
  33. package/app/server/auth/RateLimiter.ts +106 -0
  34. package/app/server/auth/contracts.ts +192 -0
  35. package/app/server/auth/guards/SessionGuard.ts +167 -0
  36. package/app/server/auth/guards/TokenGuard.ts +202 -0
  37. package/app/server/auth/index.ts +174 -0
  38. package/app/server/auth/middleware.ts +163 -0
  39. package/app/server/auth/providers/InMemoryProvider.ts +162 -0
  40. package/app/server/auth/sessions/SessionManager.ts +164 -0
  41. package/app/server/cache/CacheManager.ts +81 -0
  42. package/app/server/cache/MemoryDriver.ts +112 -0
  43. package/app/server/cache/contracts.ts +49 -0
  44. package/app/server/cache/index.ts +42 -0
  45. package/app/server/index.ts +14 -0
  46. package/app/server/live/LiveAdminPanel.ts +174 -0
  47. package/app/server/live/LiveChat.ts +78 -77
  48. package/app/server/live/LiveCounter.ts +1 -0
  49. package/app/server/live/LiveForm.ts +1 -0
  50. package/app/server/live/LiveLocalCounter.ts +38 -32
  51. package/app/server/live/LiveProtectedChat.ts +151 -0
  52. package/app/server/live/LiveRoomChat.ts +1 -0
  53. package/app/server/live/LiveUpload.ts +1 -0
  54. package/app/server/live/register-components.ts +19 -19
  55. package/app/server/routes/auth.routes.ts +278 -0
  56. package/app/server/routes/index.ts +2 -0
  57. package/config/index.ts +8 -0
  58. package/config/system/auth.config.ts +49 -0
  59. package/config/system/runtime.config.ts +4 -0
  60. package/config/system/session.config.ts +33 -0
  61. package/core/build/optimizer.ts +235 -235
  62. package/core/client/LiveComponentsProvider.tsx +76 -5
  63. package/core/client/components/Live.tsx +17 -10
  64. package/core/client/components/LiveDebugger.tsx +1324 -0
  65. package/core/client/hooks/AdaptiveChunkSizer.ts +215 -215
  66. package/core/client/hooks/useLiveComponent.ts +58 -5
  67. package/core/client/hooks/useLiveDebugger.ts +392 -0
  68. package/core/client/index.ts +16 -1
  69. package/core/framework/server.ts +36 -4
  70. package/core/plugins/built-in/index.ts +134 -134
  71. package/core/plugins/built-in/live-components/commands/create-live-component.ts +19 -8
  72. package/core/plugins/built-in/monitoring/index.ts +10 -3
  73. package/core/plugins/built-in/vite/index.ts +151 -20
  74. package/core/plugins/config.ts +5 -4
  75. package/core/plugins/discovery.ts +11 -2
  76. package/core/plugins/manager.ts +11 -5
  77. package/core/plugins/module-resolver.ts +1 -1
  78. package/core/plugins/registry.ts +53 -25
  79. package/core/server/index.ts +15 -15
  80. package/core/server/live/ComponentRegistry.ts +134 -50
  81. package/core/server/live/FileUploadManager.ts +188 -24
  82. package/core/server/live/LiveComponentPerformanceMonitor.ts +9 -8
  83. package/core/server/live/LiveDebugger.ts +462 -0
  84. package/core/server/live/LiveLogger.ts +144 -0
  85. package/core/server/live/LiveRoomManager.ts +22 -5
  86. package/core/server/live/StateSignature.ts +704 -643
  87. package/core/server/live/WebSocketConnectionManager.ts +11 -10
  88. package/core/server/live/auth/LiveAuthContext.ts +71 -0
  89. package/core/server/live/auth/LiveAuthManager.ts +304 -0
  90. package/core/server/live/auth/index.ts +19 -0
  91. package/core/server/live/auth/types.ts +179 -0
  92. package/core/server/live/auto-generated-components.ts +8 -2
  93. package/core/server/live/index.ts +16 -0
  94. package/core/server/live/websocket-plugin.ts +323 -22
  95. package/core/server/plugins/static-files-plugin.ts +179 -69
  96. package/core/templates/create-project.ts +0 -3
  97. package/core/types/build.ts +219 -219
  98. package/core/types/plugin.ts +107 -107
  99. package/core/types/types.ts +278 -22
  100. package/core/utils/index.ts +17 -17
  101. package/core/utils/logger/index.ts +5 -2
  102. package/core/utils/logger/startup-banner.ts +82 -82
  103. package/core/utils/version.ts +6 -6
  104. package/package.json +1 -8
  105. package/plugins/crypto-auth/index.ts +6 -0
  106. package/plugins/crypto-auth/server/CryptoAuthLiveProvider.ts +58 -0
  107. package/plugins/crypto-auth/server/index.ts +24 -21
  108. package/rest-tests/README.md +57 -0
  109. package/rest-tests/auth-token.http +113 -0
  110. package/rest-tests/auth.http +112 -0
  111. package/rest-tests/rooms-token.http +69 -0
  112. package/rest-tests/users-token.http +62 -0
  113. package/.dockerignore +0 -81
  114. package/Dockerfile +0 -70
  115. package/LIVE_COMPONENTS_REVIEW.md +0 -781
  116. package/app/client/src/assets/react.svg +0 -1
@@ -1,35 +1,115 @@
1
1
  import { writeFile, mkdir, unlink } from 'fs/promises'
2
2
  import { existsSync } from 'fs'
3
- import { join, extname } from 'path'
4
- import type {
5
- ActiveUpload,
6
- FileUploadStartMessage,
3
+ import { join, extname, basename } from 'path'
4
+ import { liveLog, liveWarn } from './LiveLogger'
5
+ import type {
6
+ ActiveUpload,
7
+ FileUploadStartMessage,
7
8
  FileUploadChunkMessage,
8
9
  FileUploadCompleteMessage,
9
10
  FileUploadProgressResponse,
10
11
  FileUploadCompleteResponse
11
12
  } from '@core/types/types'
12
13
 
14
+ // ๐Ÿ”’ Magic bytes mapping for content validation
15
+ // Validates actual file content, not just the MIME type header
16
+ const MAGIC_BYTES: Record<string, { bytes: number[]; offset?: number }[]> = {
17
+ 'image/jpeg': [{ bytes: [0xFF, 0xD8, 0xFF] }],
18
+ 'image/png': [{ bytes: [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A] }],
19
+ 'image/gif': [
20
+ { bytes: [0x47, 0x49, 0x46, 0x38, 0x37, 0x61] }, // GIF87a
21
+ { bytes: [0x47, 0x49, 0x46, 0x38, 0x39, 0x61] }, // GIF89a
22
+ ],
23
+ 'image/webp': [
24
+ { bytes: [0x52, 0x49, 0x46, 0x46], offset: 0 }, // RIFF header
25
+ // Byte 8-11 should be WEBP, checked separately
26
+ ],
27
+ 'application/pdf': [{ bytes: [0x25, 0x50, 0x44, 0x46] }], // %PDF
28
+ 'application/zip': [
29
+ { bytes: [0x50, 0x4B, 0x03, 0x04] }, // PK\x03\x04
30
+ { bytes: [0x50, 0x4B, 0x05, 0x06] }, // Empty archive
31
+ ],
32
+ 'application/gzip': [{ bytes: [0x1F, 0x8B] }],
33
+ }
34
+
13
35
  export class FileUploadManager {
14
36
  private activeUploads = new Map<string, ActiveUpload>()
15
- private readonly maxUploadSize = 500 * 1024 * 1024 // 500MB max (aceita qualquer arquivo)
37
+ private readonly maxUploadSize = 50 * 1024 * 1024 // ๐Ÿ”’ 50MB max (reduced from 500MB)
16
38
  private readonly chunkTimeout = 30000 // 30 seconds timeout per chunk
17
- private readonly allowedTypes: string[] = [] // Array vazio = aceita todos os tipos de arquivo
39
+ // ๐Ÿ”’ Per-user upload quota tracking
40
+ private userUploadBytes = new Map<string, number>() // userId -> total bytes uploaded
41
+ private readonly maxBytesPerUser = 500 * 1024 * 1024 // ๐Ÿ”’ 500MB per user total
42
+ private readonly quotaResetInterval = 24 * 60 * 60 * 1000 // Reset quotas daily
43
+ // ๐Ÿ”’ Default allowed MIME types - safe file types only
44
+ private readonly allowedTypes: string[] = [
45
+ 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml',
46
+ 'application/pdf',
47
+ 'text/plain', 'text/csv', 'text/markdown',
48
+ 'application/json',
49
+ 'application/zip', 'application/gzip',
50
+ ]
51
+ // ๐Ÿ”’ Blocked file extensions that could be dangerous
52
+ private readonly blockedExtensions: Set<string> = new Set([
53
+ '.exe', '.bat', '.cmd', '.com', '.msi', '.scr', '.pif',
54
+ '.sh', '.bash', '.zsh', '.csh',
55
+ '.ps1', '.psm1', '.psd1',
56
+ '.vbs', '.vbe', '.js', '.jse', '.wsf', '.wsh',
57
+ '.dll', '.sys', '.drv', '.so', '.dylib',
58
+ ])
18
59
 
19
60
  constructor() {
20
61
  // Cleanup stale uploads every 5 minutes
21
62
  setInterval(() => this.cleanupStaleUploads(), 5 * 60 * 1000)
63
+ // ๐Ÿ”’ Reset per-user upload quotas daily
64
+ setInterval(() => this.resetUploadQuotas(), this.quotaResetInterval)
22
65
  }
23
66
 
24
- async startUpload(message: FileUploadStartMessage): Promise<{ success: boolean; error?: string }> {
67
+ async startUpload(message: FileUploadStartMessage, userId?: string): Promise<{ success: boolean; error?: string }> {
25
68
  try {
26
69
  const { uploadId, componentId, filename, fileType, fileSize, chunkSize = 64 * 1024 } = message
27
70
 
28
- // Validate file size (sem restriรงรฃo de tipo)
71
+ // ๐Ÿ”’ Validate file size
29
72
  if (fileSize > this.maxUploadSize) {
30
73
  throw new Error(`File too large: ${fileSize} bytes. Max: ${this.maxUploadSize} bytes`)
31
74
  }
32
75
 
76
+ // ๐Ÿ”’ Per-user upload quota check
77
+ if (userId) {
78
+ const currentUsage = this.userUploadBytes.get(userId) || 0
79
+ if (currentUsage + fileSize > this.maxBytesPerUser) {
80
+ throw new Error(`Upload quota exceeded for user. Used: ${currentUsage} bytes, limit: ${this.maxBytesPerUser} bytes`)
81
+ }
82
+ }
83
+
84
+ // ๐Ÿ”’ Validate MIME type against allowlist
85
+ if (this.allowedTypes.length > 0 && !this.allowedTypes.includes(fileType)) {
86
+ throw new Error(`File type not allowed: ${fileType}`)
87
+ }
88
+
89
+ // ๐Ÿ”’ Validate filename - sanitize and check extension
90
+ const safeBase = basename(filename) // Strip any path traversal
91
+ const ext = extname(safeBase).toLowerCase()
92
+ if (this.blockedExtensions.has(ext)) {
93
+ throw new Error(`File extension not allowed: ${ext}`)
94
+ }
95
+
96
+ // ๐Ÿ”’ Double extension bypass prevention (e.g., malware.exe.jpg)
97
+ const parts = safeBase.split('.')
98
+ if (parts.length > 2) {
99
+ // Check all intermediate extensions
100
+ for (let i = 1; i < parts.length - 1; i++) {
101
+ const intermediateExt = '.' + parts[i].toLowerCase()
102
+ if (this.blockedExtensions.has(intermediateExt)) {
103
+ throw new Error(`Suspicious double extension detected: ${intermediateExt} in ${safeBase}`)
104
+ }
105
+ }
106
+ }
107
+
108
+ // ๐Ÿ”’ Validate filename length
109
+ if (safeBase.length > 255) {
110
+ throw new Error('Filename too long')
111
+ }
112
+
33
113
  // Check if upload already exists
34
114
  if (this.activeUploads.has(uploadId)) {
35
115
  throw new Error(`Upload ${uploadId} already in progress`)
@@ -54,13 +134,20 @@ export class FileUploadManager {
54
134
 
55
135
  this.activeUploads.set(uploadId, upload)
56
136
 
57
- console.log('๐Ÿ“ค Upload started:', {
137
+ // ๐Ÿ”’ Reserve quota for this upload
138
+ if (userId) {
139
+ const currentUsage = this.userUploadBytes.get(userId) || 0
140
+ this.userUploadBytes.set(userId, currentUsage + fileSize)
141
+ }
142
+
143
+ liveLog('messages', componentId, '๐Ÿ“ค Upload started:', {
58
144
  uploadId,
59
145
  componentId,
60
146
  filename,
61
147
  fileType,
62
148
  fileSize,
63
- totalChunks
149
+ totalChunks,
150
+ userId: userId || 'anonymous'
64
151
  })
65
152
 
66
153
  return { success: true }
@@ -87,7 +174,7 @@ export class FileUploadManager {
87
174
 
88
175
  // Check if chunk already received
89
176
  if (upload.receivedChunks.has(chunkIndex)) {
90
- console.log(`๐Ÿ“ฆ Chunk ${chunkIndex} already received for upload ${uploadId}`)
177
+ liveLog('messages', upload.componentId, `๐Ÿ“ฆ Chunk ${chunkIndex} already received for upload ${uploadId}`)
91
178
  } else {
92
179
  // Store chunk data - use binary data if available, otherwise use base64 string
93
180
  let chunkBytes: number
@@ -105,7 +192,7 @@ export class FileUploadManager {
105
192
  upload.lastChunkTime = Date.now()
106
193
  upload.bytesReceived += chunkBytes
107
194
 
108
- console.log(`๐Ÿ“ฆ Received chunk ${chunkIndex + 1}/${totalChunks} for upload ${uploadId} (${chunkBytes} bytes, total: ${upload.bytesReceived}/${upload.fileSize})${binaryData ? ' [binary]' : ' [base64]'}`)
195
+ liveLog('messages', upload.componentId, `๐Ÿ“ฆ Received chunk ${chunkIndex + 1}/${totalChunks} for upload ${uploadId} (${chunkBytes} bytes, total: ${upload.bytesReceived}/${upload.fileSize})${binaryData ? ' [binary]' : ' [base64]'}`)
109
196
  }
110
197
 
111
198
  // Calculate progress based on actual bytes received (supports adaptive chunking)
@@ -114,7 +201,7 @@ export class FileUploadManager {
114
201
 
115
202
  // Log completion status (but don't finalize until COMPLETE message)
116
203
  if (upload.bytesReceived >= upload.fileSize) {
117
- console.log(`โœ… All bytes received for upload ${uploadId} (${upload.bytesReceived}/${upload.fileSize}), waiting for COMPLETE message`)
204
+ liveLog('messages', upload.componentId, `โœ… All bytes received for upload ${uploadId} (${upload.bytesReceived}/${upload.fileSize}), waiting for COMPLETE message`)
118
205
  }
119
206
 
120
207
  return {
@@ -137,7 +224,7 @@ export class FileUploadManager {
137
224
 
138
225
  private async finalizeUpload(upload: ActiveUpload): Promise<void> {
139
226
  try {
140
- console.log(`โœ… Upload completed: ${upload.uploadId}`)
227
+ liveLog('messages', upload.componentId, `โœ… Upload completed: ${upload.uploadId}`)
141
228
 
142
229
  // Assemble file from chunks
143
230
  const fileUrl = await this.assembleFile(upload)
@@ -160,7 +247,7 @@ export class FileUploadManager {
160
247
  throw new Error(`Upload ${uploadId} not found`)
161
248
  }
162
249
 
163
- console.log(`โœ… Upload completion requested: ${uploadId}`)
250
+ liveLog('messages', upload.componentId, `โœ… Upload completion requested: ${uploadId}`)
164
251
 
165
252
  // Validate bytes received (supports adaptive chunking)
166
253
  if (upload.bytesReceived !== upload.fileSize) {
@@ -168,8 +255,10 @@ export class FileUploadManager {
168
255
  throw new Error(`Incomplete upload: received ${upload.bytesReceived}/${upload.fileSize} bytes (${bytesShort} bytes short)`)
169
256
  }
170
257
 
171
- console.log(`โœ… Upload validation passed: ${uploadId} (${upload.bytesReceived} bytes)`)
258
+ // ๐Ÿ”’ Content validation: verify file magic bytes match claimed MIME type
259
+ this.validateContentMagicBytes(upload)
172
260
 
261
+ liveLog('messages', upload.componentId, `โœ… Upload validation passed: ${uploadId} (${upload.bytesReceived} bytes)`)
173
262
 
174
263
  // Assemble file from chunks
175
264
  const fileUrl = await this.assembleFile(upload)
@@ -209,11 +298,9 @@ export class FileUploadManager {
209
298
  await mkdir(uploadsDir, { recursive: true })
210
299
  }
211
300
 
212
- // Generate unique filename
213
- const timestamp = Date.now()
214
- const extension = extname(upload.filename)
215
- const baseName = upload.filename.replace(extension, '')
216
- const safeFilename = `${baseName}_${timestamp}${extension}`
301
+ // ๐Ÿ”’ Generate secure unique filename using UUID (prevents path traversal and name collisions)
302
+ const extension = extname(basename(upload.filename)).toLowerCase()
303
+ const safeFilename = `${crypto.randomUUID()}${extension}`
217
304
  const filePath = join(uploadsDir, safeFilename)
218
305
 
219
306
  // Assemble chunks in order
@@ -234,7 +321,7 @@ export class FileUploadManager {
234
321
  const fileBuffer = Buffer.concat(chunks)
235
322
  await writeFile(filePath, fileBuffer)
236
323
 
237
- console.log(`๐Ÿ“ File assembled: ${filePath}`)
324
+ liveLog('messages', upload.componentId, `๐Ÿ“ File assembled: ${filePath}`)
238
325
  return `/uploads/${safeFilename}`
239
326
 
240
327
  } catch (error) {
@@ -257,11 +344,88 @@ export class FileUploadManager {
257
344
 
258
345
  for (const uploadId of staleUploads) {
259
346
  this.activeUploads.delete(uploadId)
260
- console.log(`๐Ÿงน Cleaned up stale upload: ${uploadId}`)
347
+ liveLog('messages', null, `๐Ÿงน Cleaned up stale upload: ${uploadId}`)
261
348
  }
262
349
 
263
350
  if (staleUploads.length > 0) {
264
- console.log(`๐Ÿงน Cleaned up ${staleUploads.length} stale uploads`)
351
+ liveLog('messages', null, `๐Ÿงน Cleaned up ${staleUploads.length} stale uploads`)
352
+ }
353
+ }
354
+
355
+ /**
356
+ * ๐Ÿ”’ Validate that the first bytes of the uploaded file match the claimed MIME type.
357
+ * Prevents attacks where a malicious file is uploaded with a fake MIME type header.
358
+ */
359
+ private validateContentMagicBytes(upload: ActiveUpload): void {
360
+ const expectedSignatures = MAGIC_BYTES[upload.fileType]
361
+ if (!expectedSignatures) {
362
+ // No magic bytes defined for this type (text types, SVG, JSON, etc.) - skip binary check
363
+ // For text types, we could add content sniffing but it's less critical
364
+ return
365
+ }
366
+
367
+ // Get the first chunk to read magic bytes
368
+ const firstChunk = upload.receivedChunks.get(0)
369
+ if (!firstChunk) {
370
+ throw new Error('Cannot validate file content: first chunk missing')
371
+ }
372
+
373
+ const headerBuffer = Buffer.isBuffer(firstChunk)
374
+ ? firstChunk
375
+ : Buffer.from(firstChunk, 'base64')
376
+
377
+ // Check if any of the expected signatures match
378
+ let matched = false
379
+ for (const sig of expectedSignatures) {
380
+ const offset = sig.offset ?? 0
381
+ if (headerBuffer.length < offset + sig.bytes.length) {
382
+ continue // File too small for this signature
383
+ }
384
+
385
+ let sigMatches = true
386
+ for (let i = 0; i < sig.bytes.length; i++) {
387
+ if (headerBuffer[offset + i] !== sig.bytes[i]) {
388
+ sigMatches = false
389
+ break
390
+ }
391
+ }
392
+
393
+ if (sigMatches) {
394
+ matched = true
395
+ break
396
+ }
397
+ }
398
+
399
+ if (!matched) {
400
+ liveWarn('messages', upload.componentId, `๐Ÿ”’ Content validation failed for upload ${upload.uploadId}: ` +
401
+ `claimed type ${upload.fileType} does not match file magic bytes`)
402
+ throw new Error(
403
+ `File content does not match claimed type '${upload.fileType}'. ` +
404
+ `The file may be disguised as a different format.`
405
+ )
406
+ }
407
+ }
408
+
409
+ /**
410
+ * ๐Ÿ”’ Reset per-user upload quotas (called periodically)
411
+ */
412
+ private resetUploadQuotas(): void {
413
+ const userCount = this.userUploadBytes.size
414
+ this.userUploadBytes.clear()
415
+ if (userCount > 0) {
416
+ liveLog('messages', null, `๐Ÿ”’ Reset upload quotas for ${userCount} users`)
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Get per-user upload usage
422
+ */
423
+ getUserUploadUsage(userId: string): { used: number; limit: number; remaining: number } {
424
+ const used = this.userUploadBytes.get(userId) || 0
425
+ return {
426
+ used,
427
+ limit: this.maxBytesPerUser,
428
+ remaining: Math.max(0, this.maxBytesPerUser - used)
265
429
  }
266
430
  }
267
431
 
@@ -2,6 +2,7 @@
2
2
  // Advanced performance monitoring, metrics collection, and optimization suggestions
3
3
 
4
4
  import { EventEmitter } from 'events'
5
+ import { liveLog, liveWarn } from './LiveLogger'
5
6
 
6
7
  export interface PerformanceMetrics {
7
8
  componentId: string
@@ -279,7 +280,7 @@ export class LiveComponentPerformanceMonitor extends EventEmitter {
279
280
  this.alerts.set(componentId, [])
280
281
  this.suggestions.set(componentId, [])
281
282
 
282
- console.log(`๐Ÿ“Š Performance monitoring initialized for component: ${componentId}`)
283
+ liveLog('performance', componentId, `๐Ÿ“Š Performance monitoring initialized for component: ${componentId}`)
283
284
  }
284
285
 
285
286
  /**
@@ -562,7 +563,7 @@ export class LiveComponentPerformanceMonitor extends EventEmitter {
562
563
 
563
564
  this.alertCooldowns.set(alertKey, now)
564
565
 
565
- console.warn(`โš ๏ธ Performance alert [${type}]: ${message}`)
566
+ liveWarn('performance', componentId, `โš ๏ธ Performance alert [${type}]: ${message}`)
566
567
  this.emit('performanceAlert', alert)
567
568
  }
568
569
 
@@ -695,7 +696,7 @@ export class LiveComponentPerformanceMonitor extends EventEmitter {
695
696
  suggestions.push(suggestion)
696
697
  this.suggestions.set(componentId, suggestions)
697
698
 
698
- console.log(`๐Ÿ’ก Optimization suggestion for ${componentId}: ${title}`)
699
+ liveLog('performance', componentId, `๐Ÿ’ก Optimization suggestion for ${componentId}: ${title}`)
699
700
  this.emit('optimizationSuggestion', suggestion)
700
701
  }
701
702
 
@@ -863,7 +864,7 @@ export class LiveComponentPerformanceMonitor extends EventEmitter {
863
864
  const alert = alerts.find(a => a.id === alertId)
864
865
  if (alert) {
865
866
  alert.resolved = true
866
- console.log(`โœ… Alert resolved: ${alertId}`)
867
+ liveLog('performance', null, `โœ… Alert resolved: ${alertId}`)
867
868
  return true
868
869
  }
869
870
  }
@@ -889,7 +890,7 @@ export class LiveComponentPerformanceMonitor extends EventEmitter {
889
890
  this.suggestions.set(componentId, validSuggestions)
890
891
  }
891
892
 
892
- console.log('๐Ÿงน Performance monitoring data cleanup completed')
893
+ liveLog('performance', null, '๐Ÿงน Performance monitoring data cleanup completed')
893
894
  }
894
895
 
895
896
  /**
@@ -900,14 +901,14 @@ export class LiveComponentPerformanceMonitor extends EventEmitter {
900
901
  this.alerts.delete(componentId)
901
902
  this.suggestions.delete(componentId)
902
903
 
903
- console.log(`๐Ÿ“Š Performance monitoring removed for component: ${componentId}`)
904
+ liveLog('performance', componentId, `๐Ÿ“Š Performance monitoring removed for component: ${componentId}`)
904
905
  }
905
906
 
906
907
  /**
907
908
  * Shutdown performance monitor
908
909
  */
909
910
  shutdown(): void {
910
- console.log('๐Ÿ“Š Shutting down Performance Monitor...')
911
+ liveLog('performance', null, '๐Ÿ“Š Shutting down Performance Monitor...')
911
912
 
912
913
  if (this.dashboardUpdateInterval) {
913
914
  clearInterval(this.dashboardUpdateInterval)
@@ -922,7 +923,7 @@ export class LiveComponentPerformanceMonitor extends EventEmitter {
922
923
  this.suggestions.clear()
923
924
  this.alertCooldowns.clear()
924
925
 
925
- console.log('โœ… Performance Monitor shutdown complete')
926
+ liveLog('performance', null, 'โœ… Performance Monitor shutdown complete')
926
927
  }
927
928
  }
928
929