* Send a command to a plugin UI and wait for the response. * By default targets the active file. Pass targetFileKey to target a specific file.
(method: string, params: Record<string, any> = {}, timeoutMs = 15000, targetFileKey?: string)
| 681 | * By default targets the active file. Pass targetFileKey to target a specific file. |
| 682 | */ |
| 683 | sendCommand(method: string, params: Record<string, any> = {}, timeoutMs = 15000, targetFileKey?: string): Promise<any> { |
| 684 | return new Promise((resolve, reject) => { |
| 685 | const fileKey = targetFileKey || this._activeFileKey; |
| 686 | |
| 687 | if (!fileKey) { |
| 688 | reject(new Error('No active file connected. Make sure the Desktop Bridge plugin is open in Figma.')); |
| 689 | return; |
| 690 | } |
| 691 | |
| 692 | const client = this.clients.get(fileKey); |
| 693 | if (!client || client.ws.readyState !== WebSocket.OPEN) { |
| 694 | reject(new Error('No WebSocket client connected. Make sure the Desktop Bridge plugin is open in Figma.')); |
| 695 | return; |
| 696 | } |
| 697 | |
| 698 | const id = `ws_${++this.requestIdCounter}_${Date.now()}`; |
| 699 | |
| 700 | const timeoutId = setTimeout(() => { |
| 701 | if (this.pendingRequests.has(id)) { |
| 702 | this.pendingRequests.delete(id); |
| 703 | reject(new Error(`WebSocket command ${method} timed out after ${timeoutMs}ms`)); |
| 704 | } |
| 705 | }, timeoutMs); |
| 706 | |
| 707 | this.pendingRequests.set(id, { |
| 708 | resolve, |
| 709 | reject, |
| 710 | method, |
| 711 | timeoutId, |
| 712 | createdAt: Date.now(), |
| 713 | targetFileKey: fileKey, |
| 714 | }); |
| 715 | |
| 716 | const message = JSON.stringify({ id, method, params }); |
| 717 | try { |
| 718 | client.ws.send(message); |
| 719 | } catch (sendError) { |
| 720 | this.pendingRequests.delete(id); |
| 721 | clearTimeout(timeoutId); |
| 722 | reject(new Error(`Failed to send WebSocket command ${method}: ${sendError instanceof Error ? sendError.message : String(sendError)}`)); |
| 723 | return; |
| 724 | } |
| 725 | client.lastActivity = Date.now(); |
| 726 | |
| 727 | logger.debug({ id, method, fileKey }, 'Sent WebSocket command'); |
| 728 | }); |
| 729 | } |
| 730 | |
| 731 | /** |
| 732 | * Start the heartbeat interval that pings all connected clients every 30s. |
no test coverage detected