whisper-money/resources
Víctor Falcón a38dc5dd95
fix(sync): don't crash when IndexedDB is unavailable (PHP-LARAVEL-43) (#654)
## Problem (Sentry PHP-LARAVEL-43)

`ReferenceError: Can't find variable: indexedDB` — an unhandled promise
rejection reported from production. Some browsers do not expose the
global `indexedDB` at all (Chrome on iOS in certain webviews,
private/locked-down modes). The app uses Dexie (IndexedDB) as an offline
cache for transactions, and any Dexie operation in that context throws.

Reproduced entry point: on `/register`, submitting the form runs
`transactionSyncService.clearAll()` → `db.transactions.clear()` → Dexie
references the missing global → throws. The same class of crash can fire
from every other cache touch point (`sync`, `getAll`, `getById`,
`getByAccountId`, and the cache eviction in `delete`).

IndexedDB here is only a cache — the API/Inertia is the source of truth
— so a missing store must degrade to "no local cache", never crash.

## Fix (3 commits)

1. **Guard helper** — `isIndexedDbAvailable()` (checked via `globalThis`
so referencing the global never throws) and `withDb(operation,
fallback)` in `dexie-db.ts`. `withDb` returns the fallback when
IndexedDB is missing or the op fails, and defers `db` access into a
closure so the lazy Dexie proxy never initializes on unsupported
browsers.
2. **Sync manager** — every Dexie read/write goes through `withDb`
(reads → empty, writes/`clearAll` → no-op); `sync()` early-returns a
skipped result and avoids the pointless server round-trip.
3. **Delete eviction** — only the best-effort local cache eviction in
`delete()` is guarded; the authoritative `axios.delete` stays outside
the guard so no API mutation is ever swallowed.

## Safety / complexity

- **No behavior change for browsers with IndexedDB** — `withDb` runs the
operation exactly as before (covered by tests).
- **Graceful degradation otherwise** — no crash; the app runs
online-only.
- **No swallowed mutations** — the guard wraps only Dexie calls; all
`axios` calls stay outside it.
- Reviewed by two agents: the crash does not actually block registration
(Inertia does not await `onBefore`), so this is a recurring unhandled
rejection rather than a hard block, and the fix has no user-facing
downside. Low complexity, high confidence → auto-merge.

## Testing

- New `resources/js/lib/sync-manager.test.ts` and
`resources/js/services/transaction-sync.test.ts` (7 tests): IndexedDB
missing → clearAll/sync/reads/delete-eviction no-op and never throw;
IndexedDB present → unchanged behavior.
- `bun run lint` / `bun run format` clean on the changed files. (The
repo's `tsc --noEmit` has pre-existing errors that CI intentionally does
not gate on; no new ones introduced.)

Fixes PHP-LARAVEL-43

---
🤖 Opened by the autonomous Sentry-triage loop. Auto-merge enabled:
additive guard, no behavior change for supported browsers, no data-loss
surface.
2026-07-07 04:59:10 +00:00
..
css fix(charts): improve contrast for chart colors 9-10 (#614) 2026-06-30 10:28:03 +00:00
js fix(sync): don't crash when IndexedDB is unavailable (PHP-LARAVEL-43) (#654) 2026-07-07 04:59:10 +00:00
views feat(encryption): commands to warn and remove inactive encrypted-data accounts (#633) 2026-07-03 15:57:04 +00:00