diff --git a/scripts/postinstall.mjs b/scripts/postinstall.mjs index 449947b2bc..d8379f6b43 100644 --- a/scripts/postinstall.mjs +++ b/scripts/postinstall.mjs @@ -1,18 +1,22 @@ #!/usr/bin/env node /** - * OmniRoute — Postinstall Native Module Rebuild + * OmniRoute — Postinstall Native Module Fix * * The npm package ships with a Next.js standalone build that includes - * better-sqlite3 compiled for the build platform (Linux x64). - * This script detects platform mismatches and rebuilds the native - * module for the user's actual OS/architecture. + * better-sqlite3 compiled for the build platform (Linux x64) inside + * app/node_modules/. However, npm also installs better-sqlite3 as a + * top-level dependency (in the root node_modules/), correctly compiled + * for the user's platform. + * + * This script copies the correctly-built native binary from the root + * into the standalone app directory — no rebuild or build tools needed. * * Fixes: https://github.com/diegosouzapw/OmniRoute/issues/129 + * Fixes: https://github.com/diegosouzapw/OmniRoute/issues/321 */ -import { execSync } from "node:child_process"; -import { existsSync } from "node:fs"; +import { existsSync, copyFileSync, mkdirSync } from "node:fs"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; @@ -20,62 +24,88 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); const ROOT = join(__dirname, ".."); -// The standalone build bundles better-sqlite3 inside app/node_modules -const appNodeModules = join(ROOT, "app", "node_modules", "better-sqlite3"); +const appBinary = join( + ROOT, + "app", + "node_modules", + "better-sqlite3", + "build", + "Release", + "better_sqlite3.node" +); +const rootBinary = join( + ROOT, + "node_modules", + "better-sqlite3", + "build", + "Release", + "better_sqlite3.node" +); -if (!existsSync(appNodeModules)) { - // No bundled better-sqlite3 — nothing to do (dev install, not npm global) +if (!existsSync(join(ROOT, "app", "node_modules", "better-sqlite3"))) { + // No standalone app directory — nothing to do (dev install, not npm global) process.exit(0); } -const buildInfoPath = join(appNodeModules, "build", "Release", "better_sqlite3.node"); - -// The published binary is compiled for linux-x64. -// On any other platform/arch, we must rebuild — dlopen alone is unreliable -// because macOS may load an incompatible binary without throwing. +// The published binary is compiled for linux-x64. On any other platform/arch, +// always replace it — dlopen alone is unreliable because macOS can load an +// incompatible binary without throwing (the exact bug fixed in #312). const BUILD_PLATFORM = "linux"; const BUILD_ARCH = "x64"; -const needsRebuild = process.platform !== BUILD_PLATFORM || process.arch !== BUILD_ARCH; +const platformMatch = process.platform === BUILD_PLATFORM && process.arch === BUILD_ARCH; -if (!needsRebuild) { +if (platformMatch) { try { - process.dlopen({ exports: {} }, buildInfoPath); + process.dlopen({ exports: {} }, appBinary); process.exit(0); } catch { - // Same platform but binary still incompatible (e.g. Node.js ABI mismatch) — rebuild + // Same platform but binary still incompatible (e.g. Node.js ABI mismatch) } } -console.log(`\n 🔧 Rebuilding better-sqlite3 for ${process.platform}-${process.arch}...`); +console.log(`\n 🔧 Fixing better-sqlite3 binary for ${process.platform}-${process.arch}...`); + +// Strategy 1: Copy the correctly-built binary from root node_modules +if (existsSync(rootBinary)) { + try { + mkdirSync(dirname(appBinary), { recursive: true }); + copyFileSync(rootBinary, appBinary); + + // Verify the copied binary loads + process.dlopen({ exports: {} }, appBinary); + console.log(" ✅ Native module fixed successfully!\n"); + process.exit(0); + } catch { + // Copy succeeded but binary still doesn't load — fall through + } +} + +// Strategy 2: Fall back to npm rebuild (may work if build tools are available) +console.log(" ⚠️ Root binary not available, attempting npm rebuild..."); try { + const { execSync } = await import("node:child_process"); execSync("npm rebuild better-sqlite3", { cwd: join(ROOT, "app"), stdio: "inherit", timeout: 120_000, }); -} catch (error) { - console.error(" ❌ Failed to rebuild better-sqlite3 automatically."); - console.error(" You can fix this manually by running:"); - console.error(` cd ${join(ROOT, "app")} && npm rebuild better-sqlite3`); - if (process.platform === "darwin") { - console.error(" If build tools are missing: xcode-select --install"); - } - console.error(""); - process.exit(1); + + // Verify rebuild worked + process.dlopen({ exports: {} }, appBinary); + console.log(" ✅ Native module rebuilt successfully!\n"); + process.exit(0); +} catch { + // Rebuild failed or binary still incompatible } -// Verify the rebuilt binary actually loads -try { - process.dlopen({ exports: {} }, buildInfoPath); - console.log(" ✅ Native module rebuilt successfully!\n"); -} catch { - console.error(" ❌ Rebuild completed but binary is still incompatible."); - console.error(" Try manually:"); - console.error(` cd ${join(ROOT, "app")} && npm rebuild better-sqlite3`); - if (process.platform === "darwin") { - console.error(" If build tools are missing: xcode-select --install"); - } - console.error(""); - process.exit(1); +// If nothing worked, warn but don't fail the install — let the package stay +// installed so users can fix manually or use the pre-flight check in the CLI +console.warn(" ⚠️ Could not fix better-sqlite3 native module automatically."); +console.warn(" The server may not start correctly."); +console.warn(" Try manually:"); +console.warn(` cd ${join(ROOT, "app")} && npm rebuild better-sqlite3`); +if (process.platform === "darwin") { + console.warn(" If build tools are missing: xcode-select --install"); } +console.warn("");