Skip to content

fix: normalize EPUB progression semantics#665

Merged
everpcpc merged 5 commits intomainfrom
fix/offline-progress-ignore-nonretryable
Mar 14, 2026
Merged

fix: normalize EPUB progression semantics#665
everpcpc merged 5 commits intomainfrom
fix/offline-progress-ignore-nonretryable

Conversation

@everpcpc
Copy link
Copy Markdown
Owner

@everpcpc everpcpc commented Mar 14, 2026

Problem

The offline EPUB progression path was mixing protocol details, response quirks, and local persistence semantics into the same control flow.

That showed up as a chain of brittle behaviors:

  • the progression endpoint could be requested with the wrong Accept header, causing 406 Not Acceptable
  • 204, 404, empty body, and locator:{} all meant "no usable remote progression", but were handled inconsistently
  • non-retryable protocol/payload failures were not clearly separated from transient retryable failures
  • local storage overloaded epubProgressionRaw == nil to mean both "unknown" and "checked but missing"

The result was a startup sync path that kept accreting special cases and could continue retrying EPUB progression imports even after the app had already learned there was no usable remote progression to persist.

Approach

Refactor the flow around explicit remote and local progression semantics.

Remote progression semantics

Centralize remote EPUB progression interpretation in BookService and normalize the endpoint into four outcomes:

  • available(R2Progression)
  • missing
  • retryableFailure
  • invalidPayload

This layer now also sends the correct header:

  • Accept: application/vnd.readium.progression+json

And it treats these cases consistently as missing:

  • 204
  • 404
  • empty response body
  • locator: {}
  • decoded payloads with an empty locator

Only transient failures remain retryable (network, offline, 429, 5xx). Non-retryable protocol/payload problems are surfaced as invalidPayload.

Local progression semantics

Keep using epubProgressionRaw, but store an explicit record instead of relying on implicit sentinel meanings.

That preserves three local states without a schema migration:

  • unknownepubProgressionRaw == nil
  • missing → explicit stored missing record
  • available → explicit stored progression record

Legacy values are normalized on read as well, including older empty sentinels, raw R2Progression payloads, and empty-locator payloads.

Consumers

Update the startup/offline import flow to consume those explicit semantics directly:

  • persist available
  • persist missing
  • persist invalidPayload as handled-missing so it does not retry forever
  • leave only retryableFailure eligible for future retries

This keeps protocol interpretation in BookService, while OfflineManager and the reader-side import path only apply business policy.

Scope

  • fix the EPUB progression endpoint Accept header
  • centralize remote EPUB progression result interpretation in BookService
  • normalize 204 / 404 / empty body / empty locator cases as missing progression
  • make local EPUB progression state explicit while keeping backward compatibility with existing stored values
  • simplify startup sync and reader-side remote progression import to consume the normalized result

Validation

  • make format
  • make build-macos-ci

@everpcpc everpcpc changed the title fix: ignore non-retryable offline progress sync errors fix: avoid retrying missing offline epub progression sync Mar 14, 2026
@everpcpc everpcpc changed the title fix: avoid retrying missing offline epub progression sync fix: normalize offline EPUB progression sync semantics Mar 14, 2026
@everpcpc everpcpc changed the title fix: normalize offline EPUB progression sync semantics fix: normalize EPUB progression semantics Mar 14, 2026
@everpcpc everpcpc merged commit fcaa3a0 into main Mar 14, 2026
3 checks passed
@everpcpc everpcpc deleted the fix/offline-progress-ignore-nonretryable branch March 14, 2026 14:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant