fix(server-storage): replay edits behind travelling writes in order, hold writes after an offline start

- An edit queued behind a travelling write keeps its own before → after
  and carries the travelling write's edit as `prior`, also in the saved
  writes. A conflict replays both in order, so adding and then removing a
  favorite removes it whether or not the first write landed, and a lost
  write is not dropped.
- Page-hide writes now report their new tag or a 412 like normal writes:
  a confirmed one rebases the next edit, a conflict is replayed.
- After an offline start from a remembered instance, writes are held and
  the retry only checks /instance before reloading, so saved writes never
  reach another database behind the same address.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Opus 5.5 committed 2026-10-12 09:31:45 +02:00
1 parent a8b6b86e43
commit 487c98ab96
11 files changed
+383 -82

No files matched your search

+13 -6
View File
@@ -182,7 +182,9 @@ instance under `iptvnator-server:instance`. When the probe gets no answer
(network error, timeout, or a 5xx such as nginx's 502 while the backend
restarts), a browser that remembers this backend still starts in server
mode, offline, and reloads once the server answers; it never falls back to
an empty browser library. An answer without server storage, or a 404 from
an empty browser library. Until that reload it sends no writes
(`holdWrites`): the address may lead to another database by then, which
the reload's probe finds out. An answer without server storage, or a 404 from
an older backend, forgets it (`server-storage-start.ts`). Everything else of the
client lives behind the `@iptvnator/services/server-storage` sub-entry, so
other builds never load it; `ServerStorageStatusService` and the token
@@ -215,11 +217,16 @@ settings and guards read them in every build.
as offline. Each write is sent on the tag of the entry it was made
against (`UserDataWriteQueue`); after a `412` the edit (before → after) is
replayed on the server's current value, so another device's change to the
same playlist's entry is kept. An entry a re-read keeps from the cache
gets no tag, so its next write conflicts and is replayed too. A page-hide
write behind a travelling one is taken from the queue only when that one
is answered. While a playlist is being deleted its edits wait; the
delete drops them only when it succeeds. Edits made before the first successful read
same playlist's entry is kept. An edit queued behind a travelling write
keeps its own before → after and carries the travelling one as `prior`
(also in the saved writes): if that write was lost, a conflict replays
both in order, and if it landed, adding and then removing an item still
removes it. An entry a re-read keeps from the cache gets no tag, so its
next write conflicts and is replayed too. Page-hide writes report their
new tag or conflict like any other, and one behind a travelling write is
taken from the queue only when that one is answered. While a playlist is
being deleted its edits wait; the delete drops them only when it
succeeds. Edits made before the first successful read
are held as before → after and applied to the server's value once it
arrives (`applyEdit`): a new device or a stale cache adds its favorite
instead of replacing the server's list. They are saved in the tab too