Blob cache previously leaked forever: clearTripData omitted it, entries had
no trip discriminator, and there was no size/count bound, so file blobs
survived trip eviction and could starve the map-tile cache for quota.
- BlobCacheEntry gains tripId + bytes; Dexie v3 adds a tripId index with a
backfill upgrade (legacy rows -> tripId -1, bytes from blob.size)
- clearTripData purges the trip's blobs in-transaction
- enforceBlobBudget() evicts oldest-by-cachedAt past 200 entries / 100 MB
- tripSyncManager threads tripId/bytes into puts and enforces the budget
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.
* fix(maps): fall back to OSM/Wikipedia for place photos and normalize non-standard language codes (#1137)
* fix(auth): refuse password reset for OIDC/SSO-linked accounts (#1129)
* fix(docker): ship server/assets (airports + atlas geo) in the runtime image (#1133, #1119)
* fix(unraid): point the template at a PNG icon Unraid can render (#1073)
* fix(offline): serve cached file blobs when offline or on network failure (#1046, #1069)
* fix(map): centre the selected pin in the visible map area above the bottom panel (#1125)
* fix(pdf): render persisted place-photo proxy URLs as images (#1130)
* fix(planner): show the selected place category in the edit form (#1134)
* fix(dashboard): collapse list-view trip cards to a compact row on mobile (#1132)