UNPKG

nano-invoke

Version:

Simple IPC client for Nano Framework applications

247 lines 7.5 kB
/** * Nano Framework IPC Client * * Simple, clean invoke function for calling nano backend functions * * @package nano-invoke * @author Imperium Industries * @version 1.0.0 */ // Default configuration let config = { baseUrl: '', debug: false, timeout: 10000 }; /** * Configure the invoke client * * @param newConfig - Configuration options */ export function configure(newConfig) { config = { ...config, ...newConfig }; } /** * Simple invoke function for calling nano functions * * @param cmd - Function name to invoke * @param args - Arguments to pass to the function * @returns Promise resolving to the function result */ export async function invoke(cmd, args = {}) { const request = { cmd, args: args || {} }; try { if (config.debug) { console.log(`[Nano IPC] Invoking: ${cmd}`, args); } const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), config.timeout); const response = await fetch(`${config.baseUrl}/ipc`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(request), signal: controller.signal }); clearTimeout(timeoutId); if (config.debug) { console.log(`[Nano IPC] Response status: ${response.status} ${response.statusText}`); } if (!response.ok) { const errorText = await response.text(); if (config.debug) { console.error(`[Nano IPC] Error response:`, errorText); } if (response.status === 404) { throw new Error('IPC endpoint not found. Make sure the Nano server is running.'); } throw new Error(`HTTP ${response.status}: ${response.statusText} - ${errorText}`); } const result = await response.json(); if (config.debug) { console.log(`[Nano IPC] Result:`, result); } if (result.error) { throw new Error(result.error); } return result.result; } catch (error) { if (config.debug) { console.error(`[Nano IPC] Invoke error:`, error); } if (error instanceof Error && error.name === 'AbortError') { throw new Error(`Request timeout after ${config.timeout}ms`); } if (error instanceof TypeError && error.message.includes('fetch')) { throw new Error('Cannot connect to server. Make sure the Nano application is running.'); } throw new Error(error instanceof Error ? error.message : String(error)); } } export class EventManager { constructor() { this.ws = null; this.listeners = new Map(); this.reconnectAttempts = 0; this.maxReconnectAttempts = 5; this.reconnectDelay = 1000; } /** * Connect to the WebSocket server */ async connect(wsUrl) { return new Promise((resolve, reject) => { try { const url = wsUrl || `ws://${window.location.host}/ws`; this.ws = new WebSocket(url); this.ws.onopen = () => { this.reconnectAttempts = 0; if (config.debug) { console.log('[Nano Events] Connected to WebSocket'); } resolve(); }; this.ws.onmessage = (event) => { try { const message = JSON.parse(event.data); this.emit(message.event || 'message', message.payload || message); } catch (error) { if (config.debug) { console.error('[Nano Events] Failed to parse event:', error); } } }; this.ws.onclose = () => { if (config.debug) { console.log('[Nano Events] WebSocket connection closed'); } this.attemptReconnect(); }; this.ws.onerror = (error) => { if (config.debug) { console.error('[Nano Events] WebSocket error:', error); } reject(new Error('WebSocket connection failed')); }; } catch (error) { reject(error); } }); } /** * Disconnect from WebSocket */ disconnect() { if (this.ws) { this.ws.close(); this.ws = null; } } /** * Subscribe to an event */ on(event, listener) { if (!this.listeners.has(event)) { this.listeners.set(event, new Set()); } this.listeners.get(event).add(listener); } /** * Unsubscribe from an event */ off(event, listener) { const eventListeners = this.listeners.get(event); if (eventListeners) { eventListeners.delete(listener); } } /** * Send a message through the WebSocket */ send(message) { if (this.ws && this.ws.readyState === WebSocket.OPEN) { this.ws.send(JSON.stringify(message)); } else { throw new Error('WebSocket is not connected'); } } emit(event, data) { const eventListeners = this.listeners.get(event); if (eventListeners) { eventListeners.forEach(listener => { try { listener(data); } catch (error) { if (config.debug) { console.error(`[Nano Events] Error in event listener:`, error); } } }); } } async attemptReconnect() { if (this.reconnectAttempts < this.maxReconnectAttempts) { this.reconnectAttempts++; const delay = this.reconnectDelay * this.reconnectAttempts; if (config.debug) { console.log(`[Nano Events] Attempting reconnection ${this.reconnectAttempts}/${this.maxReconnectAttempts} in ${delay}ms`); } setTimeout(() => { this.connect().catch(() => { // Reconnection failed, will try again }); }, delay); } } } // Default event manager instance export const events = new EventManager(); // Utility functions export var Utils; (function (Utils) { /** * Ping the nano server */ async function ping() { return await invoke('ping'); } Utils.ping = ping; /** * Get system information */ async function getSystemInfo() { return await invoke('get_system_info'); } Utils.getSystemInfo = getSystemInfo; /** * Check if the nano server is accessible */ async function isServerRunning() { try { await ping(); return true; } catch { return false; } } Utils.isServerRunning = isServerRunning; })(Utils || (Utils = {})); // Export everything as default as well export default { invoke, configure, events, EventManager, Utils }; //# sourceMappingURL=index.js.map