|
| 1 | +# API Client: import boundary guard, path-encoding, cache-invalidation, pagination |
| 2 | + |
| 3 | +Four `frontend/src/lib/api/` issues. Three are "regression trap" issues whose fix already |
| 4 | +landed in an earlier commit - the delta here is the guard test that keeps it from |
| 5 | +regressing, plus one missed call site. #1336 is a new helper. |
| 6 | + |
| 7 | +## What changed and why |
| 8 | + |
| 9 | +### #1332 - public/admin client split guard |
| 10 | +The split (`public-client.ts` / `admin-client.ts`, commit `895748e`) and an *export* |
| 11 | +boundary test (`public-client.test.ts`) already exist. Added the missing *import* boundary |
| 12 | +guard: `client-import-boundary.test.ts` statically scans `src/app/**` and fails if any route |
| 13 | +outside `src/app/admin/**` (and the privileged `markets/<id>/resolve` route) imports |
| 14 | +`admin-client` directly. It also re-asserts that `public-client.ts` never imports |
| 15 | +`admin-client` and exposes no `/api/v1/admin`, `/api/v1/audit`, or `/api/v1/email` path. |
| 16 | + |
| 17 | +### #1333 - centralize path-parameter encoding |
| 18 | +`fillPath()` was the intended single encoder (commit `dd027a9`) but lived in |
| 19 | +`public-client.ts` and three `src/lib/api/` call sites still called `encodeURIComponent` |
| 20 | +directly (`admin-client.ts` email preview, `tts-client.ts` job status + audio). |
| 21 | + |
| 22 | +- Moved `fillPath` (plus a new `fillPathParams` for multi-segment templates) into a |
| 23 | + dedicated `paths.ts`; `public-client.ts` re-exports it so existing importers are |
| 24 | + unaffected. |
| 25 | +- Routed the three stray call sites through `fillPath`. |
| 26 | +- `path-encoding.test.ts`: (a) a `market_id` / `tx_hash` containing `/`, `?`, `#` |
| 27 | + round-trips as a single encoded path segment (asserted against a mocked `fetch`); |
| 28 | + (b) `fillPath` encodes exactly once; (c) a grep guard - no `src/lib/api/*.ts` file |
| 29 | + except `paths.ts` calls `encodeURIComponent`. |
| 30 | +- Out of scope: raw `fetch()` calls in app pages/components that never used the client - |
| 31 | + a broader refactor with its own issues. |
| 32 | + |
| 33 | +### #1335 - invalidate cache tags only on mutation success |
| 34 | +The `succeeded` guard (a 200 body with `success: false` must not bust the cache, commit |
| 35 | +`4a15eda`) already exists in both request helpers. Added |
| 36 | +`cache-invalidation-on-success.test.ts`: a POST returning `{ success: false }` leaves the |
| 37 | +tagged entry untouched; `{ success: true }` and non-envelope bodies invalidate as before. |
| 38 | +(The guard lives in the request helper, not `cache.ts`, because that is where the response |
| 39 | +body is parsed.) |
| 40 | + |
| 41 | +### #1336 - offset/cursor pagination helper (new `pagination.ts`) |
| 42 | +- `buildPaginationParams({ mode: 'offset' | 'cursor', limit?, offset?/cursor? })` builds the |
| 43 | + query params for either mode. `limit` defaults to 20 and **throws `RangeError` before the |
| 44 | + request is sent** when it exceeds 100, mirroring the server's documented 400 message. |
| 45 | +- The cursor is passed through verbatim - never parsed or mutated client-side. |
| 46 | +- `CursorPager` holds the opaque cursor for cursor-mode paging; `setSort(key)` drops the |
| 47 | + stale cursor when the sort order actually changes, so the next page restarts from the top. |
| 48 | + |
| 49 | +## How to test |
| 50 | + |
| 51 | +``` |
| 52 | +cd frontend |
| 53 | +npm ci --legacy-peer-deps |
| 54 | +./node_modules/.bin/jest src/lib/api |
| 55 | +``` |
| 56 | + |
| 57 | +- `src/lib/api` Jest suite: **135 pre-existing + 21 new tests pass**. |
| 58 | +- `tsc --noEmit`: the new/changed files add no errors over the repo's pre-existing count. |
| 59 | +- `npm run build` (`generate-client && next build`) not run here - needs the full monorepo |
| 60 | + build; nothing in this change touches build config. |
| 61 | + |
| 62 | +## Breaking changes |
| 63 | + |
| 64 | +None. `fillPath` keeps its `public-client` export; the request/cache behaviour is unchanged. |
| 65 | + |
| 66 | +## Related issues |
| 67 | + |
| 68 | +Closes #1332 |
| 69 | +Closes #1333 |
| 70 | +Closes #1335 |
| 71 | +Closes #1336 |
| 72 | + |
| 73 | +## PR Checklist |
| 74 | + |
| 75 | +- [x] Branch is up to date with `main` |
| 76 | +- [x] Commit messages follow Conventional Commits |
| 77 | +- [x] Tests added for the change |
| 78 | +- [x] Documentation updated if behaviour changed (n/a - behaviour preserved) |
| 79 | +- [x] No secrets or credentials committed |
0 commit comments