Skip to content

refactor(find): FF series URLs resolve through the resolver, not ff-marketplace→Raster - #100

Merged
moskovich merged 3 commits into
mainfrom
refactor/ff-series-single-resolution
Jul 28, 2026
Merged

refactor(find): FF series URLs resolve through the resolver, not ff-marketplace→Raster#100
moskovich merged 3 commits into
mainfrom
refactor/ff-series-single-resolution

Conversation

@moskovich

Copy link
Copy Markdown
Contributor

Stacked on #98 (merge that first; this branch contains its commit). Closes the "noted, out of scope" item from #98.

Problem

find on a Feral File series URL resolved one token via ff-marketplace, then consulted Raster, which expands a token to its parent artwork. A single-edition series page — 36 Points Display Edition — therefore built the sibling collection's 35 tokens, plus minutes of FF-indexer warming for tokens the user never pointed at.

Change

Series URLs route through the source-resolver's Feral File site module (/api/series/{slug}/api/artworks?seriesID=), exactly as show URLs already did. One FF resolution path for page-shaped URLs; find builds precisely the tokens the page describes. Artwork URLs keep the coords→Raster path — a single-token page has no series intent to preserve.

resolveFeralFileToken's series branch is now unreachable from find; left in place (still unit-tested) rather than deleted here to keep the diff review-sized.

Verification

  • Live: the Display Edition URL builds 1 item with correct provenance in seconds (was 35 items after minutes).
  • Harness: series URL → resolver called, exact token indexed, zero Raster traffic; resolver-miss → clean exit with a series-specific error.
  • Suite: 447/447.

Three layers, one root cause. Node's happy-eyeballs gives each
address-family connect attempt 250 ms — shorter than one round trip to
api.raster.art from Asia (~250 ms RTT). On networks with broken IPv6
the IPv4 attempt dies mid-handshake, the IPv6 fallback has no route,
and find fails or stalls with no output.

- entry: raise the auto-select-family attempt timeout to 2.5 s so a
  transcontinental IPv4 connect is not cut off mid-handshake
- raster-client: 20 s AbortSignal on every query — a stalled request
  becomes a fast error, never a silent hang; network failures rethrow
  as RasterUnreachableError naming the endpoint and the undici cause
  (bare 'fetch failed' says neither)
- find: Raster unreachable at the coords step degrades to a one-item
  playlist with a warning instead of dying — Raster enriches a find,
  it is not required to build something playable

Repro from #97 (FF series URL that hung at 90 s and 150 s) now
completes: 35 items, or a fast actionable error when the network is
truly down. Suite: 447/447.

@feralfile-bot feralfile-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewers: 2 (approve: 2, comment: 0, request-changes: 0).

No meaningful findings after consolidation. The series path now uses the resolver’s exact token list, retains a clear resolver-miss error, and has focused coverage for the single-edition case. This resolves the previously deferred #98 concern.

Both reviewers caught it: rasterQuery only converted failures from
fetch() itself. A body that stalls or terminates after headers rejects
in response.text()/response.json(), outside the boundary — so
resolveCoords couldn't recognize RasterUnreachableError and never
performed its promised single-token fallback.

Body consumption now sits inside the boundary: a rejected read on an
ok response throws RasterUnreachableError (a 200 whose body can't be
read is unusable either way; falling back is right even for
server-side garbage). Intentional API-level errors keep their types:
an HTTP error stays a status error even when its body is unreadable
(best-effort text, never masks the status), and GraphQL errors are
unchanged.

Tests: mid-stream termination → typed error; 502 with unreadable body
→ status error, not unreachable; end-to-end find run against a mock
server that destroys the body → warns and degrades to a one-item
playlist. 450/450.
…arketplace→Raster

A Feral File series URL resolved ONE token via ff-marketplace, then
asked Raster, which maps the token to its parent artwork — so a
single-edition series page (e.g. 36 Points Display Edition) built the
sibling collection's 35 tokens instead of the 1 the page describes.
Flagged as out-of-scope in #98; this is that fix.

Series URLs now route through source-resolver's Feral File site module
(series slug → /api/series → /api/artworks?seriesID), exactly like
shows already did — one FF resolution path for page-shaped URLs, and
find builds precisely the tokens the page describes. Artwork URLs
keep the coords→Raster path: a single-token page carries no series
intent to preserve.

Verified live: the Display Edition series URL now builds 1 item with
correct provenance (was 35), in seconds (was minutes of indexer
warming for tokens the user never asked about).
@moskovich
moskovich force-pushed the refactor/ff-series-single-resolution branch from f0f67a1 to 698727e Compare July 27, 2026 13:07

@feralfile-bot feralfile-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewers: 2 (approve: 2, comment: 0, request-changes: 0).

No meaningful findings after re-review. The series route correctly uses the resolver token-list path, preserves artwork behavior, and includes focused success and resolver-miss coverage. No PR discussion exceptions apply.

@moskovich
moskovich merged commit 3e20a2a into main Jul 28, 2026
6 checks passed
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.

2 participants