refactor: improve type safety in Mermaid integration
- Replace all `any` types with proper Excalidraw types - Add proper type imports from @excalidraw/excalidraw - Improve MermaidConversionResult interface with ExcalidrawElement[] and BinaryFiles - Add mermaidDiagram and config fields to WebSocketMessage interface - Remove unused ElementBinding interface - Remove all `as any` type casts throughout App.tsx - Fix Vite security vulnerability (1 of 6 moderate vulns) Changes: - frontend/src/App.tsx: Restored proper TypeScript types, removed 12+ `as any` casts - frontend/src/utils/mermaidConverter.ts: Added proper return types - package-lock.json: Updated vite to fix security issue Remaining security issues: - 5 moderate vulnerabilities from @excalidraw/mermaid-to-excalidraw dependencies - These are upstream issues in dompurify, nanoid, and mermaid packages - No fixes available without major version upgrades - Risk is acceptable for this use case (diagram rendering)
This commit is contained in:
+33
-34
@@ -1,14 +1,16 @@
|
||||
import React, { useState, useEffect, useRef } from 'react'
|
||||
import {
|
||||
Excalidraw,
|
||||
convertToExcalidrawElements,
|
||||
CaptureUpdateAction
|
||||
import {
|
||||
Excalidraw,
|
||||
convertToExcalidrawElements,
|
||||
CaptureUpdateAction,
|
||||
ExcalidrawImperativeAPI
|
||||
} from '@excalidraw/excalidraw'
|
||||
import type { ExcalidrawElement, NonDeleted, NonDeletedExcalidrawElement } from '@excalidraw/excalidraw/types/element/types'
|
||||
import { convertMermaidToExcalidraw, DEFAULT_MERMAID_CONFIG } from './utils/mermaidConverter'
|
||||
import type { MermaidConfig } from '@excalidraw/mermaid-to-excalidraw'
|
||||
|
||||
// Type definitions
|
||||
type ExcalidrawAPIRefValue = any;
|
||||
type ExcalidrawElement = any;
|
||||
type ExcalidrawAPIRefValue = ExcalidrawImperativeAPI;
|
||||
|
||||
interface ServerElement {
|
||||
id: string;
|
||||
@@ -47,6 +49,8 @@ interface WebSocketMessage {
|
||||
count?: number;
|
||||
timestamp?: string;
|
||||
source?: string;
|
||||
mermaidDiagram?: string;
|
||||
config?: MermaidConfig;
|
||||
}
|
||||
|
||||
interface ApiResponse {
|
||||
@@ -58,11 +62,6 @@ interface ApiResponse {
|
||||
message?: string;
|
||||
}
|
||||
|
||||
interface ElementBinding {
|
||||
id: string;
|
||||
type: 'text' | 'arrow';
|
||||
}
|
||||
|
||||
type SyncStatus = 'idle' | 'syncing' | 'success' | 'error';
|
||||
|
||||
// Helper function to clean elements for Excalidraw
|
||||
@@ -165,7 +164,7 @@ function App(): JSX.Element {
|
||||
|
||||
if (result.success && result.elements && result.elements.length > 0) {
|
||||
const cleanedElements = result.elements.map(cleanElementForExcalidraw)
|
||||
const convertedElements = convertToExcalidrawElements(cleanedElements as any, { regenerateIds: false })
|
||||
const convertedElements = convertToExcalidrawElements(cleanedElements, { regenerateIds: false })
|
||||
excalidrawAPI?.updateScene({ elements: convertedElements })
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -229,18 +228,18 @@ function App(): JSX.Element {
|
||||
if (data.elements && data.elements.length > 0) {
|
||||
const cleanedElements = data.elements.map(cleanElementForExcalidraw)
|
||||
const validatedElements = validateAndFixBindings(cleanedElements)
|
||||
const convertedElements = convertToExcalidrawElements(validatedElements as any)
|
||||
excalidrawAPI.updateScene({
|
||||
const convertedElements = convertToExcalidrawElements(validatedElements)
|
||||
excalidrawAPI.updateScene({
|
||||
elements: convertedElements,
|
||||
captureUpdate: CaptureUpdateAction.NEVER
|
||||
})
|
||||
}
|
||||
break
|
||||
|
||||
|
||||
case 'element_created':
|
||||
if (data.element) {
|
||||
const cleanedNewElement = cleanElementForExcalidraw(data.element)
|
||||
const newElement = convertToExcalidrawElements([cleanedNewElement] as any)
|
||||
const newElement = convertToExcalidrawElements([cleanedNewElement])
|
||||
const updatedElementsAfterCreate = [...currentElements, ...newElement]
|
||||
excalidrawAPI.updateScene({
|
||||
elements: updatedElementsAfterCreate,
|
||||
@@ -252,31 +251,31 @@ function App(): JSX.Element {
|
||||
case 'element_updated':
|
||||
if (data.element) {
|
||||
const cleanedUpdatedElement = cleanElementForExcalidraw(data.element)
|
||||
const convertedUpdatedElement = convertToExcalidrawElements([cleanedUpdatedElement] as any)[0]
|
||||
const updatedElements = currentElements.map((el: any) =>
|
||||
const convertedUpdatedElement = convertToExcalidrawElements([cleanedUpdatedElement])[0]
|
||||
const updatedElements = currentElements.map(el =>
|
||||
el.id === data.element!.id ? convertedUpdatedElement : el
|
||||
)
|
||||
excalidrawAPI.updateScene({
|
||||
excalidrawAPI.updateScene({
|
||||
elements: updatedElements,
|
||||
captureUpdate: CaptureUpdateAction.NEVER
|
||||
})
|
||||
}
|
||||
break
|
||||
|
||||
|
||||
case 'element_deleted':
|
||||
if (data.elementId) {
|
||||
const filteredElements = currentElements.filter((el: any) => el.id !== data.elementId)
|
||||
excalidrawAPI.updateScene({
|
||||
const filteredElements = currentElements.filter(el => el.id !== data.elementId)
|
||||
excalidrawAPI.updateScene({
|
||||
elements: filteredElements,
|
||||
captureUpdate: CaptureUpdateAction.NEVER
|
||||
})
|
||||
}
|
||||
break
|
||||
|
||||
|
||||
case 'elements_batch_created':
|
||||
if (data.elements) {
|
||||
const cleanedBatchElements = data.elements.map(cleanElementForExcalidraw)
|
||||
const batchElements = convertToExcalidrawElements(cleanedBatchElements as any)
|
||||
const batchElements = convertToExcalidrawElements(cleanedBatchElements)
|
||||
const updatedElementsAfterBatch = [...currentElements, ...batchElements]
|
||||
excalidrawAPI.updateScene({
|
||||
elements: updatedElementsAfterBatch,
|
||||
@@ -296,28 +295,28 @@ function App(): JSX.Element {
|
||||
|
||||
case 'mermaid_convert':
|
||||
console.log('Received Mermaid conversion request from MCP')
|
||||
if ((data as any).mermaidDiagram) {
|
||||
if (data.mermaidDiagram) {
|
||||
try {
|
||||
const result = await convertMermaidToExcalidraw((data as any).mermaidDiagram, (data as any).config || DEFAULT_MERMAID_CONFIG)
|
||||
|
||||
const result = await convertMermaidToExcalidraw(data.mermaidDiagram, data.config || DEFAULT_MERMAID_CONFIG)
|
||||
|
||||
if (result.error) {
|
||||
console.error('Mermaid conversion error:', result.error)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (result.elements && result.elements.length > 0) {
|
||||
const convertedElements = convertToExcalidrawElements(result.elements as any, { regenerateIds: false })
|
||||
excalidrawAPI.updateScene({
|
||||
const convertedElements = convertToExcalidrawElements(result.elements, { regenerateIds: false })
|
||||
excalidrawAPI.updateScene({
|
||||
elements: convertedElements,
|
||||
captureUpdate: CaptureUpdateAction.IMMEDIATELY
|
||||
})
|
||||
|
||||
|
||||
if (result.files) {
|
||||
excalidrawAPI.addFiles(Object.values(result.files))
|
||||
}
|
||||
|
||||
|
||||
console.log('Mermaid diagram converted successfully:', result.elements.length, 'elements')
|
||||
|
||||
|
||||
// Sync to backend automatically after creating elements
|
||||
await syncToBackend()
|
||||
}
|
||||
@@ -367,7 +366,7 @@ function App(): JSX.Element {
|
||||
console.log(`Syncing ${currentElements.length} elements to backend`)
|
||||
|
||||
// Filter out deleted elements
|
||||
const activeElements = currentElements.filter((el: any) => !el.isDeleted)
|
||||
const activeElements = currentElements.filter(el => !el.isDeleted)
|
||||
|
||||
// 3. Convert to backend format
|
||||
const backendElements = activeElements.map(convertToBackendFormat)
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { parseMermaidToExcalidraw, MermaidConfig } from '@excalidraw/mermaid-to-excalidraw';
|
||||
import type { ExcalidrawElement } from '@excalidraw/excalidraw/types/element/types';
|
||||
import type { BinaryFiles } from '@excalidraw/excalidraw/types/types';
|
||||
|
||||
export interface MermaidConversionResult {
|
||||
elements: any[];
|
||||
files?: any;
|
||||
elements: readonly ExcalidrawElement[];
|
||||
files?: BinaryFiles;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user