Commit Graph

447 Commits

Author SHA1 Message Date
Julian Dominguez-Schatz
5fc6f7638c Check in mobile app scaffolding generated by Capacitor (#8387)
* Check in app scaffolding generated by Capacitor

All files generated by `npx cap init` and `npx cap sync`.

* Add release notes

* Add package.json file

* Update capacitor

* Prune deps

* CodeRabbit feedback
2026-07-03 21:31:30 +00:00
Matt Fiddaman
e8c7f8040e bcrypt -> argon2id (#7829)
* bcrypt -> argon2id

* coderabbit feedback

* copilot review

* switch hashing settings

* [AI] Add unit tests for sync-server password hashing

Cover password.js, focused on the bcrypt -> argon2id migration:
- loginWithPassword upgrades a legacy bcrypt hash to argon2id on a
  successful login, leaves it untouched on a failed login, and does not
  needlessly rehash an existing argon2id hash
- hashPassword/verifyPassword round-trip, bcrypt backward-compat, and
  graceful handling of non-string/malformed hashes
- bootstrapPassword/changePassword/checkPassword behaviour

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* dedupe minimatch

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 00:04:07 +00:00
dependabot[bot]
cbc056536b Bump js-yaml in the npm_and_yarn group across 1 directory (#8379)
Bumps the npm_and_yarn group with 1 update in the / directory: [js-yaml](https://github.com/nodeca/js-yaml).


Updates `js-yaml` from 3.14.2 to 3.15.0
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.14.2...3.15.0)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 3.15.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 19:13:07 +00:00
Matiss Janis Aboltins
8ad218b7d9 [AI] api: make the browser build work in consumer production bundles (#8289)
* api: make the browser build work in consumer production bundles

The browser build shipped `new Worker(new URL("./worker.js", import.meta.url))`
in dist/browser.js. A consumer bundler (Vite/Rollup) recognizes that as a worker
entry and re-bundles the already-prebuilt worker.js from scratch, which desyncs
the absurd-sql/connection RPC so `init` throws a structured-clone error. It only
worked in dev and in the package's own e2e because both serve dist verbatim.

Make the browser build fully self-contained instead:

- Inline the worker into browser.js and spawn it from a Blob URL, so consumer
  bundlers never see a worker entry to re-bundle. The worker is built as an IIFE
  (classic worker), mirroring the web app's kcab.worker.
- Embed the sql.js wasm and the default filesystem data (migrations + default
  DB) into the worker so it performs no PUBLIC_URL asset fetches:
  - loot-core sqlite gains an opt-in `setWasmBinary` (dormant for web/node);
  - the worker installs a scoped fetch shim serving the embedded data/* files
    from a sentinel base URL.
- Share the asset-collection logic between the Node disk copy and the embedded
  build via scripts/embedded-assets.mjs so the two never drift.

Result: `import '@actual-app/api'` + `init()` works in any bundler with zero
config; only COOP/COEP headers remain required (SharedArrayBuffer).

Adds an e2e (e2e/consumer + e2e/global-setup.mjs) that builds a real consumer
app for production and boots it under COOP/COEP — coverage the verbatim-dist
harness can't provide. Docs note the build is self-contained and that
cross-origin isolation is still required.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: address review feedback (guard DOM lookup, revoke blob URL on worker failure)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: address review nitpicks (drop BodyInit cast, reword release note)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: build the browser worker via Vite ?worker&inline

Replace the hand-rolled worker inlining (a custom Vite plugin that read the
prebuilt worker.js off disk and inlined it as a string, plus a second build
config and a build-ordering dependency) with Vite's native `?worker&inline`
import. This collapses the two browser build configs into one, removes the
`virtual:actual-worker-code` module and the manual Blob URL spawn, and drops
the worker-before-facade build step.

Vite's `?worker&inline` sub-build doesn't receive vite-plugin-node-polyfills'
global-shim injection (the plugin writes it to `build.rollupOptions`, but the
worker sub-build reads `worker.rollupOptions`), so the worker crashed on
`process is not defined`. Patch the plugin to also emit the injection on
`worker.rollupOptions` — additive and backwards compatible (the web app build,
including loot-core's nodePolyfills worker, is unaffected).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: embed browser assets via Vite, drop the embedded-assets script

Replace scripts/embedded-assets.mjs with Vite-native asset imports in the
worker entry: `?inline` for the sql.js wasm and default DB, `import.meta.glob`
(`?raw`) for the migrations. The worker builds the default-filesystem wire
format from those at module load, so the embedded set comes straight from
loot-core and can't drift.

The Node build only needs migrations + the default DB on disk (it reads them at
runtime); the old script also wrote sql-wasm.wasm and the data/ fetch tree,
which are dead now that the browser worker is self-contained. Replace
writeEmbeddedAssetsToDist with a small closeBundle copy of just those two.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: simplify embedded data map; drop loot-core comment churn

Merge the worker's binData/textData into a single dataFiles map (Response
accepts both bytes and strings), collapsing the fetch shim's two lookups into
one. Revert the unrelated comment edits to loot-core's backend-worker.ts and
fs/index.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] api: rename e2e consumer-dist to consumer/dist; drop a prose comment

Use the standard `dist` directory name for the consumer fixture's build output
(now e2e/consumer/dist), and remove the verbose comment from index.browser.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] loot-core: own the default-filesystem assets; consumers stop reaching in

Add packages/loot-core/default-filesystem.mjs as the single source of truth for
loot-core's runtime assets (sql.js wasm, default DB, migrations) and the
data-file-index wire format, exported as @actual-app/core/default-filesystem.

- @actual-app/api: the browser worker embeds them via the actual-embedded-assets
  Vite plugin (which calls collectEmbeddedAssets), replacing the relative
  ?inline/?raw/import.meta.glob reaches into ../loot-core; the Node build copies
  migrations + default DB using the helper's paths. Drops the direct
  @jlongster/sql.js devDependency (loot-core resolves the wasm).
- @actual-app/web: stagePublicData copies from the helper's paths instead of
  hardcoding loot-core's tree and the sql.js wasm location.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] loot-core: scan migrations once when collecting embedded assets

collectEmbeddedAssets() read the migrations directory twice — once in its
own loop and once via buildDataFileIndex(). List the names once and pass
them through, keeping the index wire-format defined in a single place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 20:36:21 +00:00
Matiss Janis Aboltins
c4acaa2951 [AI] Add knip to detect unused files, dependencies and exports (#8309)
Adds the knip tool with a monorepo-tuned config (knip.json) and a CI gate
in check.yml, plus the dead-code and dependency cleanup it surfaced. The
unused-export/type/duplicate rules are disabled so exporting an unused
symbol is allowed.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 22:11:48 +00:00
Matiss Janis Aboltins
60c5d9b993 [AI] DX: replace ts-node with Node native TypeScript execution (#8304)
* [AI] Replace ts-node with Node native TypeScript execution

Node 22.18+ runs TypeScript files directly via type stripping, so the
ts-node dependency is no longer needed for the repo's helper scripts.

- Run bin/ and loot-core pack-hook scripts with `node` instead of `ts-node`
- Rename bin/*.ts scripts to .mts (unambiguous ESM, runs warning-free)
- ESM-ify validate-publish-imports: __dirname / require.main via import.meta
- Drop ts-node devDependency and the tsconfig ts-node override block
- Bump engines.node to >=22.18.0 (first release with stable type stripping)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019d6S7BWQdmtAcYg89WuAVd

* [AI] Use .mts import specifier and simplify release note

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019d6S7BWQdmtAcYg89WuAVd

* [AI] Bump .nvmrc to v22.18.0 to match engines floor

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019d6S7BWQdmtAcYg89WuAVd

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-22 21:41:00 +00:00
Matiss Janis Aboltins
3a894b1bf6 [AI] Reduce dependencies (lodash, ua-parser-js, memoize-one, promise-retry, resize-observer polyfill) (#8278)
* [AI] Reduce dependencies (lodash, ua-parser-js, memoize-one, promise-retry, resize-observer polyfill)

Remove or replace several low-usage / native-coverable dependencies to shrink
the dependency surface and transitive deps:

- lodash -> es-toolkit (es-toolkit/compat) for the 5 functions in use
  (debounce, keyBy, isMatch, isEqual, uniqueId)
- ua-parser-js -> inline UA regex checks in loot-core shared/platform
- memoize-one -> small local memoizeOne helper in loot-core shared
- promise-retry -> small local retry helper (loot-core shared + a local copy
  in desktop-electron, which loads compiled JS at runtime)
- @juggle/resize-observer polyfill removed; native ResizeObserver is supported
  by all current targets

uuid was intentionally left untouched: crypto.randomUUID is only available in
secure contexts, which would break self-hosted instances served over plain HTTP
(including loot-core code running in the browser web worker).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdZrw7VVwyF6VJgb34U9Qo

* [AI] Use es-toolkit main entry for keyBy in report spreadsheets

The two report spreadsheet files only used keyBy from es-toolkit/compat. Switch
them to the lighter main es-toolkit entry (which takes a key function instead of
a string), so these chunks no longer pull in the compat layer. Runtime behavior
is identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdZrw7VVwyF6VJgb34U9Qo

* [AI] Rename release note to slug and reword for readability

Rename the dependency-reduction release note from the guessed PR number to a
descriptive slug, and reword the body to read clearly as changelog content.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdZrw7VVwyF6VJgb34U9Qo

* [AI] Shorten release note wording

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdZrw7VVwyF6VJgb34U9Qo

* [AI] Harden retry helper against sync throws; simplify memoizeOne typing

- retry: run fn inside a promise chain so synchronous throws (and synchronous
  retry() calls) are routed through the rejection handler instead of escaping
  uncaught. Applied to both loot-core and desktop-electron copies.
- memoizeOne: use separate Args/Result generics and drop the `as T` cast.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JdZrw7VVwyF6VJgb34U9Qo

* [AI] Update yarn.lock after merge and install

Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
2026-06-21 20:06:15 +00:00
dependabot[bot]
c1c4856aa7 Bump dompurify in the npm_and_yarn group across 1 directory (#8286)
Bumps the npm_and_yarn group with 1 update in the / directory: [dompurify](https://github.com/cure53/DOMPurify).


Updates `dompurify` from 3.4.10 to 3.4.11
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.10...3.4.11)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.11
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-21 07:12:44 +00:00
Matiss Janis Aboltins
c74923eb30 [AI] Replace vite-plugin-peggy-loader with an in-repo Vite plugin (#8285)
* [AI] Replace vite-plugin-peggy-loader with in-repo Peggy plugin

Drop the third-party vite-plugin-peggy-loader dependency in favor of a
small in-repo Vite plugin (packages/loot-core/scripts/peggy.mts) that
compiles .pegjs grammars using the existing peggy dependency. Wire it
into the loot-core and api vite/vitest configs, and remove the now-dead
loader from the component-library config (it imports no grammars).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Up3H4YR7L8Qb4UKxbasyMp

* [AI] Host the Peggy Vite plugin in a workspace package

Move the in-repo Peggy loader out of packages/loot-core/scripts and into a
dedicated private workspace package, @actual-app/vite-plugin-peggy, so the
loot-core and api configs reference it as a normal workspace dependency
instead of reaching across packages with ../loot-core relative imports.

The peggy dependency now lives with the plugin that uses it.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Up3H4YR7L8Qb4UKxbasyMp

* [AI] Rename release note to match PR number

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Up3H4YR7L8Qb4UKxbasyMp

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-21 06:53:19 +00:00
Matiss Janis Aboltins
8f27e177dc [AI] api: add browser build (#8166)
* [AI] api: add browser build

Continuation of #7545 (and #8143, which moved the browser Web Worker
bootstrap into loot-core). This adds the browser-compatible build of
@actual-app/api: the full loot-core stack runs inside a Web Worker and a
thin main-thread facade exposes the same public surface as the Node entry.

loot-core:
- Add platform/client/backend-worker.ts: createBackendWorker(worker)
  centralizes absurd-sql's main-thread initBackend + __absurd:* message
  filtering, and is exported for the api to reuse.
- Refactor browser-preload's start.ts and worker-bridge.ts to route through
  it (drops the duplicated __absurd:* filtering; terminate() now also clears
  listeners). Behavior is preserved.

api:
- browser-worker.ts: Web Worker entry owning real loot-core, speaking
  loot-core's {id,name,args} backend protocol; wraps fetch to map the
  .js -> .js.data migration names.
- index.browser.ts: main-thread facade that spawns the worker.
- browser/rpc.ts: main-thread <-> worker RPC bridge (uses createBackendWorker).
- browser/lib-stub.ts: stubs lib.send so methods.ts routes over postMessage.
- vite.browser.config.mts / vite.browser-worker.config.mts and asset bundling
  in vite.config.mts (sql-wasm.wasm + data/ + data-file-index.txt); package.json
  build chain, browser export, and absurd-sql/npm-run-all/vite-plugin-node-polyfills deps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] Rename release note to match PR number 8166

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] api: move browser worker plumbing into loot-core

Address review feedback: the browser worker logic shouldn't live in the api
package since it's generic "loot-core as a browser npm package" plumbing, not
api-specific. Resolve it via loot-core's existing conditional-export system
instead of a vite alias.

- loot-core: add server/main.api-browser.ts (worker-routing `lib` that
  delegates to the existing platform/client/connection), selected via a new
  `api-browser` export condition on "./server/main". Move the worker entry to
  server/api-browser-worker.ts.
- api: the facade (index.browser.ts) now reuses loot-core's client connection
  via a minimal global.Actual.getServerSocket shim and spawns the worker built
  from loot-core's entry. Deletes browser-worker.ts, browser/rpc.ts and
  browser/lib-stub.ts (~270 lines) in favor of the shared loot-core code.
- The facade build selects the impl with resolve.conditions: ['api-browser']
  instead of resolve.alias, removing the enforce-boundaries lint exception.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] api: simplify browser build cleanups

Post-review cleanups (no behavior change):
- backend-worker.ts: fix stale doc comment referencing the removed
  packages/api/browser/rpc.ts (the consumer is now index.browser.ts).
- api-browser-worker.ts: drop the redundant `endsWith('/data-file-index.txt')`
  disjunct (subsumed by `endsWith('data-file-index.txt')`) and the no-op
  `input as RequestInfo | URL` re-casts on the fetch pass-throughs.
- index.browser.ts: replace the single-field `state` holder with a plain
  module-level `let backend`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] api: drop the createBackendWorker wrapper, revert browser-preload churn

Now that the facade rides on loot-core's platform/client/connection (which
already ignores absurd-sql's `__`-prefixed internal messages), the only thing
the main thread still needs is absurd's one-line initBackend. So:

- Replace the 60-line createBackendWorker wrapper (onMessage/postMessage/
  terminate/listener filtering) with a tiny initBrowserBackend(worker) helper.
- Revert browser-preload/start.ts and worker-bridge.ts to their merged state —
  they keep importing absurd's initBackend directly, so this PR no longer
  refactors just-merged files and the loot-core change is purely additive.
- Facade holds a plain Worker handle and calls worker.terminate() on shutdown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] api: trim verbose comments in the browser build

Keep only the load-bearing "why" notes (the absurd-sql init, the fetch/.js.data
workaround, the global.Actual shim, the api-browser condition); drop protocol
diagrams, usage examples, and restated context. No code/behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] api: drop redundant absurd-sql dependency

No api source imports absurd-sql; it's reached only through @actual-app/core
(the facade's initBrowserBackend and the bundled worker), which declares it.
It's bundled into the build output, so it isn't a runtime dependency of the
published package either. Bundles are byte-for-byte unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] api: drop the api-browser condition, inject the worker into the connection

Review feedback: the api-browser export condition fought loot-core's idiom
(browser IS the default platform; node/"api" is the override) and hid a bug —
the condition-swapped lib only had `send`, so api.utils.amountToInteger was
silently undefined in the browser build.

Replace the condition with a plain transport seam:
- methods.ts no longer imports server/main; it sends through api/send.ts,
  which the Node entry wires to in-process lib.send and the browser entry
  wires to loot-core's client connection. utils.ts re-exports
  amountToInteger/integerToAmount from shared/util directly (fixes the bug).
- loot-core's client connection init() now accepts an optional injected
  socket; connectBackendWorker(worker) installs absurd-sql's bridge and
  connects in one call. This removes the forged global.Actual shim and the
  facade's `define: { global: 'globalThis' }` workaround. Desktop's argless
  init() path is unchanged.
- Delete main.api-browser.ts, the "./server/main" conditional export, and
  the facade's resolve.conditions.

The 4 loot-core snapshot failures (main.test.ts / transfer.test.ts) are
pre-existing on the branch without these changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] api: route methods through the client connection, slim the facade

Review feedback: get rid of the _setSend seam and move the worker init logic
into loot-core.

- methods.ts now imports `send` from loot-core's client connection. The node
  build resolves it via the existing `api` condition to a new in-process
  implementation (platform/client/connection/index.api.ts — previously a dead
  re-export of the electron client), matching the platform idiom: browser is
  the default, node/"api" overrides. api/send.ts is deleted and the node
  entry (index.ts) reverts to master byte-for-byte.
- loot-core's startBackendWorker(worker, config, assetsBaseUrl) now owns the
  whole worker boot (absurd-sql bridge + connection.init + server init RPC),
  so the browser entry just spawns the worker and calls it.
- Trims: single chained build script (drops the npm-run-all dep), copyFileSync
  instead of the linkOrCopy helper, drop the worker entry's error listeners
  (browsers already log worker uncaughts to DevTools).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] api: trim prose comments to the essential ones

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] fix client connection stalling when no sends are queued at connect

Found while smoke-testing the browser api build in a real consumer app: the
client connection only nulled messageQueue (switching from queueing to direct
postMessage) when the queue was non-empty at 'connect'. The desktop app always
has queued sends by then, so it never hits this; the api facade connects
before sending anything, so every later send queued forever and init() hung.
Drop the queue on connect unconditionally.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] fix client connection dropping api/* error replies

Found via the browser smoke test: opening an e2e-encrypted budget without the
key surfaced as "undefined is not an object" instead of an error. The server
connection forwards api/* handler failures as {type:'reply', id, error}, but
the web client connection only read `result`, silently resolving undefined for
every failed api/* call. Reject when the reply carries an error, matching the
server's envelope (and the old api rpc behavior). The electron client has the
same latent gap; left untouched here.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] api: move the worker entry back into the api package

Review feedback: browser-worker.ts is the api package's build entry, not
generic loot-core code — move it back (it now imports loot-core through the
public @actual-app/core exports instead of #-subpaths).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] api: drop the manifest rewrite, align shutdown, trim comments

The data-file-index.txt manifest now lists migrations under their real .js
names (the worker's fetch shim already redirects those to the .js.data files
on disk), so the manifest-rewriting half of the shim is gone. The worker init
payload carries config and assetsBaseUrl as separate fields instead of
smuggling a private key into the config object, browser shutdown() mirrors
the node facade, and the client connection's queue flush drops a redundant
branch.

* [AI] fix rejected handler promises poisoning close-budget

runHandler only removed a method's promise from runningMethods when it
fulfilled, so any handler rejection left a stale promise in the set. The next
close-budget then failed: flushRunningMethods' Promise.all rejected with that
old, unrelated error before closeBudget even ran. Remove settled promises on
rejection too, and flush with Promise.allSettled since rejected methods
already reported their error to their caller.

* [AI] api: add a browser e2e test

A Playwright test loads the built browser bundle in chromium from a static
COOP/COEP server and exercises the full stack: worker boot, the rejected-error
contract, budget import, transaction read-back, and IndexedDB persistence
across a page reload. A dedicated workflow runs it in CI on api/loot-core
changes.

* [AI] api: drop the worker fetch shim; strip .data names in loot-core fs

The .js.data scheme was spread across three coordinated pieces: the build
renamed migrations on disk, the manifest listed names that didn't exist, and
a global fetch monkey-patch in the worker un-lied at runtime. Now the
manifest lists the true on-disk names and populateDefaultFilesystem (the only
fetcher of these files) strips the .data suffix when naming the virtual file
— the fetch patch is gone and the suffix becomes a small documented fs
convention. Also: e2e static server uses a single stat and drops a dead MIME
entry.

* [AI] fix electron client connection dropping api/* error replies

Both server connection variants post api/* handler failures as
{type: 'reply', id, error}, but only the web client rejected on it
(778ed25cf); the electron renderer client still resolved undefined. Mirror
the check so the wire contract is implemented uniformly.

* [AI] api: exclude the playwright e2e from vitest; review polish

vitest's default include matched e2e/browser.test.ts and failed CI - a
playwright test file defines no vitest tests. Exclude e2e/ from the unit-test
run (it has its own yarn e2e script). Also from review: the browser exports
condition now carries its own declaration file (the browser init's return
type differs from the node facade), and a failed init terminates the worker
it just spawned before rethrowing instead of leaving it running.

* [AI] api: trim code comments to the essentials

* [AI] api: always terminate the worker when browser shutdown fails

Review feedback: a rejected close-budget left the worker running. Terminate
in a finally block so cleanup is guaranteed while the failure still reaches
the caller.

* [AI] docs: document experimental browser support in the api package

* [AI] docs: drop the CORS note from the api browser section

* [AI] docs: trim the api browser section to the basics

* [AI] api: drop the import comment in methods.ts

* [AI] Run API browser test on depot runner

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* [AI] Align api vite-plugin-node-polyfills to 0.28.0

The api workspace was pinned to ^0.27.0 (left over from an earlier merge
conflict resolution) while loot-core uses ^0.28.0. This both tripped the
workspace version-consistency constraint and broke the browser worker build:
under vite 8.0.16 the 0.27 process shim no longer round-trips runtime writes
to process.env.PUBLIC_URL, so sql-wasm.wasm was fetched from
/dist/undefinedsql-wasm.wasm (404) and api.init() failed in the browser e2e.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 06:32:26 +00:00
dependabot[bot]
e0880f3901 Bump the npm_and_yarn group across 1 directory with 2 updates (#8272)
Bumps the npm_and_yarn group with 2 updates in the / directory: [http-proxy-middleware](https://github.com/chimurai/http-proxy-middleware) and [undici](https://github.com/nodejs/undici).


Updates `http-proxy-middleware` from 3.0.6 to 3.0.7
- [Release notes](https://github.com/chimurai/http-proxy-middleware/releases)
- [Changelog](https://github.com/chimurai/http-proxy-middleware/blob/v3.0.7/CHANGELOG.md)
- [Commits](https://github.com/chimurai/http-proxy-middleware/compare/v3.0.6...v3.0.7)

Updates `undici` from 7.27.2 to 7.28.0
- [Release notes](https://github.com/nodejs/undici/releases)
- [Commits](https://github.com/nodejs/undici/compare/v7.27.2...v7.28.0)

---
updated-dependencies:
- dependency-name: http-proxy-middleware
  dependency-version: 3.0.7
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: undici
  dependency-version: 7.28.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-18 19:48:40 +00:00
dependabot[bot]
1ad52d7128 Bump the npm_and_yarn group across 1 directory with 6 updates (#8253)
Bumps the npm_and_yarn group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.4.2` | `3.4.10` |
| [form-data](https://github.com/form-data/form-data) | `4.0.5` | `4.0.6` |
| [launch-editor](https://github.com/vitejs/launch-editor) | `2.12.0` | `2.14.1` |
| [tar](https://github.com/isaacs/node-tar) | `7.5.13` | `7.5.16` |
| [webpack-dev-server](https://github.com/webpack/webpack-dev-server) | `5.2.4` | `5.2.5` |
| [ws](https://github.com/websockets/ws) | `7.5.10` | `7.5.11` |



Updates `dompurify` from 3.4.2 to 3.4.10
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.4.2...3.4.10)

Updates `form-data` from 4.0.5 to 4.0.6
- [Release notes](https://github.com/form-data/form-data/releases)
- [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md)
- [Commits](https://github.com/form-data/form-data/compare/v4.0.5...v4.0.6)

Updates `launch-editor` from 2.12.0 to 2.14.1
- [Commits](https://github.com/vitejs/launch-editor/compare/v2.12.0...v2.14.1)

Updates `tar` from 7.5.13 to 7.5.16
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v7.5.13...v7.5.16)

Updates `webpack-dev-server` from 5.2.4 to 5.2.5
- [Release notes](https://github.com/webpack/webpack-dev-server/releases)
- [Changelog](https://github.com/webpack/webpack-dev-server/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-server/compare/v5.2.4...v5.2.5)

Updates `ws` from 7.5.10 to 7.5.11
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.5.10...7.5.11)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.10
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: form-data
  dependency-version: 4.0.6
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: launch-editor
  dependency-version: 2.14.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: tar
  dependency-version: 7.5.16
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: webpack-dev-server
  dependency-version: 5.2.5
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: ws
  dependency-version: 7.5.11
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 19:49:49 +00:00
dependabot[bot]
df00ff2028 Bump joi in the npm_and_yarn group across 1 directory (#8239)
Bumps the npm_and_yarn group with 1 update in the / directory: [joi](https://github.com/hapijs/joi).


Updates `joi` from 17.13.3 to 17.13.4
- [Commits](https://github.com/hapijs/joi/compare/v17.13.3...v17.13.4)

---
updated-dependencies:
- dependency-name: joi
  dependency-version: 17.13.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-17 17:25:30 +00:00
Kevin
de245df61f Upgrade pluggy-sdk and migrate away from deprecated page-size transactions to cursor-based transactions (#8103)
* Bump pluggy-sdk to include new fetchTransactionsCursor GET /v2/transactions

* Use fetchAllTransactions, which uses cursor-based /v2/transactions

* Remove weak helper function

* Add release note

* Fix unused result mapping

* Add lelemm's changes

Co-authored by: lelemm

* Fix lint

* Add lelemm as co-author on release note

* Update message

* Fix comma typo

* Update yarn.lock

---------

Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2026-06-17 15:48:56 +00:00
Matt Fiddaman
fbdad57ff0 ⬆️ mid month dependency updates (#8235)
* @monorepo-utils/workspaces-to-typescript-project-references (^2.10.3 → ^2.11.0)

* @types/node (^22.19.17 → ^22.19.21)

* eslint (^10.2.0 → ^10.5.0)

* eslint-plugin-perfectionist (^5.8.0 → ^5.9.0)

* lage (^2.15.5 → ^2.15.13)

* typescript (^6.0.2 → ^6.0.3)

* vitest (^4.1.2 → ^4.1.8)

* better-sqlite3 (^12.8.0 → ^12.10.0)

* vite (^8.0.5 → ^8.0.16)

* cosmiconfig (^9.0.1 → ^9.0.2)

* react-aria-components (^1.16.0 → ^1.18.0)

* @chromatic-com/storybook (^5.1.1 → ^5.2.1)

* @storybook/addon-a11y (^10.3.4 → ^10.4.4)

* @storybook/addon-docs (^10.3.4 → ^10.4.4)

* @storybook/react-vite (^10.3.4 → ^10.4.4)

* @types/react (^19.2.14 → ^19.2.17)

* @vitejs/plugin-react (^6.0.1 → ^6.0.2)

* eslint-plugin-storybook (^10.3.4 → ^10.4.4)

* storybook (^10.3.4 → ^10.4.4)

* @bufbuild/protobuf (^2.11.0 → ^2.12.0)

* @bufbuild/protoc-gen-es (^2.11.0 → ^2.12.0)

* @babel/core (^7.29.0 → ^7.29.7)

* @codemirror/autocomplete (^6.20.1 → ^6.20.3)

* @react-aria/interactions (^3.27.1 → ^3.28.1)

* @reduxjs/toolkit (^2.11.2 → ^2.12.0)

* @tanstack/react-query (^5.96.2 → ^5.101.0)

* @uiw/react-codemirror (^4.25.9 → ^4.25.10)

* date-fns (^4.1.0 → ^4.4.0)

* hyperformula (^3.2.0 → ^3.3.0)

* lru-cache (^11.2.7 → ^11.5.1)

* react-aria (^3.47.0 → ^3.49.0)

* react-error-boundary (^6.1.1 → ^6.1.2)

* react-hotkeys-hook (^5.2.4 → ^5.3.2)

* react-redux (^9.2.0 → ^9.3.0)

* react-spring (^10.0.3 → ^10.0.4)

* rolldown (^1.0.0-rc.13 → ^1.1.1)

* sass (^1.99.0 → ^1.101.0)

* vite-plugin-pwa (^1.2.0 → ^1.3.0)

* @docusaurus/core (^3.10.0 → ^3.10.1)

* @docusaurus/plugin-content-docs (^3.10.0 → ^3.10.1)

* @docusaurus/plugin-ideal-image (^3.10.0 → ^3.10.1)

* @docusaurus/preset-classic (^3.10.0 → ^3.10.1)

* @docusaurus/theme-common (^3.10.0 → ^3.10.1)

* @docusaurus/theme-mermaid (^3.10.0 → ^3.10.1)

* @easyops-cn/docusaurus-search-local (^0.55.1 → ^0.55.2)

* @docusaurus/module-type-aliases (^3.10.0 → ^3.10.1)

* @oxlint/plugins (^1.60.0 → ^1.69.0)

* ua-parser-js (^2.0.9 → ^2.0.10)

* fast-check (^4.6.0 → ^4.8.0)

* jest-diff (^30.3.0 → ^30.4.1)

* workbox-precaching (^7.4.0 → ^7.4.1)

* ipaddr.js (^2.3.0 → ^2.4.0)

* jws (^3.2.2 → ^3.2.3)

* http-proxy-middleware (^3.0.5 → ^3.0.6)

* oxlint (^1.59.0 → ^1.69.0)

* @codemirror/view (^6.41.0 → ^6.43.1)

* yarn dedupe

* nano-staged (^0.8.0 → ^1.0.2)

* commander (^14.0.3 → ^15.0.0)

* react (>=19.2 → 19.2.7)

* react-dom (>=19.2 → 19.2.7)

* @rolldown/plugin-babel (~0.1.8 → ~0.2.3)

* downshift (9.3.2 → 9.3.6)

* i18next (^25.10.10 → ^26.3.1)

* react-i18next (^16.6.6 → ^17.0.8)

* react-router (7.15.0 → 7.17.0)

* vite-plugin-node-polyfills (^0.27.0 → ^0.28.0)

* jws (^3.2.3 → ^4.0.1)

* oxlint-tsgolint (^0.20.0 → ^0.23.0)

* jsdom (^27.4.0 → ^29.1.1)

* note

* [autofix.ci] apply automated fixes

* [autofix.ci] apply automated fixes (attempt 2/3)

* crdt regenerate and version bump

* fix test

* yarn dedupe

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-16 09:21:37 +00:00
dependabot[bot]
c31219291c Bump esbuild in the npm_and_yarn group across 1 directory (#8213)
Bumps the npm_and_yarn group with 1 update in the / directory: [esbuild](https://github.com/evanw/esbuild).


Updates `esbuild` from 0.25.10 to 0.25.12
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2025.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.10...v0.25.12)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.25.12
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-14 05:28:37 +00:00
dependabot[bot]
351f544498 Bump shell-quote in the npm_and_yarn group across 1 directory (#8182)
Bumps the npm_and_yarn group with 1 update in the / directory: [shell-quote](https://github.com/ljharb/shell-quote).


Updates `shell-quote` from 1.8.3 to 1.8.4
- [Changelog](https://github.com/ljharb/shell-quote/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/shell-quote/compare/v1.8.3...v1.8.4)

---
updated-dependencies:
- dependency-name: shell-quote
  dependency-version: 1.8.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-10 20:02:31 +00:00
Matiss Janis Aboltins
328417f8de [AI] Consolidate agent tooling and simplify developer guidance (#8089)
* [AI] Replace mechanical agent rules with shared agent hooks + nano-staged

Move the mechanical "remember to run X" rules out of AGENTS.md and the
agent guidance docs into deterministic, cross-agent hooks, and migrate
the pre-commit runner from lint-staged to nano-staged.

- Add shared hook scripts in scripts/agent-hooks/ (git-guard,
  format-edited-file, no-strict-ignore-new-file, prefer-one-component,
  check-on-stop, common helpers) wired for Claude (.claude/settings.json),
  Codex (.codex/config.toml), and Cursor (.cursor/hooks.json + adapters).
- Enforce "avoid enum" via a new actual/no-enum lint rule, grandfathering
  the two existing declarations in .oxlintrc.json overrides.
- Migrate lint-staged -> nano-staged (.nano-staged.json, .husky/pre-commit,
  package.json).
- Trim AGENTS.md, .github/agents/pr-and-commit-rules.md, the Cursor rule,
  and the committing skill down to the rules that aren't auto-enforced.
- Add release note 8089.md.

* [AI] Align PR-title wording in AGENTS.md with canonical rules

* [AI] Fix check-on-stop hook on bash 3.2 and TSV column collapse

The Stop hook silently no-op'd in two ways:

- `declare -A` isn't supported on stock macOS bash 3.2, and the
  slash/hyphen path subscript aborted under `set -u`. Dedupe via a
  space-padded string match instead.
- jq emitted an empty middle TSV field for a package with test but no
  typecheck; tab is IFS-whitespace so `read` collapsed it and shifted the
  columns (eslint-plugin-actual then ran neither). Emit non-empty
  sentinels (-, yes/no) so no field can collapse.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-08 20:54:54 +00:00
Matiss Janis Aboltins
ebcb86ab7d [AI] Move browser web-worker logic into loot-core (#8143)
* [AI] Move browser web-worker logic into loot-core

Relocate the browser Web Worker bootstrap (WorkerBridge + startBrowserBackend)
and the multi-tab SharedWorker coordinator from desktop-client into loot-core
so they can be reused (e.g. by the api package in a browser).

Behavior is preserved as faithfully as possible. The two Vite worker entry
targets stay in desktop-client (the browser-server.js classic worker and the
?sharedworker entry); the ?sharedworker entry now imports the coordinator from
loot-core. console.* calls in the moved code route through loot-core's logger,
except the SharedWorker console-forwarding mechanism, which must keep the real
console. absurd-sql's initBackend is imported directly (loot-core already
depends on absurd-sql); the desktop-client dependency was dropped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] Rename release note to match PR number 8143

* Update release notes for Web Worker migration

Removed mention of reuse by the API package in the release notes.

* [AI] Drop move-related header comments per review

Remove the "moved from desktop-client" header comments that only made sense
in the context of this PR. worker-bridge.ts and start.ts had no header comment
in the original source, so they're removed entirely; coordinator.ts's header
is restored to its original wording.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 20:50:59 +00:00
ItsThatDude
f1c0960fee Feature: Add Akahu New Zealand bank sync (#6041)
* add akahu integration for nz banks

* akahu fix bank name being set to account name

* rename apiToken and fix reset pointing to the wrong function

* fix apitoken wording in akahu init modal

* add upcoming-release-notes

* fix lint issues

* fix lint issues

* add loan account type to starting balance inversion

* fix initial sync balance

* initially select 365 days of transactions on first sync for akahu sync

* add SyncServerAkahuAccount to onSetLinkedAccount

* set transaction currency to account currency

* remove unnecessary code

* handle TFR TO/FROM payees and account for loan account type in transactions

* [autofix.ci] apply automated fixes

* rename Error to ErrorAlert and fix intial sync start date

* extract note from description for TFR TO/FROM transactions

* fix normalizeNotes not referencing the transaction

* refactor app-akahu code

* [autofix.ci] apply automated fixes

* Add Akahu to ExternalAccount type

* Update yarn.lock

* update yarn.lock

* Remove unused error var in catch block

* [autofix.ci] apply automated fixes

* require authentication for akahu endpoint

* fix lint issue in mutations.ts

* fix up import paths

* fix lint issues

* reorder form fields

* remove unnecessary handling for debt accounts

* lint fixes

* Put Akahu bank sync under feature flag

* remove incorrect feedback link

* [autofix.ci] apply automated fixes

* Add feedback link for feature toggle

* use uuidv4

* prevent fetch if feature not enabled

* change app-akahu to ts and tidy up

* fix typecheck errors

* [autofix.ci] apply automated fixes

* fix browser client build issues

* [autofix.ci] apply automated fixes

* update akahu npm package to latest version

* use amountToInteger for balance reducer

* add additional details to transactions

* change initial sync start date logic

* add akahu fields to mappable fields in desktop-client

* [autofix.ci] apply automated fixes

* getDate use formatISO to get the timezone adjusted date

* remove duplicate payeeName

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-06-06 18:05:10 +00:00
dependabot[bot]
e934258dc8 Bump react-router in the npm_and_yarn group across 1 directory (#8072)
Bumps the npm_and_yarn group with 1 update in the / directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router).


Updates `react-router` from 7.14.2 to 7.15.0
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.15.0/packages/react-router)

---
updated-dependencies:
- dependency-name: react-router
  dependency-version: 7.15.0
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-04 20:00:37 +00:00
dependabot[bot]
e9f7e3672b Bump the npm_and_yarn group across 2 directories with 1 update (#8071)
Bumps the npm_and_yarn group with 1 update in the / directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router).
Bumps the npm_and_yarn group with 1 update in the /packages/desktop-client directory: [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router).


Updates `react-router` from 7.13.1 to 7.14.2
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.14.2/packages/react-router)

Updates `react-router` from 7.13.1 to 7.14.2
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router@7.14.2/packages/react-router)

---
updated-dependencies:
- dependency-name: react-router
  dependency-version: 7.14.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: react-router
  dependency-version: 7.14.2
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-03 21:31:10 +00:00
Matiss Janis Aboltins
75dd9f674a [AI] Add auto-generated Upcoming Release docs page (#8065)
* [AI] Add auto-generated Upcoming Release docs page

Adds a docs page that always reflects the current contents of the
repo-root upcoming-release-notes/ directory — the changes that have been
merged but not yet published in a stable release (i.e. what ships in the
nightly/edge builds).

- Extract parseReleaseNotes/formatNotes from the release-notes generator
  into the shared ci-actions util so both the release generator and the
  docs build reuse the same formatting.
- Add a docs build script that regenerates docs/upcoming-release-notes.md
  on every start/build, with an intro explaining the notes are unreleased
  and how to try them, plus the categorized notes list.
- Wire the generator into the docs start/build scripts, add a sidebar
  entry next to Release Notes, and gitignore the generated page.

https://claude.ai/code/session_01MuY9Phome8uJKH51HbrsQw

* [AI] Simplify upcoming-release-notes generator

- Declare @actual-app/ci-actions as a workspace dependency and import the
  shared util by package name instead of a relative cross-package path, so
  the dependency is registered in the workspace graph.
- Resolve the repo root once via new URL() and drop the dirname import.
- Use a ternary for the page body instead of let + if/else.

https://claude.ai/code/session_01MuY9Phome8uJKH51HbrsQw

* Add release notes for PR #8065

* [AI] Remove edge references, keep only nightly in docs

Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>

* Update packages/docs/scripts/generate-upcoming-release-notes.mjs

Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2026-06-03 20:45:11 +00:00
Copilot
3f7439f180 [AI] Scope Electron update to v41 (#8044)
* Initial plan

* Bump desktop Electron runtime to v42.3.0

* Add release notes entry for PR #8044

* Change author from Copilot to MikesGlitch

Updated author information in release notes.

* Update node-abi mappings to support Electron 42 ABI detection

* Address validation feedback for Electron update PR

* Add better-sqlite3 Electron 42 patch

* Update lockfile for better-sqlite3 patch

* Remove better-sqlite patch protocol and target Electron 41

* Align release note with Electron 41.7.1 change

* Update better-sqlite3 ranges to ^12.8.0

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Michael Clark <5285928+MikesGlitch@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2026-06-03 14:10:05 +00:00
Matiss Janis Aboltins
7f4a0e7d6b [AI] Move i18n usage outside of loot-core (#8027)
* [AI] Move i18n usage outside of loot-core

loot-core should be platform-agnostic and free of i18n concerns. This
moves all user-facing translated string helpers (rule/schedule labels,
error formatters) into desktop-client's #util/{rule,schedule,error}
modules, while keeping the underlying logic in loot-core.

- Headless @actual-app/api error formatters in loot-core now return
  plain English strings (the API has no i18n runtime).
- The persisted "Unknown" institution name and the platform storage
  alert use plain strings.
- Removed the unused i18next dependency from loot-core.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* [AI] Address review feedback on i18n-out-of-loot-core PR

- Wrap switch default arms in blocks to satisfy noSwitchDeclarations
  (desktop-client and loot-core getDownloadError)
- getSecretsError: return a localized generic message instead of leaking
  the raw backend error token
- getRecurringDescription: always separate the weekend annotation so it no
  longer renders as "Monday(after weekend)"; add test coverage
- IndexedDB quota error: surface a typed 'indexeddb-quota-error' event from
  loot-core and present the localized alert in desktop-client instead of a
  hard-coded English string
- Persist a null bank name (instead of English "Unknown") when the provider
  reports no institution, and render a localized fallback in the bank sync UI

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 22:10:33 +00:00
Matiss Janis Aboltins
c02dfc0d29 [AI] Fix docusaurus docs build (#8018)
* [AI] Fix docs build by pinning webpackbar to v7

webpack 5.106+ removed deprecated ProgressPlugin options, which breaks
the older webpackbar 6.0.1 that Docusaurus 3.10.0 depends on. The build
fails with "Progress Plugin has been initialized using an options object
that does not match the API schema" (unknown properties name/color/
reporters/reporter).

Add a webpackbar ^7.0.0 resolution, matching the upstream fix in
Docusaurus 3.10.1 (facebook/docusaurus#11981).

* Add release notes for PR #8018

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-06-01 12:20:59 +00:00
Matiss Janis Aboltins
ff70d2d5f8 [AI] Upgrade dependencies to resolve security advisories (#7982)
* [AI] Upgrade dependencies to resolve security advisories

Resolve 9 Dependabot security alerts via version upgrades:

- rollup: remove the resolutions pin that forced workbox-build's
  rollup ^2.79.2 up to vulnerable 4.40.1; it now resolves to the
  patched 2.80.0 (vite 8 uses rolldown, so no rollup 4.x is needed)
  (CVE-2026-27606)
- serialize-javascript: add resolution ^7.0.5 (build-time only;
  consumers are deep-transitive webpack/terser plugins pinned to ^6)
  (GHSA-5c6j-r48x-rmvq, CVE-2026-34043)
- express-rate-limit ^8.3.2 -> ^8.5.2, pulling ip-address 10.2.0
  (CVE-2026-42338)
- refresh in-range transitives: bn.js 5.2.3, ajv 8.20.0, glob 10.5.0,
  path-to-regexp 8.4.2 (CVE-2026-2739, CVE-2025-69873, CVE-2025-64756,
  CVE-2026-4926, CVE-2026-4923)

Not addressed: elliptic (CVE-2025-14505) has no patched release;
uuid 8.3.2 (dev-only via sockjs, not reachable) left as-is.

https://claude.ai/code/session_018obbND7t9dBZvfvUBBJKFz

* [AI] Remove redundant minimatch resolution pins

Five of the six minimatch resolutions were no-ops: their consumers use
caret ranges (^3.x, ^5.x, ^9.x, ^10.x) that already float to versions
patched against CVE-2026-26996 (3.1.5, 5.1.9, 9.0.9, 10.2.5). Only
serve-handler pins minimatch to exactly 3.1.2 (vulnerable), so the
single targeted "minimatch@3.1.2" override is kept. The resolved
lockfile is unchanged.

https://claude.ai/code/session_018obbND7t9dBZvfvUBBJKFz

* Add release notes for PR #7982

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-28 19:03:12 +00:00
dependabot[bot]
be5804717a Bump the npm_and_yarn group across 1 directory with 3 updates (#7980)
Bumps the npm_and_yarn group with 3 updates in the / directory: [ip-address](https://github.com/beaugunderson/ip-address), [tmp](https://github.com/raszi/node-tmp) and [webpack](https://github.com/webpack/webpack).


Updates `ip-address` from 10.0.1 to 10.1.0
- [Commits](https://github.com/beaugunderson/ip-address/compare/v10.0.1...v10.1.0)

Updates `tmp` from 0.2.5 to 0.2.7
- [Changelog](https://github.com/raszi/node-tmp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/raszi/node-tmp/compare/v0.2.5...v0.2.7)

Updates `webpack` from 5.102.1 to 5.107.2
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.102.1...v5.107.2)

---
updated-dependencies:
- dependency-name: ip-address
  dependency-version: 10.1.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: tmp
  dependency-version: 0.2.7
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: webpack
  dependency-version: 5.107.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-28 17:20:54 +00:00
dependabot[bot]
8d5f903b54 Bump qs from 6.15.1 to 6.15.2 (#7932)
Bumps [qs](https://github.com/ljharb/qs) from 6.15.1 to 6.15.2.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.15.1...v6.15.2)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.15.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-23 23:20:26 +00:00
clintharris
fb4b0aaba8 fix: Add glob-hasher-linux-arm64-gnu as optional dependency to fix yarn typecheck error on ARM64 containers (#7917)
* fix: Add glob-hasher-linux-arm64-gnu as optional dependency for ARM64 containers

* docs: Add release notes for PR #7916

* Renamed `upcoming-release-notes/7916.md` → `7917.md`

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-21 15:17:41 +00:00
dependabot[bot]
802fde5dbe Bump ws from 7.5.10 to 8.20.1 (#7895)
Bumps [ws](https://github.com/websockets/ws) from 7.5.10 to 8.20.1.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.5.10...8.20.1)

---
updated-dependencies:
- dependency-name: ws
  dependency-version: 8.20.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-19 20:30:09 +00:00
dependabot[bot]
ffb3c1b708 Bump webpack-dev-server from 5.2.2 to 5.2.4 (#7896)
Bumps [webpack-dev-server](https://github.com/webpack/webpack-dev-server) from 5.2.2 to 5.2.4.
- [Release notes](https://github.com/webpack/webpack-dev-server/releases)
- [Changelog](https://github.com/webpack/webpack-dev-server/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack-dev-server/compare/v5.2.2...v5.2.4)

---
updated-dependencies:
- dependency-name: webpack-dev-server
  dependency-version: 5.2.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-19 20:07:12 +00:00
Matiss Janis Aboltins
46c350613c [AI] Upgrade vite-plugin-node-polyfills to 0.27.0 (#7860)
* [AI] Upgrade vite-plugin-node-polyfills to 0.27.0

Bumps `vite-plugin-node-polyfills` from `^0.26.0` to `^0.27.0` in
`packages/loot-core`.

Changelog: https://github.com/davidmyersdev/vite-plugin-node-polyfills/releases/tag/v0.27.0
Related upstream issue (already referenced from `vite.config.mts`):
https://github.com/davidmyersdev/vite-plugin-node-polyfills/issues/142

* [AI] Add release note for vite-plugin-node-polyfills upgrade

* [AI] Remove stale upstream issue reference from vite config

The link pointed at davidmyersdev/vite-plugin-node-polyfills#142, which
no longer needs a workaround note alongside the `nodePolyfills` call.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-16 15:03:52 +00:00
dependabot[bot]
740392941d Bump qs from 6.13.0 to 6.15.1 (#7857)
Bumps [qs](https://github.com/ljharb/qs) from 6.13.0 to 6.15.1.
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.13.0...v6.15.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.15.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-15 20:53:31 +00:00
dependabot[bot]
d4528e18ea Bump lodash-es from 4.17.21 to 4.18.1 (#7854)
Bumps [lodash-es](https://github.com/lodash/lodash) from 4.17.21 to 4.18.1.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.18.1)

---
updated-dependencies:
- dependency-name: lodash-es
  dependency-version: 4.18.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-15 20:23:31 +00:00
Julian Dominguez-Schatz
a7e22b023c Disable postinstall scripts except for an allowlist (#7825)
* Disable postinstall scripts except for an allowlist

* Add release notes

* Temp: trigger docs CI

* Revert "Temp: trigger docs CI"

This reverts commit 1c2ca1125c.

* Remove some unneeded builds
2026-05-13 13:29:15 +00:00
Matiss Janis Aboltins
2c7f3c7a3d [AI] Replace google-protobuf with @bufbuild/protobuf (#7535)
* [AI] crdt: typecheck test files and clean up lint issues

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Replace google-protobuf with @bufbuild/protobuf

Swap the google-protobuf + ts-protoc-gen + protoc-gen-js toolchain for
@bufbuild/protobuf + @bufbuild/protoc-gen-es. The generator now emits a
single pure-TS sync_pb.ts (no .js sidecar, no globalThis.proto hack)
and a thin wrapper in proto/compat.ts preserves the SyncProtoBuf /
SyncRequest / etc. API so call sites stay unchanged. Removes the
loot-core CommonJS require polyfill that only existed to service
google-protobuf.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Align @bufbuild/protobuf version ranges with installed

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] crdt: drop the SyncProtoBuf compat layer

The proto/compat.ts wrapper was introduced alongside the bufbuild
migration to avoid touching call sites. With bufbuild messages already
exposing fields as plain mutable properties, the wrapper was just
boilerplate hiding direct reads and writes — and it had drifted (e.g.
setMessagesList was called in a test but never defined).

Delete compat.ts and migrate the six call sites in loot-core and
sync-server to use @bufbuild/protobuf directly. The crdt package now
re-exports the sync_pb types/schemas and the three bufbuild runtime
helpers (create, fromBinary, toBinary) so consumers keep a single
import source.

Also switch sync-server's @actual-app/crdt dependency from the pinned
"2.1.0" to "workspace:*", matching api/loot-core — the npm pin was
pulling the stale published copy instead of the workspace source.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] CI: drive sync-server build through lage so crdt deps are built

Before: the server job ran `yarn workspace @actual-app/sync-server build`
directly, which invokes tsgo without first emitting the workspace
dependencies' declarations. That worked when sync-server pinned crdt to
the published npm version (declarations bundled in the tarball), but
with `workspace:*` it fails with TS6305 because packages/crdt/dist/*.d.ts
hasn't been built yet.

Switch the CI command to `yarn build --to=@actual-app/sync-server`.
Lage respects the `dependsOn: ['^build']` pipeline and builds
@actual-app/crdt (and the other transitive deps) before sync-server.

Using --to rather than --scope keeps the build set minimal; --scope
would also include dependents like desktop-electron.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] sync-server: build project references via tsgo -b

The build script ran plain `tsgo`, which doesn't compile referenced
projects. With @actual-app/crdt now a `workspace:*` dep (no bundled
declarations from the npm tarball), the sync-server build fails with
TS6305 because packages/crdt/dist/index.d.ts doesn't exist yet.

Switch to `tsgo -b` so the sync-server build is self-contained: it
emits crdt's declarations into packages/crdt/dist on demand. This
mirrors what the sync-server `typecheck` script already does and fixes
all callers (`build:server`, docker-edge, publish workflows, the
direct `yarn workspace @actual-app/sync-server build` invocation in
build.yml) without needing per-workflow lage orchestration.

Revert the build.yml workaround added in the previous commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] sync-server: build @actual-app/crdt before tsgo

The previous tsgo -b approach emitted crdt's .d.ts via the project
reference but never produced dist/index.js — tsgo respects crdt's
tsconfig which has emitDeclarationOnly: true, and the actual JS
runtime is emitted by Vite in crdt's build script. So sync-server
compiled cleanly but crashed at runtime when forked by desktop-electron
(require('@actual-app/crdt') resolved to a package whose main pointed
at a nonexistent file, surfaced in e2e as the onboarding screen never
leaving the "Configure your server" state).

Unlike packages/api (which uses Vite with noExternal: true and bundles
crdt's source inline), sync-server uses plain tsgo compilation and
keeps its deps external — so crdt must be built ahead of time and be
resolvable via node_modules at runtime.

Chain `yarn workspace @actual-app/crdt build` before tsgo so every
caller of sync-server's build (build:server, docker-edge, publish
workflows, direct invocations in CI) gets a complete crdt dist. Revert
tsgo -b back to plain tsgo since crdt's build step now emits both the
JS and the declarations.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] crdt: expose dist/ via conditional exports so Node can load it

The package's `exports` field pointed straight at `./src/index.ts`,
which works for TS tooling and bundlers (vite with noExternal, vitest)
but breaks at plain-Node runtime — Node can't execute `.ts` files and
resolves dependent `./crdt` as a directory import, failing with
ERR_UNSUPPORTED_DIR_IMPORT.

That was invisible before because sync-server pinned
`@actual-app/crdt@2.1.0` and ran against the published npm tarball
(whose `publishConfig.exports` had already been promoted to the main
`exports` by yarn pack). Switching sync-server to `workspace:*` made
the raw workspace exports win at runtime: the compiled server imported
crdt when desktop-electron forked it, Node hit the `.ts` entry, the
utility process crashed before emitting `server-started`, and the
onboarding flow stalled on "Configure your server".

Switch to the same conditional-exports pattern packages/api already
uses: types → dist/index.d.ts, development → src/index.ts (for vitest
runs that enable the `development` condition), default → dist/index.js
(Node runtime and any other consumer). `publishConfig.exports` still
collapses this to just types + default for the npm tarball.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] crdt: split exports per consumer (browser source, node dist)

Previous commit's conditional exports routed everything non-development
to ./dist/index.js. That broke the web build: rolldown runs with
conditions ['electron-renderer', 'module', 'browser', 'default'] — no
match for development, falls through to the dist entry, which isn't
built by bin/package-browser, and fails to resolve @actual-app/crdt
when bundling loot-core's server/undo.ts.

Split the entries so each consumer lands on the right artifact:

  types       → ./dist/index.d.ts   (TypeScript, project references)
  development → ./src/index.ts      (vitest — both configs include it)
  browser     → ./src/index.ts      (web rolldown bundles the source)
  node        → ./dist/index.js     (sync-server forked by Node at
                                     runtime — the failure that kicked
                                     off this whole saga)
  default     → ./src/index.ts      (fallback for bundlers like api's
                                     vite build with conditions=['api'])

Verified: node resolves to dist, yarn build:browser succeeds from a
clean crdt/, sync-server build produces both dist/index.js and
build/app.js, loot-core (552) + sync-server (386) tests pass, full
typecheck clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] address review feedback on crdt/sync-server

- generate-proto: add `set -euo pipefail` so a protoc failure exits the
  script non-zero instead of silently running oxfmt on whatever is in
  src/proto/ from the previous run.
- sync.proto SyncRequest: field numbers jumped from 3 to 5; declare
  `reserved 4;` so the slot can't be silently reused for a new field
  with an incompatible type. Regenerated sync_pb.ts — the reservation
  shows up in the encoded file descriptor.
- sync-simple.js: SQLite stores is_encrypted as a 0/1 integer and
  better-sqlite3 hands it back as a number, but the bufbuild
  MessageEnvelope schema types isEncrypted as bool. Coerce to boolean
  when constructing the envelope so the JS value matches the field
  type before toBinary runs.

Skipped the suggested `types` → ./src/index.ts swap in crdt's exports:
packages/api uses the same `types` → dist pattern and TypeScript's
bundler resolution already falls through when dist/*.d.ts doesn't yet
exist (verified — loot-core typecheck passes with packages/crdt/dist
removed).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] address review feedback on encoder/app-sync test

- encoder.ts: prefs.getPrefs().encryptKeyId is `string | undefined`
  (MetadataPrefs is a Partial<>). The bufbuild SyncRequestSchema's
  keyId field is a non-optional proto3 string. Current code worked by
  accident — passing undefined into `create(Schema, init)` falls back
  to the schema default '' — but relied on bufbuild's undef-handling
  and would break if someone dropped @ts-strict-ignore. Normalize to
  '' explicitly.
- app-sync.test.ts: add a short WHY comment next to
  `syncRequest.since = ''` in "returns 422 if since is not provided".
  The test's intent (missing since) only matches the handler's
  `requestPb.since || null` falsy-check because proto3 strips '' on
  the wire and decodes it back to ''. Not obvious without the comment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] crdt: load source directly in dev, only use dist when published

Local exports point at src/index.ts so consumers (sync-server in
particular) never load a stale Vite bundle. publishConfig keeps the
dist/ mapping for npm consumers. Switched the Vite output to ESM and
added "type": "module" so the published bundle stays consistent.

Sync-server's existing extension-resolution loader is extended to
handle directory imports and is now registered at runtime via
--import ./register-loader.mjs, matching how tests already load it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [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) <noreply@anthropic.com>

* [AI] sync-server: bootstrap entry that registers the loader for utilityProcess

Electron's utilityProcess.fork accepts execArgv but silently ignores
--import (verified with a minimal repro: the flag shows up in
process.execArgv but the preload module never executes), so the
previous attempt was a no-op and the embedded sync-server still
crashed on crdt's ESM directory imports. Add packages/sync-server/start.mjs
that statically imports register-loader.mjs and then dynamic-imports
build/app.js, so the loader is in place before the app's module graph
resolves. desktop-electron now points utilityProcess.fork at start.mjs
and drops the ineffective --import flag.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-12 17:55:52 +00:00
Nadir Miralimov
8263e58eb2 [AI] Replace payee and category autocomplete filter/sort with fzf fuzy search (#7261)
* [AI] feat(web): replace custom autocomplete ranking with fzf

Replace substring-based filter/sort in PayeeAutocomplete and
CategoryAutocomplete with fzf fuzzy search. Remove deprecated
autocompleteRanking utility.

Closes #7261

* Update #7261 release notes

Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7261

* Regenerate yarn.lock file

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7261

* Restore e2e snapshots

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #7261

---------

Co-authored-by: Nadir Miralimov <riid@pm.me>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-11 21:33:36 +00:00
dependabot[bot]
f3a9c1a02c Bump mermaid from 11.12.1 to 11.15.0 (#7801)
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.12.1 to 11.15.0.
- [Release notes](https://github.com/mermaid-js/mermaid/releases)
- [Commits](https://github.com/mermaid-js/mermaid/compare/mermaid@11.12.1...mermaid@11.15.0)

---
updated-dependencies:
- dependency-name: mermaid
  dependency-version: 11.15.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 21:10:29 +00:00
Matiss Janis Aboltins
a95c0ad9b0 [AI] sync server changes; crdt & et al (#7702)
* [AI] Load @actual-app/crdt from source in dev, only bundle for publish

@actual-app/crdt's local exports now point at src/index.ts so consumers
(sync-server, loot-core, desktop-client) never see a stale Vite bundle.
publishConfig keeps the dist/ mapping for npm consumers. crdt's
tsconfig switches to bundler module resolution to match the rest of
the workspace (no extensions in source imports).

Sync-server's existing extension-resolution loader is extended to also
handle directory-index imports (./crdt → ./crdt/index.ts), and the
standalone `start` / `start-monitor` scripts now invoke Node with
--import ./register-loader.mjs so the loader is in place before crdt's
source resolves.

Electron's utilityProcess.fork accepts execArgv but doesn't actually
preload --import modules, so a new packages/sync-server/start.mjs
bootstrap entry registers the loader imperatively and then dynamic-
imports build/app.js. desktop-electron's startSyncServer() points the
fork at start.mjs. sync-server's "files" array now ships start.mjs,
register-loader.mjs and loader.mjs so packaged Electron / npm
consumers actually receive them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add release notes for PR #7702

* [AI] Restructure sync-server to build with Vite

Replace the hand-rolled tsgo + add-import-extensions + copy-static-assets
+ runtime loader pipeline with a single Vite SSR build. Bundles every
entry (app, bin/actual-server, scripts/*) and inlines @actual-app/crdt
source so Node never has to resolve TS at runtime — the
MODULE_TYPELESS_PACKAGE_JSON warning that surfaced via crdt's source
exports is gone. Migrations and bank handlers move from readdir-based
dynamic imports to import.meta.glob; messages.sql becomes a ?raw import.

Drop loader.mjs, register-loader.mjs, start.mjs, and
bin/add-import-extensions.mjs. Electron's startSyncServer() forks
build/app.js directly. publishConfig.imports goes away (subpath imports
are resolved at build time and don't appear in the bundle).

In dev (start:server-dev) sync-server proxies to Vite, so loosen the CSP
to allow Vite's inline preamble script and HMR websocket — production
CSP is unchanged. desktop-client skips registerSW() in dev (and disables
vite-plugin-pwa's devOptions) so stale cached assets don't override
edits between page loads.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Address review feedback

- sync-server CSP: drop 'unsafe-eval' from the production script-src;
  the bundle has no genuine eval/new Function usage (only a defensive
  branch in setimmediate's polyfill that's never hit). Keep it on the
  dev branch where Vite's HMR runtime relies on it. Add a comment so
  it's obvious which branch needs it and why.
- bank-factory: widen the loader glob to ./banks/*_*.{ts,js} so
  TypeScript handlers are discovered too, mirroring migrations.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Restore 'unsafe-eval' in production CSP for Electron

The Electron app needs `'unsafe-eval'` at runtime, so revert the dev-only
restriction and keep `'unsafe-eval'` in both branches. Comment updated to
record the actual reason instead of marking it as removable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Revert bank-factory glob change

Widening the glob to ./banks/*_*.{ts,js} broke the desktop e2e tests in
CI even though every current handler is .js and the brace expansion
matches no .ts files locally. Reverting to ./banks/*_*.js — the change
had no behavioural benefit since there are no TS handlers, so the
nitpick isn't worth chasing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Strip CSP comment to restore identical state to 9513c1e16

The desktop e2e has been failing despite my prior commits being a strict
revert (only difference was a 2-line comment, which can't change runtime).
Removing even the comment so the branch matches 9513c1e16's relevant
files exactly, to isolate whether the failure is from the master merge
or from CI-environment drift.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Make rebuild-electron actually rebuild better-sqlite3

PR #7712 simplified rebuild-electron to just `electron-rebuild -f -o
better-sqlite3,bcrypt` from the repo root. Two problems with that:

  1. Without `-m`, electron-rebuild scans the root workspace's package.json
     for native deps. better-sqlite3 isn't a direct root dep — it lives
     under packages/sync-server/ — so the scan returns no candidates and
     the rebuild silently no-ops.
  2. Without --build-from-source, electron-rebuild defers to
     prebuild-install, which downloads a stale prebuilt binary keyed off
     better-sqlite3's package.json (ABI 127) instead of recompiling
     against Electron 39's bundled Node ABI 140. The download succeeds
     and "Rebuild Complete" prints, but the resulting `better_sqlite3.node`
     can't `dlopen` inside Electron's utility process — sync-server
     crashes immediately on db init, the renderer's startSyncServer IPC
     never resolves, and the e2e test hangs on "Configure your server".

Point -m at packages/desktop-electron (which transitively pulls in
better-sqlite3 and bcrypt via @actual-app/sync-server) and force a real
compile via --build-from-source. Verified locally: better-sqlite3
rebuilds to darwin-arm64-140 and the desktop e2e onboarding test passes
in 6s instead of hanging for 60s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Restore CSP unsafe-eval comment

Bring back the explanatory comment that was stripped diagnostically in
99682268c. Now that the desktop e2e regression is traced to
rebuild-electron and not to anything in this branch, we can keep the
documentation noting why 'unsafe-eval' is retained in both CSP branches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Restore bank-factory glob to ./banks/*_*.{ts,js}

Re-apply the glob widening originally added in 145868f9d. It was
reverted in 531b1a191 because the desktop e2e was failing — that
failure is now traced to the rebuild-electron breakage (fixed in
6e8ac0784), not to this glob. Mirroring migrations.ts so future TS
bank handlers are picked up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Fix applyAppUpdate hanging in dev mode

In dev mode browser-preload's updateSW was () => undefined, so
applyAppUpdate() — which calls updateSW() and then awaits a
deliberately never-resolving promise (waiting for the SW-driven page
reload) — hung the renderer instead of refreshing. In prod the page
is replaced by the new service worker, so the never-resolving await is
fine. The dev path now triggers a plain window.location.reload() so
the page reloads and the never-settling await is irrelevant, matching
prod's effective behaviour.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Revert rebuild-electron to master version

* Revert "[AI] Revert rebuild-electron to master version"

This reverts commit 4b6baab79f.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
2026-05-11 17:40:42 +00:00
Aurel Demiri
0fd510a1d4 Integrate Enable Banking as a bank sync provider (#7345)
* Integrate Enable Banking as bank sync provider

Rewrite Enable Banking modal to match GoCardless pattern

Resolve Enable Banking bugs and improve auth flow

* [AI] Address code review feedback for Enable Banking integration

Bug fixes:
- Fix double-negative for DBIT transaction amounts (e.g. '--25.99')
- Fix payeeName counterparty mapping (CRDT→debtor, DBIT→creditor)
- Add missing state validation in EnableBankingCallback and /auth_callback
- Fix stuck loading state in useEnableBankingStatus with try/catch/finally
- Make session-expiry error matching case-insensitive
- Prefer CLAV balance type for startingBalance in /transactions route
- Guard setTimeout in post/del/patch when timeout is null
- Distinguish abort from network failure in post() catch

Credential handling:
- Add validateCredentials() to validate before persisting secrets
- Refactor client to use enablebanking-configure instead of manual secret-set
- Distinguish null (loading) from false (not configured) in setup checks

Poll-auth robustness:
- Add unique waiter IDs to prevent superseded waiter cleanup race
- Always cache results in completedAuths for retry resilience
- Add client disconnect cleanup via res.on('close')
- Cancel poll when Enable Banking modal closes via AbortController
- Prevent concurrent poll controller race with local reference check

Code quality:
- Extract buildSessionResult() to deduplicate auth_callback/complete-auth
- Add enabled parameter to useEnableBankingStatus to skip unused requests
- Add re-entrancy guard on onJump, reset bank on country change
- Refetch bank list after Enable Banking setup completes
- Type enableBankingConfigure config, make state required in completeAuth
- Add AbortError→TIMED_OUT test, fix startAuth test assertion
- Add afterAll vi.unstubAllGlobals() for test cleanup
- Add explanatory comments for bank-per-account model and in-memory maps

* [AI] Fix missing patterns in Enable Banking integration

- Add SyncServerEnableBankingAccount to ExternalAccount union and
  getInstitutionName parameter type in SelectLinkedAccountsModal
- Use BankSyncProviders type in mobile BankSyncAccountsList instead of
  hardcoded union missing enableBanking
- Add getSecretsError handling to EnableBankingInitialiseModal for
  proper auth/permission error messages
- Replace hardcoded #666 color with theme.pageTextSubdued
- Wrap onConnectEnableBanking in try/catch with error notification and
  init modal re-open, matching SimpleFin/PluggyAI pattern
- Translate hardcoded error string in enablebanking.ts
- Add 60s timeout to downloadEnableBankingTransactions matching PluggyAI
- Revert out-of-scope changes to del()/patch() in post.ts
- Revert shared starting balance dedup logic back to master pattern

* Forward PSU headers to Enable Banking API

* Fix Enable Banking re-auth dispatch

* Respect ASPSP maximum_consent_validity when starting Enable Banking auth

* Fix missing types for module jws

* Add upcoming release notes

* Fix format

Expected "sign" (value-import) to come before "Algorithm"

* Fix code review findings on Enable Banking integration

* [AI] Disable Enable Banking button while status is loading

* typo

* [AI] Migrate enable-banking files to subpath imports

Update all enable-banking files to use # subpath imports and
@actual-app/core paths, matching the migration done in master.
Add #enablebanking entry to desktop-client package.json imports map.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [AI] Add #app-enablebanking subpath imports to sync-server package.json

Register enablebanking service, utils, and root entries in both
the imports and publishConfig.imports maps.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add jws to dependencies

* [AI] Harden Enable Banking OAuth callback handoff

Enforce exact OAuth state round-trip in the Enable Banking callback so
mismatched/missing state values no longer silently complete the flow.
Replace unsafe `as`/`!` assertions in the auth handoff with typed
locals so the callback path stays sound under strict TypeScript.

* [AI] Tighten Enable Banking type safety

Make the Enable Banking external-msg modal strict-ts compatible,
annotate the id type in linkEnableBankingAccount, derive
AccountSyncSource from a single SYNC_PROVIDERS list, and annotate the
return type of getJWTBody. No behaviour change.

* [AI] Fix Enable Banking poll lifecycle and abort handling

Make the popup-driven auth poll cancellable and isolated:

- Allow the popup retry path to abort the in-flight poll instead of
  leaving it hanging on the previous attempt.
- Clear the Enable Banking stateRef when the retry attempt finishes so
  a new attempt starts from a clean state.
- Start useEnableBankingStatus in loading state until the first fetch
  resolves so the UI doesn't briefly flash "not connected".
- Cancel only the requested poll, not every in-flight Enable Banking
  poll, so unrelated link attempts aren't affected.
- Skip writing the poll response when the client has already
  disconnected, with a regression test covering the disconnect path.

* [AI] Tighten Enable Banking client/test plumbing

Misc code-quality improvements with no behaviour change:

- Parallelize Enable Banking secret reset calls so wiping multiple
  secrets doesn't serialize the request chain.
- Use absolute imports in the enable-banking client module to match the
  rest of desktop-client.
- Document externalSignal usage in the post helper.
- Tighten Enable Banking test fixtures with `satisfies` and dynamic
  dates so they stop drifting when the real "now" moves.

* [AI] Fix Enable Banking initial-balance and post-link bookkeeping

Apply the standard post-sync bookkeeping when linking an Enable Banking
account so the new account picks up the same starting-balance
treatment as other bank-sync providers, and skip pending transactions
when computing the initial balance so the figure isn't inflated by
transactions that haven't cleared yet.

* [AI] Refine Enable Banking error model and bank-sync surface

Carry the human-readable Enable Banking message in
EnableBankingError.error_type and the machine-friendly identifier in
error_code, then map error_code to a bank-sync category in the
/transactions wire format so AccountSyncCheck can match on the same
categories as other providers.

* [AI] Improve Enable Banking bank-sync field mapping

Bring the Enable Banking transaction normalizer in line with how other
bank-sync providers feed the field mapper:

- Strip SEPA structured prefixes from remittance text so notes/payee
  display the human-meaningful portion instead of the SEPA boilerplate.
- Return the notes field and spread the raw transaction so downstream
  field mapping can reach the full payload.
- Expose Enable Banking raw fields in the bank-sync field mapper UI so
  users can map any underlying property, not just the curated subset.

* [AI] Use req.ip for Enable Banking PSU header so trust-proxy whitelist applies

* [AI] Address Enable Banking CodeRabbit pass-3 follow-ups

Three small fixes from the latest CodeRabbit re-review:

- Guard the aspsps fetch in EnableBankingExternalMsgModal against stale
  responses. Switching countries quickly could let an earlier in-flight
  request overwrite the newer selection's bank list. Added a cleanup
  flag in the useEffect so only the latest response updates state.
- Clear `enablebanking_auth_state` from localStorage when the auth flow
  exits, but only if the stored value still matches this attempt's
  state, so a concurrent retry can't wipe a newer session. Wrapping
  the poll in try/finally covers every return path (success, timeout,
  abort, body-level error).
- Use `Boolean(trans.booked)` in the Enable Banking initial-balance
  predicate to match `normalizeBankSyncTransactions`. The Enable
  Banking normalizer always sets `booked` to a boolean today, so this
  is defensive rather than a live bug, but keeping the two predicates
  aligned avoids surprises if the upstream shape ever loosens.

* [AI] Address Enable Banking CodeRabbit pass-3 follow-ups (round 2)

Two more findings from the latest CodeRabbit pass:

- Guard onJump against stale-retry completions. Token each call with a
  monotonic jumpIdRef counter and gate every post-await write
  (setError/setWaiting after onMoveExternal, the second setWaiting,
  and the finally-block ref reset) on `myJumpId === jumpIdRef.current`.
  Without this, a retry click while the previous poll was still
  unwinding could surface the older call's error in the newer
  attempt's UI and clear stateRef/isJumpingRef out from under it,
  leaving the new poll un-cancellable.
- Translate the (beta) suffix on Enable Banking ASPSP names so
  non-English locales don't surface a hardcoded English token in the
  bank list. The existing `actual/no-untranslated-strings` rule misses
  this case (regex requires a leading uppercase, and template-literal
  interpolations aren't visited as standalone strings).

* [AI] Use SEPA prefix allowlist instead of catch-all regex

The previous `^[A-Z]{3,}\+` regex would incorrectly strip merchant
tokens like `BMW+`, `USB+`, or `COVID+` from the start of a remittance
line. Replaced it with an explicit allowlist of known SEPA / ISO 20022
prefixes and added a regression test covering the false-positive case.

* [AI] Use uuidv4 instead of crypto.randomUUID in Enable Banking

Aligns with master's revert in #7734 (crypto.randomUUID back to uuid
library). Two stray spots remained in Enable Banking code: the
link-account flow in loot-core/server/accounts/app.ts and the OAuth
state token in sync-server/app-enablebanking.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-11 14:05:30 +00:00
dependabot[bot]
b63f5dd303 Bump fast-uri from 3.1.0 to 3.1.2 (#7762)
Bumps [fast-uri](https://github.com/fastify/fast-uri) from 3.1.0 to 3.1.2.
- [Release notes](https://github.com/fastify/fast-uri/releases)
- [Commits](https://github.com/fastify/fast-uri/compare/v3.1.0...v3.1.2)

---
updated-dependencies:
- dependency-name: fast-uri
  dependency-version: 3.1.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 15:05:29 +00:00
dependabot[bot]
35a01b0fa6 Bump @babel/plugin-transform-modules-systemjs from 7.28.5 to 7.29.4 (#7776)
Bumps [@babel/plugin-transform-modules-systemjs](https://github.com/babel/babel/tree/HEAD/packages/babel-plugin-transform-modules-systemjs) from 7.28.5 to 7.29.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.29.4/packages/babel-plugin-transform-modules-systemjs)

---
updated-dependencies:
- dependency-name: "@babel/plugin-transform-modules-systemjs"
  dependency-version: 7.29.4
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-09 15:03:49 +00:00
dependabot[bot]
db38565524 Bump uuid from 13.0.2 to 14.0.0 (#7739)
* Bump uuid from 13.0.2 to 14.0.0

Bumps [uuid](https://github.com/uuidjs/uuid) from 13.0.2 to 14.0.0.
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v13.0.2...v14.0.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 14.0.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* [AI] Add engines field to sync-server package.json for Node.js >=22 requirement

Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
2026-05-08 20:40:07 +00:00
Matiss Janis Aboltins
852b95524b [AI] Revert crypto.randomUUID back to uuid library (#7734)
* [AI] Revert crypto.randomUUID back to uuid library

Partial revert of #7529. Restores the `uuid` package dependency in
api, crdt, desktop-client, loot-core, and sync-server, swapping every
`crypto.randomUUID()` call introduced by that PR back to `uuidv4()`
(and the `uuid()` alias in RuleEditor.tsx and ruleUtils.ts where it
was previously used). The lint rule, docs entry, and `vi.mock('uuid')`
test setup are restored as well. The `fs-extra` removals in
desktop-electron from the same PR are left in place.

https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw

* Add release notes for PR #7734

* [check-spelling] Update metadata

Update for https://github.com/actualbudget/actual/actions/runs/25480733101/attempts/1
Accepted in https://github.com/actualbudget/actual/pull/7734#issuecomment-4394811498

Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
on-behalf-of: @check-spelling <check-spelling-bot@check-spelling.dev>

* [AI] Use the uuidv4 alias in RuleEditor and ruleUtils

The previous commit preserved the `v4 as uuid` alias in these two files
to match their pre-#7529 state, but the project convention (and lint
rule message) is `v4 as uuidv4`. CodeRabbit flagged the inconsistency,
so normalize the alias and call sites in both files.

https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw

* [AI] Pin uuid to ^11.1.0 to fix Electron e2e

uuid v13 is ESM-only (no CJS entry, only an exports map with `node` and
`default` conditions both pointing to ESM files). The Electron backend is
bundled as CJS by `loot-core/vite.desktop.config.mts` and loaded via a
dynamic `await import(process.env.lootCoreScript!)` in the
desktop-electron utilityProcess; that pipeline appears to fall over on
the ESM-to-CJS transform of uuid v13 in Vite 8 / rolldown, which makes
the Functional Desktop App e2e job fail consistently while every other
check (web e2e, VRT, unit tests, lint, typecheck) passes.

uuid v11 still ships `dist/cjs/index.js`, so pinning each workspace's
uuid range to ^11.1.0 sidesteps the resolution path entirely. The API
is unchanged (`v4` is still the same export), so no source-code changes
are needed.

https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw

* [AI] Capture Electron stdout/stderr in desktop e2e fixture (TEMP DEBUG)

Pipe the Electron main+utility process stdout/stderr into both the
playwright runner stderr and an electron.log file inside the test's
output directory. This makes the actual backend error visible in the
Functional Desktop App job output and in the desktop-app-test-results
artifact.

Will be reverted once the failure cause is identified.

https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw

* Revert "[AI] Capture Electron stdout/stderr in desktop e2e fixture (TEMP DEBUG)"

This reverts commit 4cb5148859.

* [AI] Fix rebuild-electron and bump uuid back to ^13.0.0

The Functional Desktop App e2e job had been failing with
ERR_DLOPEN_FAILED on better-sqlite3 (NODE_MODULE_VERSION 127 vs 140),
which surfaced because this PR's yarn.lock change invalidated the
GitHub Actions node_modules cache. With a fresh install,
rebuild-electron is responsible for compiling better-sqlite3 and
bcrypt against Electron's ABI — but since #7712 the script has been
running from the repo root, where neither module is a direct
dependency, so electron-rebuild silently exits as a no-op.

Restore the `-m ./packages/desktop-electron` scoping that #7712
removed, so the rebuild actually finds and rebuilds the modules.
This fix is technically out of scope for this PR but it blocks any
PR that touches yarn.lock.

Also revert the `^11.1.0` pin from the previous commit back to
`^13.0.0`. The pin was based on a wrong hypothesis (that uuid v13's
ESM-only packaging was breaking the bundle); now that the real
cause is identified, there is no reason to deviate from the
pre-#7529 version.

https://claude.ai/code/session_01KTg1g416Jdjf5feGke8MQw

---------

Signed-off-by: check-spelling-bot <check-spelling-bot@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <MatissJanis@users.noreply.github.com>
2026-05-07 19:18:47 +00:00
Matiss Janis Aboltins
ff0f5bdb35 [AI] lage - move browser build to using lage (#7602)
* Simplify desktop client browser build

* [AI] Move browser build orchestration into vite config and lage

Moves loot-core worker build, public/ staging (migrations, default-db,
sql-wasm, data-file-index), and build-stats wiring from the deleted
packages/desktop-client/bin/build-browser shell script into a
lootCoreBackend vite plugin in packages/desktop-client/vite.config.mts.

Adds a build:browser target to lage.config.js so bin/package-browser
runs as a single `lage build:browser --to=@actual-app/web` call, with
crdt + loot-core built via lage's ^build dependency before the
desktop-client build.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Refactor e2e-test workflow and update desktop-client configurations

* [AI] Move plugins-service staging into desktop-client vite config

Declares plugins-service as a workspace devDependency of @actual-app/web
so lage's ^build edge picks it up automatically in the build:browser
pipeline, and moves the cross-package file staging (production copy +
dev serving) into vite.config.mts, mirroring the lootCoreBackend
pattern. Drops the plugins-service shell wrapper script and simplifies
its package.json scripts to invoke vite build directly. Updates root
start:browser to run plugins-service watch in parallel with the dev
server instead of pre-building once.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Sync tsconfig project references for plugins-service edge

Follow-up to the plugins-service workspace edge: adds the
../plugins-service project reference in packages/desktop-client/tsconfig.json
via yarn sync:tsconfig-references.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Release notes

* [AI] Ignore .venv/ so lage's git hasher skips Electron CI's Python venv

Electron CI provisions a Python virtualenv at the repo root for
setuptools. With browser builds now routed through lage, lage's
git hash-object pass walks untracked-not-ignored files and fails on
the venv's broken lib64 symlink ("fatal: Unable to hash .../.venv/lib64").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Bake Weblate translations back into VRT/e2e bundle

build-web set download-translations: false and relied on bin/package-browser's
ad-hoc git clone + git pull. That path is fragile inside the playwright
container, so vite's import.meta.glob('/locale/*.json') frequently produced an
empty languages map and the bundle shipped with no en.json. VRTs then rendered
source-code English and diffed against snapshots authored from Weblate strings.

Route translation provisioning back through actions/checkout (download-translations: true)
in build-web and vrt-update-generate, and add --skip-translations to bin/package-browser
(mirroring bin/package-electron) so the in-script git pull is bypassed when CI
has already staged the locale dir.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Skip translation cloning in build-web bundle for VRT determinism

bin/package-browser used to unconditionally clone actualbudget/translations
before vite ran, baking Weblate en.json into the build artifact. With the
e2e-test pipeline now serving that artifact via serve-build.mjs, VRT
screenshots ended up rendering Weblate strings — drifting from the snapshots,
which were authored against source-code English (master VRTs ran on vite dev
without a locale dir).

Pass --skip-translations to bin/package-browser from build-web so the bundle
ships with no locale chunks. download-translations stays 'false' across the
e2e-test and vrt-update-generate workflows, matching the prior behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 20:20:13 +00:00
Matiss Janis Aboltins
9acbd6388b [AI] Cache the CLI's local budget between invocations (#7539)
* [AI] Cache the CLI's local budget between invocations

Every `actual <cmd>` call currently delta-syncs the budget with the
sync server via `api.downloadBudget`, which hits the server's
500-req/min rate limit on scripted workflows. Actual is local-first:
once the budget is on disk, most read commands do not need fresh
server data.

Introduce a CLI-only cache layer inside `withConnection` that
decides per invocation whether to skip, sync, or re-download:

- Cache state lives at `{dataDir}/.actual-cli/{syncId}/state.json`,
  keyed by `syncId` to avoid the chicken-and-egg of not knowing the
  on-disk `budgetId` before the first download. The on-disk id is
  resolved via `api.getBudgets()` and persisted after first download.
- Read commands (list, balance, query run, …) skip the `/sync`
  call while `now - lastSyncedAt < cacheTtl`. Write commands
  (create, update, delete, set-*, etc.) sync before and after the
  operation to keep server state consistent.
- Encrypted budgets force a sync per call since `api/load-budget`
  does not re-verify the password.
- New `proper-lockfile`-backed shared/exclusive lock serializes
  writes while allowing parallel reads. Reader markers live in
  `{meta}/readers/`; writers sweep stale markers by PID.

New `actual sync` command with three modes: default (sync now),
`--status` (print cache age, TTL, stale flag), `--clear` (delete
cache, holding the exclusive lock to avoid racing writers).

New config surface, following the existing flag → env → config file
→ default precedence chain:

- `--cache-ttl <s>` / `ACTUAL_CACHE_TTL` / `cacheTtl` (default 60)
- `--refresh` / `--no-cache`
- `--lock-timeout <s>` / `ACTUAL_LOCK_TIMEOUT` / `lockTimeout` (10)
- `--no-lock` / `ACTUAL_NO_LOCK` / `noLock`

Every `withConnection` call site now passes an explicit
`{ mutates: boolean, skipBudget?: boolean }` so read/write intent is
visible at the edge.

The old `budgets sync` subcommand is removed — it silently diverged
from the new top-level `actual sync`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Simplify CLI cache/lock internals

Use a discriminated SyncDecision union so connection.ts no longer needs
non-null assertions on the cached state. Thread the resolved CliConfig
through withConnection's callback to drop duplicate resolveConfig calls
in the sync and budgets commands. Extract an errorCode helper and
replace the existsSync+readdirSync TOCTOU pattern in the reader-wait
polling loop with a single readdir that tolerates ENOENT.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [AI] Address PR #7539 review comments

- cache.ts: use a unique per-writer tmp filename so concurrent shared-
  lock writers (encrypted budgets, --refresh, stale TTL) don't clobber
  each other's publish and silently drop state updates.

- index.ts/config.ts: fix --no-cache and --no-lock flags. Commander
  stores --no-foo under the positive key (cache/lock) and an explicit
  false default makes the flag a no-op; the previous code also read
  the wrong keys (noCache/noLock) so the flags had no effect at all.
  Derive refresh/noLock from the correct keys.

- budgets.ts: invert encryption password precedence so the subcommand
  flag (--encryption-password) wins over env/config-file values.

- sync.ts: report stale=true in --status when lastSyncedAt is in the
  future, matching decideSyncAction's clock-skew handling.

- connection.ts: drop unnecessary `as` cast on api.getBudgets() now
  that the return type is Promise<APIFileEntity[]>.

- utils.ts: parseBoolEnv throws on unrecognized values instead of
  silently returning undefined so typos like ACTUAL_NO_LOCK=yes fail
  loudly.

- Shorten 7539 release note to a single user-facing sentence.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 21:58:18 +00:00
dependabot[bot]
4f1bc3fcdd Bump postcss from 8.5.8 to 8.5.10 (#7613)
* Bump postcss from 8.5.8 to 8.5.10

Bumps [postcss](https://github.com/postcss/postcss) from 8.5.8 to 8.5.10.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.8...8.5.10)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.10
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* Add release notes for postcss version bump

Updated postcss version for maintenance.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Julian Dominguez-Schatz <julian.dominguezschatz@gmail.com>
2026-04-25 00:23:54 +00:00
dependabot[bot]
c8224d24be Bump @xmldom/xmldom from 0.8.12 to 0.8.13 (#7596)
Bumps [@xmldom/xmldom](https://github.com/xmldom/xmldom) from 0.8.12 to 0.8.13.
- [Release notes](https://github.com/xmldom/xmldom/releases)
- [Changelog](https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md)
- [Commits](https://github.com/xmldom/xmldom/compare/0.8.12...0.8.13)

---
updated-dependencies:
- dependency-name: "@xmldom/xmldom"
  dependency-version: 0.8.13
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-23 20:06:30 +00:00
dependabot[bot]
bd1da27404 Bump dompurify from 3.3.2 to 3.4.1 (#7591)
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.3.2 to 3.4.1.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.3.2...3.4.1)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-version: 3.4.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
2026-04-22 20:51:12 +00:00