signalk-server
Version:
An implementation of a [Signal K](http://signalk.org) server for boats.
237 lines • 9.04 kB
JavaScript
;
/* 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