diff --git a/apps/desktop/electron-builder.ts b/apps/desktop/electron-builder.ts index 68820521693..a58f1210b4c 100644 --- a/apps/desktop/electron-builder.ts +++ b/apps/desktop/electron-builder.ts @@ -34,11 +34,24 @@ const config: Configuration = { asar: true, asarUnpack: [ "**/node_modules/better-sqlite3/**/*", + // better-sqlite3 uses `bindings` to locate native modules - must be unpacked together + "**/node_modules/bindings/**/*", + "**/node_modules/file-uri-to-path/**/*", "**/node_modules/node-pty/**/*", // Sound files must be unpacked so external audio players (afplay, paplay, etc.) can access them "**/resources/sounds/**/*", ], + // Extra resources placed outside asar archive (accessible via process.resourcesPath) + extraResources: [ + // Database migrations - must be outside asar for drizzle-orm to read + { + from: "dist/resources/migrations", + to: "resources/migrations", + filter: ["**/*"], + }, + ], + files: [ "dist/**/*", "package.json", @@ -47,13 +60,8 @@ const config: Configuration = { to: "resources", filter: ["**/*"], }, - // Database migrations from local-db package (copied to dist/resources/migrations by vite) - { - from: "dist/resources/migrations", - to: "resources/migrations", - filter: ["**/*"], - }, // Native modules that can't be bundled by Vite. + // bun creates symlinks for direct deps in workspace node_modules. // The copy:native-modules script replaces symlinks with real files // before building (required for Bun 1.3+ isolated installs). { @@ -61,6 +69,18 @@ const config: Configuration = { to: "node_modules/better-sqlite3", filter: ["**/*"], }, + // better-sqlite3 uses `bindings` package to locate its native .node file + { + from: "node_modules/bindings", + to: "node_modules/bindings", + filter: ["**/*"], + }, + // `bindings` requires `file-uri-to-path` for file:// URL handling + { + from: "node_modules/file-uri-to-path", + to: "node_modules/file-uri-to-path", + filter: ["**/*"], + }, { from: "node_modules/node-pty", to: "node_modules/node-pty", @@ -69,10 +89,8 @@ const config: Configuration = { "!**/.DS_Store", ], - // Skip npm rebuild - dependencies already built in monorepo - npmRebuild: false, - buildDependenciesFromSource: false, - nodeGypRebuild: false, + // Rebuild native modules for Electron's Node.js version + npmRebuild: true, // macOS mac: { diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 5a3857f4a07..445c4523663 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -59,6 +59,7 @@ "@xterm/addon-webgl": "^0.18.0", "@xterm/xterm": "^5.5.0", "better-sqlite3": "12.5.0", + "bindings": "^1.5.0", "clsx": "^2.1.1", "culori": "^4.0.2", "date-fns": "^4.1.0", @@ -71,6 +72,7 @@ "execa": "^9.6.0", "express": "^5.1.0", "fast-glob": "^3.3.3", + "file-uri-to-path": "^1.0.0", "framer-motion": "^12.23.24", "http-proxy": "^1.18.1", "jose": "^6.1.3", @@ -79,6 +81,7 @@ "lowdb": "^7.0.1", "monaco-editor": "^0.55.1", "nanoid": "^5.1.6", + "node-addon-api": "^7.1.0", "node-pty": "1.1.0-beta30", "os-locale": "^6.0.2", "posthog-js": "^1.306.1", diff --git a/apps/desktop/scripts/copy-native-modules.ts b/apps/desktop/scripts/copy-native-modules.ts index fc176626c12..0f28a56b4bd 100644 --- a/apps/desktop/scripts/copy-native-modules.ts +++ b/apps/desktop/scripts/copy-native-modules.ts @@ -16,42 +16,68 @@ import { cpSync, existsSync, lstatSync, realpathSync, rmSync } from "node:fs"; import { dirname, join } from "node:path"; +// Native modules that must exist for the app to work const NATIVE_MODULES = ["better-sqlite3", "node-pty"] as const; -function prepareNativeModules() { - console.log("Preparing native modules for electron-builder..."); +// Dependencies of native modules that need to be copied (may be hoisted or symlinked) +const NATIVE_MODULE_DEPS = ["bindings", "file-uri-to-path"] as const; - const nodeModulesDir = join(dirname(import.meta.dirname), "node_modules"); - - for (const moduleName of NATIVE_MODULES) { - const modulePath = join(nodeModulesDir, moduleName); +function copyModuleIfSymlink( + nodeModulesDir: string, + moduleName: string, + required: boolean, +): boolean { + const modulePath = join(nodeModulesDir, moduleName); - if (!existsSync(modulePath)) { + if (!existsSync(modulePath)) { + if (required) { console.error(` [ERROR] ${moduleName} not found at ${modulePath}`); process.exit(1); } + console.log(` ${moduleName}: not found (skipping)`); + return false; + } - const stats = lstatSync(modulePath); + const stats = lstatSync(modulePath); - if (stats.isSymbolicLink()) { - // Resolve symlink to get real path - const realPath = realpathSync(modulePath); - console.log(` ${moduleName}: symlink -> replacing with real files`); - console.log(` Real path: ${realPath}`); + if (stats.isSymbolicLink()) { + // Resolve symlink to get real path + const realPath = realpathSync(modulePath); + console.log(` ${moduleName}: symlink -> replacing with real files`); + console.log(` Real path: ${realPath}`); - // Remove the symlink - rmSync(modulePath); + // Remove the symlink + rmSync(modulePath); - // Copy the actual files - cpSync(realPath, modulePath, { recursive: true }); + // Copy the actual files + cpSync(realPath, modulePath, { recursive: true }); - console.log(` Copied to: ${modulePath}`); - } else { - console.log(` ${moduleName}: already real directory (not a symlink)`); - } + console.log(` Copied to: ${modulePath}`); + } else { + console.log(` ${moduleName}: already real directory (not a symlink)`); + } + + return true; +} + +function prepareNativeModules() { + console.log("Preparing native modules for electron-builder..."); + + // bun creates symlinks for direct dependencies in the workspace's node_modules + const nodeModulesDir = join(dirname(import.meta.dirname), "node_modules"); + + // Copy required native modules + for (const moduleName of NATIVE_MODULES) { + copyModuleIfSymlink(nodeModulesDir, moduleName, true); + } + + // Copy native module dependencies (not required but needed if present) + console.log("\nPreparing native module dependencies..."); + for (const moduleName of NATIVE_MODULE_DEPS) { + copyModuleIfSymlink(nodeModulesDir, moduleName, false); } - console.log("Done!"); + console.log("\nDone!"); } prepareNativeModules(); diff --git a/bun.lock b/bun.lock index 121fd07fe6f..991f0e30f62 100644 --- a/bun.lock +++ b/bun.lock @@ -145,6 +145,7 @@ "@xterm/addon-webgl": "^0.18.0", "@xterm/xterm": "^5.5.0", "better-sqlite3": "12.5.0", + "bindings": "^1.5.0", "clsx": "^2.1.1", "culori": "^4.0.2", "date-fns": "^4.1.0", @@ -157,6 +158,7 @@ "execa": "^9.6.0", "express": "^5.1.0", "fast-glob": "^3.3.3", + "file-uri-to-path": "^1.0.0", "framer-motion": "^12.23.24", "http-proxy": "^1.18.1", "jose": "^6.1.3",