From 0779c9a84b30cd9db65ef557ec6ef847ce7cd7b6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 4 May 2026 12:41:10 +0100 Subject: [PATCH] [AI] desktop-electron: register sync-server loader on the embedded fork The Electron app starts the sync server via utilityProcess.fork, which bypasses sync-server's `start` script. With crdt now loaded from source, the fork needs the same `--import register-loader.mjs` that the standalone server uses; otherwise it crashes on the extensionless `from './crdt'` directory import. Adds the loader files to sync-server's published `files` so they actually ship with the packaged app. Co-Authored-By: Claude Opus 4.7 (1M context) --- packages/desktop-electron/index.ts | 26 ++++++++++++++++---------- packages/sync-server/package.json | 2 ++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/packages/desktop-electron/index.ts b/packages/desktop-electron/index.ts index 44299a6a1d..019d9498a9 100644 --- a/packages/desktop-electron/index.ts +++ b/packages/desktop-electron/index.ts @@ -2,6 +2,7 @@ import fs from 'fs'; import { createServer } from 'http'; import type { Server } from 'http'; import { cp, mkdir, rm } from 'node:fs/promises'; +import { pathToFileURL } from 'node:url'; import path from 'path'; import type { GlobalPrefsJson } from '@actual-app/core/types/prefs'; @@ -239,12 +240,17 @@ async function startSyncServer() { ), }; - const serverPath = path.join( - // require.resolve will recursively search up the workspace for the module - path.dirname(require.resolve('@actual-app/sync-server/package.json')), - 'build', - 'app.js', + // require.resolve will recursively search up the workspace for the module + const syncServerRoot = path.dirname( + require.resolve('@actual-app/sync-server/package.json'), ); + const serverPath = path.join(syncServerRoot, 'build', 'app.js'); + // crdt is loaded from source (no dist), so the sync-server fork needs + // sync-server's loader registered to resolve extensionless and + // directory-index imports the same way the standalone `start` script does. + const loaderUrl = pathToFileURL( + path.join(syncServerRoot, 'register-loader.mjs'), + ).href; const webRoot = path.join( // require.resolve will recursively search up the workspace for the module @@ -268,15 +274,15 @@ async function startSyncServer() { void mkdir(syncServerConfig.ACTUAL_SERVER_DATA_DIR, { recursive: true }); } - let forkOptions: ForkOptions = { + const execArgv = [`--import=${loaderUrl}`]; + if (isDev) execArgv.push('--inspect'); + + const forkOptions: ForkOptions = { stdio: 'pipe', env: envVariables, + execArgv, }; - if (isDev) { - forkOptions = { ...forkOptions, execArgv: ['--inspect'] }; - } - let syncServerStarted = false; const syncServerPromise = new Promise(resolve => { diff --git a/packages/sync-server/package.json b/packages/sync-server/package.json index 534ad9fffa..2e3897aee9 100644 --- a/packages/sync-server/package.json +++ b/packages/sync-server/package.json @@ -13,6 +13,8 @@ }, "files": [ "build", + "loader.mjs", + "register-loader.mjs", "LICENSE", "README.md" ],