UNPKG

signalk-server

Version:

An implementation of a [Signal K](http://signalk.org) server for boats.

237 lines 9.04 kB
"use strict"; /* eslint-disable @typescript-eslint/no-explicit-any */ /** * WASM Runtime Management * * Handles WASM runtime initialization, module loading, * and instance lifecycle management for Signal K WASM plugins. * * This is the main entry point that coordinates the various loaders * and bindings for different WASM plugin formats. */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.WasmRuntime = exports.wasmWeatherProviders = exports.wasmResourceProviders = exports.detectWasmFormat = void 0; exports.getWasmRuntime = getWasmRuntime; exports.initializeWasmRuntime = initializeWasmRuntime; exports.resetWasmRuntime = resetWasmRuntime; const fs = __importStar(require("fs")); const debug_1 = __importDefault(require("debug")); // Re-export utilities var format_detection_1 = require("./utils/format-detection"); Object.defineProperty(exports, "detectWasmFormat", { enumerable: true, get: function () { return format_detection_1.detectWasmFormat; } }); // Import loaders const standard_loader_1 = require("./loaders/standard-loader"); // Import bindings const resource_provider_1 = require("./bindings/resource-provider"); Object.defineProperty(exports, "wasmResourceProviders", { enumerable: true, get: function () { return resource_provider_1.wasmResourceProviders; } }); const weather_provider_1 = require("./bindings/weather-provider"); Object.defineProperty(exports, "wasmWeatherProviders", { enumerable: true, get: function () { return weather_provider_1.wasmWeatherProviders; } }); // Import utilities const format_detection_2 = require("./utils/format-detection"); const debug = (0, debug_1.default)('signalk:wasm:runtime'); class WasmRuntime { instances = new Map(); enabled = true; constructor() { debug('Initializing WASM runtime'); } /** * Check if WASM support is enabled */ isEnabled() { return this.enabled; } /** * Enable or disable WASM plugin support */ setEnabled(enabled) { this.enabled = enabled; debug(`WASM support ${enabled ? 'enabled' : 'disabled'}`); } /** * Load and instantiate a WASM plugin module */ async loadPlugin(pluginId, wasmPath, vfsRoot, capabilities, app) { if (!this.enabled) { throw new Error('WASM support is disabled'); } debug(`Loading WASM plugin: ${pluginId} from ${wasmPath}`); try { // Ensure VFS root exists if (!fs.existsSync(vfsRoot)) { fs.mkdirSync(vfsRoot, { recursive: true }); } // Load WASM binary debug(`Reading WASM file: ${wasmPath}`); const wasmBuffer = fs.readFileSync(wasmPath); debug(`WASM file size: ${wasmBuffer.length} bytes`); const wasmFormat = (0, format_detection_2.detectWasmFormat)(wasmBuffer); debug(`Detected WASM format: ${wasmFormat}`); if (wasmFormat !== 'wasi-p1') { throw new Error(`Unsupported WASM format: ${wasmFormat}. Only WASI P1 plugins (AssemblyScript/Rust) are supported.`); } // Load standard WASI P1 plugin (AssemblyScript or Rust) const pluginInstance = await (0, standard_loader_1.loadStandardPlugin)(pluginId, wasmPath, wasmBuffer, vfsRoot, capabilities, app); // Store the instance this.instances.set(pluginId, pluginInstance); debug(`Successfully loaded WASM plugin: ${pluginId}`); return pluginInstance; } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); debug(`Failed to load WASM plugin ${pluginId}: ${errorMsg}`); throw new Error(`Failed to load WASM plugin ${pluginId}: ${errorMsg}`); } } /** * Unload a WASM plugin instance * @param pluginId The plugin ID to unload * @param app Optional Signal K app reference for proper API cleanup */ async unloadPlugin(pluginId, app) { const instance = this.instances.get(pluginId); if (!instance) { debug(`Plugin ${pluginId} not found in loaded instances`); return; } debug(`Unloading WASM plugin: ${pluginId}`); try { // Call stop if available if (instance.exports.stop) { instance.exports.stop(); } // Clean up resource provider registrations for this plugin // Pass app to also unregister from ResourcesApi (0, resource_provider_1.cleanupResourceProviders)(pluginId, app); // Clean up weather provider registrations for this plugin // Pass app to also unregister from WeatherApi (0, weather_provider_1.cleanupWeatherProviders)(pluginId, app); // Remove from instances this.instances.delete(pluginId); // Let GC clean up the instance debug(`Successfully unloaded WASM plugin: ${pluginId}`); } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); debug(`Error unloading WASM plugin ${pluginId}: ${errorMsg}`); throw error; } } /** * Reload a WASM plugin (unload + load) */ async reloadPlugin(pluginId) { const oldInstance = this.instances.get(pluginId); if (!oldInstance) { throw new Error(`Plugin ${pluginId} not loaded`); } const { wasmPath, vfsRoot, capabilities } = oldInstance; // Unload old instance await this.unloadPlugin(pluginId); // Load new instance return this.loadPlugin(pluginId, wasmPath, vfsRoot, capabilities); } /** * Get a loaded plugin instance */ getInstance(pluginId) { return this.instances.get(pluginId); } /** * Get all loaded plugin instances */ getAllInstances() { return Array.from(this.instances.values()); } /** * Check if a plugin is loaded */ isPluginLoaded(pluginId) { return this.instances.has(pluginId); } /** * Shutdown the WASM runtime and unload all plugins */ async shutdown() { debug('Shutting down WASM runtime'); const pluginIds = Array.from(this.instances.keys()); for (const pluginId of pluginIds) { try { await this.unloadPlugin(pluginId); } catch (error) { debug(`Error unloading plugin ${pluginId} during shutdown:`, error); } } this.instances.clear(); debug('WASM runtime shutdown complete'); } } exports.WasmRuntime = WasmRuntime; // Global singleton instance let runtimeInstance = null; /** * Get the global WASM runtime instance */ function getWasmRuntime() { if (!runtimeInstance) { runtimeInstance = new WasmRuntime(); } return runtimeInstance; } /** * Initialize the WASM runtime */ function initializeWasmRuntime() { if (runtimeInstance) { debug('WASM runtime already initialized'); return runtimeInstance; } runtimeInstance = new WasmRuntime(); return runtimeInstance; } /** * Reset the WASM runtime singleton (for hotplug support) * This should be called after shutdown to allow re-initialization */ function resetWasmRuntime() { debug('Resetting WASM runtime singleton'); runtimeInstance = null; } //# sourceMappingURL=wasm-runtime.js.map