mirror of
https://github.com/actualbudget/actual.git
synced 2026-05-06 20:15:33 -05:00
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>
40 lines
1.3 KiB
TypeScript
40 lines
1.3 KiB
TypeScript
// Browser main-thread stub for `@actual-app/core/server/main`.
|
|
//
|
|
// The real loot-core runs inside the worker (see browser-worker.ts). The
|
|
// main-thread bundle reuses packages/api/methods.ts verbatim, but that file
|
|
// reads `lib.send(...)` from loot-core. Resolving that import to this stub
|
|
// routes every call over postMessage instead of touching loot-core on the
|
|
// main thread.
|
|
|
|
export type BrowserSendFn = (name: string, args?: unknown) => Promise<unknown>;
|
|
|
|
let workerSend: BrowserSendFn = () => {
|
|
return Promise.reject(
|
|
new Error('@actual-app/api: call init() before any other method'),
|
|
);
|
|
};
|
|
|
|
// Shape-cast rather than `typeof import(...)` so this stub stays
|
|
// module-graph-independent from the real loot-core.
|
|
export const lib = {
|
|
send(name: string, args?: unknown) {
|
|
return workerSend(name, args);
|
|
},
|
|
} as unknown as {
|
|
send: <T = unknown>(name: string, args?: unknown) => Promise<T>;
|
|
};
|
|
|
|
export function _setBrowserSend(fn: BrowserSendFn) {
|
|
workerSend = fn;
|
|
}
|
|
|
|
// Inline InitConfig (matches loot-core's shape) so this stub does not force
|
|
// TS to pull in the real @actual-app/core/server/main module graph at all.
|
|
export type InitConfig = {
|
|
dataDir?: string;
|
|
serverURL?: string;
|
|
password?: string;
|
|
sessionToken?: string;
|
|
verbose?: boolean;
|
|
};
|