js-tts-wrapper
Version:
A JavaScript/TypeScript library that provides a unified API for working with multiple cloud-based Text-to-Speech (TTS) services
475 lines (469 loc) • 18.6 kB
JavaScript
/**
* SherpaOnnx Native Module Loader
*
* This module handles loading the sherpa-onnx-node native module with the correct
* environment variables set for the current platform.
*/
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;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getCurrentPlatformKey = getCurrentPlatformKey;
exports.getExpectedPlatformPackage = getExpectedPlatformPackage;
exports.findSherpaOnnxLibraryPath = findSherpaOnnxLibraryPath;
exports.isESModule = isESModule;
exports.setupEnvironmentVariables = setupEnvironmentVariables;
exports.getInstallationInstructions = getInstallationInstructions;
exports.loadSherpaOnnxNode = loadSherpaOnnxNode;
exports.canRunSherpaOnnx = canRunSherpaOnnx;
exports.canRunSherpaOnnxSimple = canRunSherpaOnnxSimple;
exports.applyPlatformPackageWorkaround = applyPlatformPackageWorkaround;
exports.getSherpaOnnxDiagnostics = getSherpaOnnxDiagnostics;
exports.loadSherpaOnnxNodeSafe = loadSherpaOnnxNodeSafe;
const fs = __importStar(require("node:fs"));
const path = __importStar(require("node:path"));
/**
* Platform-specific package mapping
*/
const PLATFORM_PACKAGES = {
"darwin-arm64": "sherpa-onnx-darwin-arm64",
"darwin-x64": "sherpa-onnx-darwin-x64",
"linux-arm64": "sherpa-onnx-linux-arm64",
"linux-x64": "sherpa-onnx-linux-x64",
"win32-x64": "sherpa-onnx-win-x64",
};
/**
* Get the current platform key
* @returns Platform key for package mapping
*/
function getCurrentPlatformKey() {
return `${process.platform}-${process.arch}`;
}
/**
* Get the expected platform package name
* @returns Expected package name for current platform
*/
function getExpectedPlatformPackage() {
const platformKey = getCurrentPlatformKey();
return PLATFORM_PACKAGES[platformKey] || null;
}
/**
* Find the sherpa-onnx platform-specific library directory
* @returns Path to the library directory or null if not found
*/
function findSherpaOnnxLibraryPath() {
// Get the expected package for current platform
const expectedPackage = getExpectedPlatformPackage();
if (!expectedPackage) {
console.warn(`Unsupported platform: ${getCurrentPlatformKey()}`);
return null;
}
// Check multiple possible locations
const possiblePaths = [
// Current working directory
path.join(process.cwd(), "node_modules", expectedPackage),
// Parent directory (for development)
path.join(process.cwd(), "..", "node_modules", expectedPackage),
// Global node_modules
path.join(process.cwd(), "..", "..", "node_modules", expectedPackage),
];
// Also check for other platform packages as fallback
const allPlatformPackages = Object.values(PLATFORM_PACKAGES);
for (const pkg of allPlatformPackages) {
if (pkg !== expectedPackage) {
possiblePaths.push(path.join(process.cwd(), "node_modules", pkg));
}
}
// Find the first existing path
for (const libPath of possiblePaths) {
if (fs.existsSync(libPath)) {
return libPath;
}
}
return null;
}
/**
* Check if we're running in ES module or CommonJS context
* @returns True if ES modules, false if CommonJS
*/
function isESModule() {
try {
// Check for ES module indicators
// In CommonJS, 'require' is defined and 'module' exists
// In ES modules, these are not available
return typeof require === "undefined" && typeof module === "undefined";
}
catch {
return false;
}
}
/**
* Set up environment variables for the current platform
* @param libraryPath Path to the platform-specific library
* @returns True if environment variables were set successfully
*/
function setupEnvironmentVariables(libraryPath) {
try {
let envVarName = "";
if (process.platform === "darwin") {
envVarName = "DYLD_LIBRARY_PATH";
}
else if (process.platform === "linux") {
envVarName = "LD_LIBRARY_PATH";
}
else if (process.platform === "win32") {
envVarName = "PATH";
}
else {
console.warn(`Unsupported platform for environment setup: ${process.platform}`);
return false;
}
const currentValue = process.env[envVarName] || "";
const separator = process.platform === "win32" ? ";" : ":";
// Only add the path if it's not already in the environment variable
if (!currentValue.includes(libraryPath)) {
process.env[envVarName] = libraryPath + (currentValue ? separator + currentValue : "");
console.log(`Set ${envVarName} to include: ${libraryPath}`);
return true;
}
console.log(`${envVarName} already includes: ${libraryPath}`);
return true;
}
catch (error) {
console.error("Error setting up environment variables:", error);
return false;
}
}
/**
* Generate installation instructions for missing platform packages
* @returns Installation instructions string
*/
function getInstallationInstructions() {
const expectedPackage = getExpectedPlatformPackage();
const platformKey = getCurrentPlatformKey();
return `
To use SherpaOnnx TTS on ${platformKey}, you need to:
1. Install the required packages:
npm install sherpa-onnx-node@^1.12.0 ${expectedPackage || "sherpa-onnx-<platform>"} decompress decompress-bzip2 decompress-tarbz2 decompress-targz tar-stream
OR use the convenience script:
npx js-tts-wrapper install sherpaonnx
2. Ensure you're using Node.js 16+ (current version: ${process.version})
3. If you still have issues, try the helper script:
node scripts/run-with-sherpaonnx.cjs your-script.js
4. For cloud deployments, ensure the platform-specific package is installed:
${expectedPackage || "sherpa-onnx-<platform>"}
`;
}
/**
* Load the sherpa-onnx-node module with the correct environment variables
* @returns The loaded sherpa-onnx-node module
*/
async function loadSherpaOnnxNode() {
// Find the library path
const libraryPath = findSherpaOnnxLibraryPath();
if (!libraryPath) {
const error = new Error(`Could not find sherpa-onnx library directory. ${getInstallationInstructions()}`);
error.name = "SherpaOnnxLibraryNotFound";
throw error;
}
// Set up environment variables
if (!setupEnvironmentVariables(libraryPath)) {
console.warn("Failed to set up environment variables, but continuing...");
}
// Verify the native module exists
const nativeModulePath = path.join(libraryPath, "sherpa-onnx.node");
if (!fs.existsSync(nativeModulePath)) {
const error = new Error(`Native module not found at ${nativeModulePath}. ${getInstallationInstructions()}`);
error.name = "SherpaOnnxNativeModuleNotFound";
throw error;
}
// Try to load the module
try {
console.log(`Loading sherpa-onnx-node from: ${libraryPath}`);
// Use appropriate loading mechanism based on module system
if (isESModule()) {
console.log("Using ES module import for sherpa-onnx-node");
return await Promise.resolve().then(() => __importStar(require("sherpa-onnx-node")));
}
console.log("Using CommonJS require for sherpa-onnx-node");
// Verify the module can be resolved
const resolvedPath = require.resolve("sherpa-onnx-node");
console.log(`Resolved sherpa-onnx-node path: ${resolvedPath}`);
return require("sherpa-onnx-node");
}
catch (loadError) {
const error = loadError;
console.error("Failed to load sherpa-onnx-node:", error.message);
const wrappedError = new Error(`Could not load sherpa-onnx-node: ${error.message}. ${getInstallationInstructions()}`);
wrappedError.name = "SherpaOnnxLoadError";
// Add cause property for better error tracking (ES2022 feature, fallback for older targets)
wrappedError.cause = error;
throw wrappedError;
}
}
/**
* Check if we can run SherpaOnnx in the current environment
* @returns Object with detailed environment check results
*/
function canRunSherpaOnnx() {
const result = {
canRun: false,
hasMainPackage: false,
hasPlatformPackage: false,
hasNativeModule: false,
platformKey: getCurrentPlatformKey(),
expectedPackage: getExpectedPlatformPackage(),
foundLibraryPath: null,
issues: [],
};
try {
// Check if the sherpa-onnx-node package is installed
const sherpaOnnxNodePath = path.join(process.cwd(), "node_modules", "sherpa-onnx-node");
result.hasMainPackage = fs.existsSync(sherpaOnnxNodePath);
if (!result.hasMainPackage) {
result.issues.push("sherpa-onnx-node package not found");
}
// Check if the platform-specific library is installed
result.foundLibraryPath = findSherpaOnnxLibraryPath();
result.hasPlatformPackage = !!result.foundLibraryPath;
if (!result.hasPlatformPackage) {
result.issues.push(`Platform package ${result.expectedPackage} not found`);
}
// Check if the native module exists
if (result.foundLibraryPath) {
const nativeModulePath = path.join(result.foundLibraryPath, "sherpa-onnx.node");
result.hasNativeModule = fs.existsSync(nativeModulePath);
if (!result.hasNativeModule) {
result.issues.push(`Native module not found at ${nativeModulePath}`);
}
}
// Can run if all components are available
result.canRun = result.hasMainPackage && result.hasPlatformPackage && result.hasNativeModule;
return result;
}
catch (error) {
result.issues.push(`Environment check failed: ${error instanceof Error ? error.message : String(error)}`);
return result;
}
}
/**
* Simple boolean check for backward compatibility
* @returns True if SherpaOnnx can run in the current environment
*/
function canRunSherpaOnnxSimple() {
return canRunSherpaOnnx().canRun;
}
/**
* Attempt to create platform package symlink/copy for sherpa-onnx-node compatibility
* @returns True if workaround was applied successfully
*/
function applyPlatformPackageWorkaround() {
try {
const expectedPackage = getExpectedPlatformPackage();
if (!expectedPackage) {
return false;
}
const sourcePath = path.join(process.cwd(), "node_modules", expectedPackage);
const targetPath = path.join(process.cwd(), "node_modules", "sherpa-onnx-node", "node_modules", expectedPackage);
// Check if source exists and target doesn't
if (fs.existsSync(sourcePath) && !fs.existsSync(targetPath)) {
console.log("🔧 Creating platform package symlink for sherpa-onnx-node compatibility");
// Create the directory structure
const targetDir = path.dirname(targetPath);
if (!fs.existsSync(targetDir)) {
fs.mkdirSync(targetDir, { recursive: true });
}
try {
// Try to create symlink first
fs.symlinkSync(path.relative(targetDir, sourcePath), targetPath);
console.log(`✅ Created symlink: ${targetPath} -> ${sourcePath}`);
return true;
}
catch (_symlinkError) {
// Fallback to copying if symlinks not supported
try {
copyDirectorySync(sourcePath, targetPath);
console.log(`✅ Copied platform package: ${sourcePath} -> ${targetPath}`);
return true;
}
catch (copyError) {
console.warn(`⚠️ Failed to copy platform package: ${copyError}`);
return false;
}
}
}
return false;
}
catch (error) {
console.warn("⚠️ Platform package workaround failed:", error);
return false;
}
}
/**
* Recursively copy directory (fallback for when symlinks aren't supported)
* @param src Source directory
* @param dest Destination directory
*/
function copyDirectorySync(src, dest) {
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest, { recursive: true });
}
const entries = fs.readdirSync(src, { withFileTypes: true });
for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);
if (entry.isDirectory()) {
copyDirectorySync(srcPath, destPath);
}
else {
fs.copyFileSync(srcPath, destPath);
}
}
}
/**
* Get comprehensive diagnostics for SherpaOnnx setup
* @returns Detailed diagnostic information
*/
function getSherpaOnnxDiagnostics() {
const platformKey = getCurrentPlatformKey();
const expectedPackage = getExpectedPlatformPackage();
const diagnostics = {
platform: platformKey,
expectedPackage,
hasMainPackage: false,
hasPlatformPackage: false,
hasNativeModule: false,
environmentVariables: {},
recommendations: [],
canRun: false,
};
// Check main package
const mainPackagePath = path.join(process.cwd(), "node_modules", "sherpa-onnx-node");
diagnostics.hasMainPackage = fs.existsSync(mainPackagePath);
// Check platform package
const libraryPath = findSherpaOnnxLibraryPath();
diagnostics.hasPlatformPackage = !!libraryPath;
// Check native module
if (libraryPath) {
const nativeModulePath = path.join(libraryPath, "sherpa-onnx.node");
diagnostics.hasNativeModule = fs.existsSync(nativeModulePath);
}
// Check environment variables
if (process.platform === "darwin") {
diagnostics.environmentVariables.DYLD_LIBRARY_PATH = process.env.DYLD_LIBRARY_PATH;
}
else if (process.platform === "linux") {
diagnostics.environmentVariables.LD_LIBRARY_PATH = process.env.LD_LIBRARY_PATH;
}
else if (process.platform === "win32") {
diagnostics.environmentVariables.PATH = process.env.PATH;
}
// Generate recommendations
if (!diagnostics.hasMainPackage) {
diagnostics.recommendations.push("Install sherpa-onnx-node: npm install sherpa-onnx-node@^1.12.0");
}
if (!diagnostics.hasPlatformPackage) {
diagnostics.recommendations.push(`Install platform package: npm install ${expectedPackage || "sherpa-onnx-<platform>"}@^1.12.0`);
}
if (!diagnostics.hasNativeModule && diagnostics.hasPlatformPackage) {
diagnostics.recommendations.push("Platform package exists but native module missing - try reinstalling");
}
if (diagnostics.hasMainPackage &&
diagnostics.hasPlatformPackage &&
!diagnostics.hasNativeModule) {
diagnostics.recommendations.push("Try the platform package workaround: applyPlatformPackageWorkaround()");
}
diagnostics.canRun =
diagnostics.hasMainPackage && diagnostics.hasPlatformPackage && diagnostics.hasNativeModule;
return diagnostics;
}
/**
* Create a graceful fallback loader that doesn't throw errors
* @returns Object with loaded module or null, plus error information
*/
async function loadSherpaOnnxNodeSafe() {
const environmentCheck = canRunSherpaOnnx();
if (!environmentCheck.canRun) {
// Try the platform package workaround before giving up
console.log("🔧 Attempting platform package workaround...");
const workaroundApplied = applyPlatformPackageWorkaround();
if (workaroundApplied) {
// Re-check environment after workaround
const recheckEnvironment = canRunSherpaOnnx();
if (recheckEnvironment.canRun) {
console.log("✅ Platform package workaround successful, retrying load...");
try {
const module = await loadSherpaOnnxNode();
return {
module,
success: true,
error: null,
environmentCheck: recheckEnvironment,
};
}
catch (error) {
return {
module: null,
success: false,
error: error instanceof Error ? error : new Error(String(error)),
environmentCheck: recheckEnvironment,
};
}
}
}
return {
module: null,
success: false,
error: new Error(`SherpaOnnx environment check failed: ${environmentCheck.issues.join(", ")}`),
environmentCheck,
};
}
try {
const module = await loadSherpaOnnxNode();
return {
module,
success: true,
error: null,
environmentCheck,
};
}
catch (error) {
return {
module: null,
success: false,
error: error instanceof Error ? error : new Error(String(error)),
environmentCheck,
};
}
}
;