From 332db28e2eb1d41fc8e481409e7e1191fab63e83 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 16 Mar 2026 22:19:22 +0000 Subject: [PATCH] [AI] Add browser-compatible build for @actual-app/api The API package was previously Node.js-only due to its dependency on better-sqlite3 and Node.js fs/path modules. This adds a browser build that uses loot-core's existing browser platform implementations (sql.js/WASM, IndexedDB, absurd-sql) instead. Changes: - Add index.web.ts: browser entry point (no Node.js version check or node-fetch polyfill) - Add vite.browser.config.ts: browser-targeted Vite build that resolves to browser platform files (index.ts) instead of Node.js ones (index.api.ts -> index.electron.ts) - Update package.json: conditional exports (browser vs default/node), module field, build:browser script - Update tsconfig.json: exclude new config file from type checking The browser build outputs dist/browser.js (ESM) alongside the existing dist/index.js (CJS/Node). Bundlers that support the "browser" condition in package.json exports will automatically use the browser build. https://claude.ai/code/session_01MnxRXLNjqXrVb5CdsC85Fb --- packages/api/index.web.ts | 26 +++++++++++++++++++++++ packages/api/package.json | 30 ++++++++++++++++++++------- packages/api/tsconfig.json | 9 +++++++- packages/api/vite.browser.config.ts | 32 +++++++++++++++++++++++++++++ 4 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 packages/api/index.web.ts create mode 100644 packages/api/vite.browser.config.ts diff --git a/packages/api/index.web.ts b/packages/api/index.web.ts new file mode 100644 index 0000000000..905f07c547 --- /dev/null +++ b/packages/api/index.web.ts @@ -0,0 +1,26 @@ +import { init as initLootCore } from '@actual-app/core/server/main'; +import type { InitConfig, lib } from '@actual-app/core/server/main'; + +export * from './methods'; +export * as utils from './utils'; + +/** @deprecated Please use return value of `init` instead */ +export let internal: typeof lib | null = null; + +export async function init(config: InitConfig = {}) { + internal = await initLootCore(config); + return internal; +} + +export async function shutdown() { + if (internal) { + try { + await internal.send('sync'); + } catch { + // most likely that no budget is loaded, so the sync failed + } + + await internal.send('close-budget'); + internal = null; + } +} diff --git a/packages/api/package.json b/packages/api/package.json index b6e026f1c3..475c48da58 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -8,23 +8,39 @@ "dist" ], "main": "dist/index.js", + "module": "dist/browser.js", "types": "@types/index.d.ts", "exports": { ".": { "development": "./index.ts", - "default": "./dist/index.js" - } - }, - "publishConfig": { - "exports": { - ".": { + "browser": { + "types": "./@types/index.d.ts", + "default": "./dist/browser.js" + }, + "default": { "types": "./@types/index.d.ts", "default": "./dist/index.js" } } }, + "publishConfig": { + "exports": { + ".": { + "browser": { + "types": "./@types/index.d.ts", + "default": "./dist/browser.js" + }, + "default": { + "types": "./@types/index.d.ts", + "default": "./dist/index.js" + } + } + } + }, "scripts": { - "build": "vite build", + "build": "vite build && vite build --config vite.browser.config.ts", + "build:node": "vite build", + "build:browser": "vite build --config vite.browser.config.ts", "test": "vitest --run", "typecheck": "tsgo -b && tsc-strict" }, diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json index dc92789cf3..5442c9f370 100644 --- a/packages/api/tsconfig.json +++ b/packages/api/tsconfig.json @@ -18,5 +18,12 @@ }, "references": [{ "path": "../crdt" }, { "path": "../loot-core" }], "include": ["."], - "exclude": ["**/node_modules/*", "dist", "@types", "*.test.ts", "*.config.ts"] + "exclude": [ + "**/node_modules/*", + "dist", + "@types", + "*.test.ts", + "*.config.ts", + "*.browser.config.ts" + ] } diff --git a/packages/api/vite.browser.config.ts b/packages/api/vite.browser.config.ts new file mode 100644 index 0000000000..c2dd5bad54 --- /dev/null +++ b/packages/api/vite.browser.config.ts @@ -0,0 +1,32 @@ +import path from 'path'; + +import { defineConfig } from 'vite'; +import peggyLoader from 'vite-plugin-peggy-loader'; + +const distDir = path.resolve(__dirname, 'dist'); + +export default defineConfig({ + build: { + target: 'esnext', + outDir: distDir, + emptyOutDir: false, + sourcemap: true, + lib: { + entry: path.resolve(__dirname, 'index.web.ts'), + formats: ['es'], + fileName: () => 'browser.js', + }, + rollupOptions: { + external: [ + // These are browser APIs provided by the environment + /^node:/, + ], + }, + }, + plugins: [peggyLoader()], + resolve: { + // Default extensions — picks up browser implementations (index.ts) + // instead of .api.ts (which resolves to Node.js/Electron code) + extensions: ['.js', '.ts', '.tsx', '.json'], + }, +});