websocket.ts•2.92 kB
/**
 * WebSocket Service - Handles communication with Figma plugin
 */
import { WebSocketServer, WebSocket as WSWebSocket } from "ws";
import { log, logError } from "../utils.js";
// Store active plugin connection WebSocket
let activePluginConnection: WSWebSocket | null = null;
// Callbacks for handling responses
const pendingCommands = new Map<string, (response: any) => void>();
interface PluginResponse {
  success: boolean;
  result?: any;
  error?: string;
}
/**
 * Create WebSocket server
 */
export function initializeWebSocketServer(port = 3001) {
  const wss = new WebSocketServer({ port });
  log(`WebSocket server started on port ${port}`);
  wss.on("connection", (ws: WSWebSocket) => {
    log("New WebSocket connection");
    ws.on("message", (message: WSWebSocket.Data) => {
      try {
        const data = JSON.parse(message.toString());
        log(`Received WebSocket message: ${JSON.stringify(data)}`);
        if (data.type === "figma-plugin-connected") {
          // Store active connection
          activePluginConnection = ws;
          log(`Figma plugin connected: ${data.pluginId || "unknown"}`);
        } else if (data.type === "figma-plugin-response") {
          // Handle response from plugin
          const { command, success, result, error } = data;
          const callback = pendingCommands.get(command);
          if (callback) {
            callback({ success, result, error });
            pendingCommands.delete(command);
          }
        }
      } catch (error) {
        logError("Error processing WebSocket message", error);
      }
    });
    ws.on("close", () => {
      log("WebSocket connection closed");
      if (activePluginConnection === ws) {
        activePluginConnection = null;
      }
    });
    ws.on("error", (error: Error) => {
      logError("WebSocket error", error);
    });
  });
  return wss;
}
/**
 * Send command to Figma plugin
 */
export async function sendCommandToPlugin(
  command: string,
  params: any
): Promise<PluginResponse> {
  return new Promise((resolve, reject) => {
    if (!activePluginConnection) {
      reject(new Error("No active Figma plugin connection"));
      return;
    }
    try {
      // Store callback
      pendingCommands.set(command, resolve);
      // Send command
      activePluginConnection.send(
        JSON.stringify({
          type: "mcp-command",
          command,
          params,
        })
      );
      // Set timeout
      setTimeout(() => {
        if (pendingCommands.has(command)) {
          pendingCommands.delete(command);
          reject(new Error(`Command ${command} timed out`));
        }
      }, 10000); // 10 second timeout
    } catch (error) {
      pendingCommands.delete(command);
      reject(error);
    }
  });
}
/**
 * Check if a Figma plugin is connected
 */
export function isPluginConnected(): boolean {
  return activePluginConnection !== null;
}