2026-07-17 — XP-134 Refund/settlement pagination + error-body sanitize
What shipped
Section titled “What shipped”- Keyset (forward-only, cursor+limit) pagination on
listRefundableSettlementsandlistRefunds— previouslyfindManyover the whole table with nested relations, a Worker CPU/memory blow-up as settlements accrue. Contract input gains{ limit (coerce, 1–100, default 20), next: uuid? }; response goes bare-array →{ items, next? }, mirroring theusers.listkeyset shape. UI (/admin/finance/refunds) gets a per-panel forward-only pager (“Awal”/“Berikutnya”), same as the admin-users pager. - Global non-JSON error-body sanitize: extracted
parseApiErrorBody(status, rawBody)inapi-client.ts. A JSON oRPC object error still passesmessage+data.codethrough; anything else (HTML CF error page, plain text, bare scalar, empty{}) collapses to a concise Indonesian sentence — never the raw body. Fixes the incident where a 5xx HTML page rendered as a wall of raw HTML in the error display.
Key decisions
Section titled “Key decisions”- Kept the per-file convention (two client fns, two schemas, two service files repeat the keyset shape) matching the repo’s
users.listlazy-load isolation, rather than extracting a shared helper — advisory-only per standards review. - Non-JSON messages surface clean; JSON errors keep the dev
API <method> <url> →debug prefix.
Gotchas / lessons
Section titled “Gotchas / lessons”- Drizzle RQBv2 (
1.0.0-rc.3)whereis an object DSL, not rawSQL. A rawSQLseek predicate emitsWHERE "refunds"."requested_at" < …while the query aliases the root table asd0→ Postgres42P01 invalid reference to FROM-clause entry. Fix: the v2RAWoperator’s callback form{ RAW: (t) => or(lt(t.col,…), and(eq(t.col,…), lt(t.id,…))) }—tis the aliased root, so the comparison binds tod0. (Covered by.claude/rules/drizzle-relational-raw-alias.md.) - A static screenshot cannot distinguish
overflow-x-autoscroll from silent overflow, nor measure a button’s touch-target hit-area — added that nuance tofrontend-visual-test.md.
Reference changes
Section titled “Reference changes”.claude/rules/frontend-visual-test.md— added the “PNG can’t prove scroll vs overflow, nor tap-target size; flag for manual scroll/tap check” note to the review checklist.