Consumers no longer copy default-db.sqlite, migrations, sql-wasm.wasm,
or data-file-index.txt into their static assets directory. The api's
dist/ now contains everything loot-core's browser fs asks for — the
existing files plus a new data-file-index.txt manifest and a data/
mirror directory (hard-linked to avoid duplicating bytes).
At init time the main-thread facade derives the directory portion of
its own bundle URL (via string manipulation to dodge Vite's asset
plugin) and hands it to the worker as __assetsBaseUrl. The worker
sets process.env.PUBLIC_URL to that URL before calling loot-core's
init(config), so populateDefaultFilesystem and sql.js locateFile all
resolve against @actual-app/api/dist/ wherever the consumer's bundler
placed it.
Playground shrinks accordingly: no more public/ directory,
copy-assets.sh script, or predev hook. `yarn dev` now does just
`vite` — matching the zero-setup `api.init()` story.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Removes the last piece of bespoke browser wiring in @actual-app/api so
consumers call api.init({...}) with no worker construction of their own.
Under the hood, the api package now:
- Spawns its Web Worker itself via `new Worker(new URL('./worker.js',
import.meta.url), { type: 'module' })`. Consumer bundlers resolve the
sibling asset at their own build time.
- Speaks loot-core's existing {id, name, args} / {type:'reply', id,
result} backend protocol, including the {type:'connect'} handshake
— same protocol desktop-client's browser-preload.js already feeds.
- Delegates sqlite bootstrap to loot-core's public init(config) via a
worker-registered `api-browser/init` handler; server-side dispatch is
handled by the existing packages/loot-core/src/platform/server/
connection layer, so no custom {op, payload} shape remains.
The absurd-sql main-thread plumbing (initSQLBackend + __absurd:* filter)
is now a single function in loot-core:
`packages/loot-core/src/platform/client/backend-worker/createBackendWorker`,
consumed by both desktop-client's browser-preload.js and the api's
browser rpc.ts.
Test split moves accordingly: browser-facade.test.ts swaps in a
Worker mock that speaks the new protocol (id, name, args / reply handshake)
and confirms init forwards config via api-browser/init.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
absurd-sql uses Atomics.wait for sync sqlite access, which only works
inside a Web Worker. Rather than forcing every consumer to wire up
their own worker + RPC glue, ship two artifacts:
- dist/browser.js: tiny main-thread facade (~10 KB). Reuses
packages/api/methods.ts verbatim by aliasing
@actual-app/core/server/main to browser/lib-stub.ts at build time;
every lib.send call posts to the worker.
- dist/worker.js: the full loot-core + sql.js + absurd-sql stack
(~3.6 MB) running in a Web Worker.
Consumer wiring:
const worker = new Worker(
new URL('@actual-app/api/dist/worker.js', import.meta.url),
{ type: 'module' },
);
await api.init({ worker, dataDir: '/documents', serverURL, password });
await api.getAccounts();
Same named imports as Node/Electron — the worker is the only
browser-specific wiring. Keeping the URL construction in consumer
code lets their bundler (Vite, Webpack, ...) handle worker.js as an
asset without forcing us onto a single bundler convention.
Tests split accordingly: Node runs the full CRUD roundtrip against
real loot-core; jsdom runs a facade test that verifies init
validation, postMessage payload shapes, and error propagation via
a mock Worker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds setup.browser.ts with fake-indexeddb, a fetch polyfill that points
sql.js WASM and loot-core's data-file-index fetches at on-disk files,
and wires the browser Vite config to use jsdom. The shared integration
spec now gates the full CRUD roundtrip behind __API_FULL_SUITE__ (set
only in Node) because absurd-sql's worker + SharedArrayBuffer
requirement is not met under jsdom; the browser smoke test verifies
that init returns a usable handle. Full-flow browser coverage moves
to the playground app in the next phase.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a small end-to-end integration test that bootstraps a budget via
the internal create-budget handler, writes an account and a couple of
transactions through the public API, then reads them back. The spec is
environment-agnostic and will be rerun under jsdom + fake-indexeddb in
a follow-up task.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Moves methods.test.ts and its snapshots into test/, and extracts the
loot-core fs mock and IS_TESTING flag into a dedicated setup.node.ts
wired up via vite.config.mts. Prepares for a sibling setup.browser.ts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>