Skip to content

fix: end-of-feed and all-type search semantics - #72

Merged
aeharding merged 2 commits into
mainfrom
fix-pagination-search-semantics
Jul 25, 2026
Merged

fix: end-of-feed and all-type search semantics#72
aeharding merged 2 commits into
mainfrom
fix-pagination-search-semantics

Conversation

@aeharding

Copy link
Copy Markdown
Owner

Two places the canonical API misreported reality on the page-number providers (Lemmy v0, PieFed) — both found while building the fakes' pagination/search, both verified against live servers.

next_page could never mean "there is more"

toPageResponse computed (page_cursor ?? 1) + 1 unconditionally, so a consumer looping while (next_page) would never terminate on v0/PieFed; paging could only be stopped by noticing a short page. It's now derived:

  1. the server's own cursor when the endpoint returns one — PieFed's post/list, comment/list, community/list, user/replies, user/mentions all do, and we were throwing it away;
  2. otherwise inferred from a full page (a short page is the last one);
  3. and when there's no limit to compare against, the old assumption is kept rather than guessing.

Absent now means end-of-feed on every provider, which is what the canonical type always implied. The matrix test's provider-specific carve-out for this is gone.

All-type search 400'd against real PieFed

PieFed's search requires type_ and its enum has no All, so search({ type_: "all" }) — and a search with no type_ — failed in production:

GET /api/alpha/search?q=news&type_=All
400 {"message":"Validation failed","status":"{'query': {'type_': ['Must be one of: Communities, Posts, Users, Url, Comments.']}}"}
GET /api/alpha/search?q=news
400 {"message":"Validation failed","status":"{'query': {'type_': ['Missing data for required field.']}}"}

The adapter now fans out across the four concrete types and merges, taking only each response's requested bucket so a fan-out can't double-count. The canonical→PieFed search-type map is explicit instead of borrowed from lemmyv0's (whose All was slipping past the query spread's widened typing behind a @ts-expect-error).

Verification

447 unit tests; matrix coverage for both fixes on both fakes; live smoke gains an all-type search scenario — the exact call that used to 400 — now passing against lemmy.world (v0), ds9.lemmy.ml (v1) and piefed.social. Live suites 13/13 smoke + 9/9 fidelity.

Two ways the canonical API misreported reality on the page-number
providers (Lemmy v0, PieFed):

next_page always had a value, so it could never mean 'there is more' —
a consumer looping while (next_page) would spin forever, and paging had
to be stopped by noticing a short page instead. It's now derived: the
server's own cursor where the endpoint returns one (PieFed's list
endpoints do), otherwise inferred from a full page. Absent means end of
feed on every provider now.

search({ type_: 'all' }) — and a search with no type_ at all — 400'd
against real PieFed, whose search requires a concrete type_ and has no
'All' in its enum (verified live). The adapter now fans out across the
four concrete types and merges, taking only each response's requested
bucket so nothing double-counts. The canonical-to-PieFed search type map
is now explicit rather than borrowed from lemmyv0, so an unsupported
value can't slip through the query spread's widened typing again.

Covered by a matrix test (uniform end-of-feed + all-type search across
both fakes) and a live smoke scenario that exercises the previously
failing call against real instances.
Review catch, confirmed live: these servers apply the limit per bucket,
so every endpoint that merges requests (modlog's 14 buckets,
notifications, person content, all-type search) returns MORE than the
limit on page one. Comparing with === called that the last page, ending
those feeds after a single page — Voyager's modlog, modqueue, inbox,
profile, saved and voted feeds, plus large comment threads.

>= is the correct rule, not a patch: if any bucket came back full the
merged page is at least the limit, and a merged page shorter than the
limit means every bucket was exhausted.

Also: an unusable server cursor (NaN) now reports end-of-feed instead of
being sent back to the server, and piefed's merged person content ORs
the two sub-cursors it already had rather than inferring from the merged
length (piefed silently clamps large limits).

test/toPageResponse.test.ts pins all of these rules directly — the fakes
don't model lemmyv0, so none of it was reachable from the matrix.
@aeharding
aeharding merged commit a171103 into main Jul 25, 2026
1 check passed
@aeharding
aeharding deleted the fix-pagination-search-semantics branch July 25, 2026 17:58
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