Closes BLOCKER B4 — three reinforcing paths could serve one account's
cached data to the next user on a shared device:
- The Workbox 'api-data' cache keyed trip/user-scoped GETs by URL only
(cookie-blind). Changed to NetworkOnly; offline reads come from the
per-user IndexedDB cache via the repo layer instead.
- IndexedDB had no per-user scoping. The Dexie connection is now scoped
per user (trek-offline-u<id>) behind a Proxy so the ~19 importers keep a
stable binding; login opens the user DB, logout deletes it and returns
to the anonymous DB.
- logout() was fire-and-forget and racy: background flush/syncAll could
re-seed the DB after the wipe. It is now async and ordered — close an
auth gate, unregister sync triggers, disconnect, clear caches, delete
the user DB — and flush()/syncAll() bail when the gate is closed.
Closes three offline BLOCKERs from the PWA audit:
- B1: offline edits/deletes of an offline-created entity were lost. The
negative temp id was baked into the PUT/DELETE url and never rewritten
after the CREATE returned a real id, so dependents 404'd and were dropped.
Dependents now carry a {id} placeholder + tempEntityId; flush builds a
tempId->realId map and durably rewrites still-queued dependents on CREATE
success (survives flush boundaries / reloads).
- B2: tempId = -(Date.now()) collided within a millisecond, overwriting an
optimistic row. Replaced with a monotonic nextTempId() minter.
- B3: any 4xx marked the mutation failed with no rollback and no signal, and
the badge ignored failed rows. Terminal failures now roll back the phantom
optimistic CREATE; 401/408/425/429 are treated as retryable; failedCount()
is surfaced in OfflineBanner (red pill) and OfflineTab.