dex/src/data/api.js
chris 0f00b50198 Move & item lookup: tabbed Search + detail pages
- Search becomes a tabbed lookup: Pokémon (offline snapshot) / Moves /
  Items. Moves + Items lazy-load a name index from PokéAPI (SW-cached) and
  filter client-side; short move result sets get type/class/power chips
- MoveDetail (#/move/:id): type-themed header, power/accuracy/PP/priority,
  effect text with %-substitution, target, ailment/crit/drain/stat-change
  summary, learned-by count
- ItemDetail (#/item/:id): item sprite, category, cost, fling power,
  attributes, held-by count, effect text
- Loose name matching (hyphens vs spaces); run-token guards stale async
  renders when the query or tab changes mid-fetch

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-27 13:51:51 -04:00

50 lines
1.8 KiB
JavaScript

/**
* Lazy detail fetches. Only detail pages call these; the list, search and
* game switching all run off the bundled snapshot.
*
* In-flight requests are de-duplicated in memory. Durable caching (across
* reloads and offline) is the service worker's job — see src/sw.js, which
* runs stale-while-revalidate over this origin.
*/
const BASE = 'https://pokeapi.co/api/v2';
const inflight = new Map();
export function getJSON(path) {
if (inflight.has(path)) return inflight.get(path);
const promise = fetch(`${BASE}/${path}`)
.then((res) => {
if (!res.ok) throw new Error(`${path}${res.status}`);
return res.json();
})
.finally(() => inflight.delete(path));
inflight.set(path, promise);
return promise;
}
export const getPokemon = (idOrName) => getJSON(`pokemon/${idOrName}`);
export const getSpecies = (idOrName) => getJSON(`pokemon-species/${idOrName}`);
export const getEvolutionChain = (id) => getJSON(`evolution-chain/${id}`);
export const getMove = (idOrName) => getJSON(`move/${idOrName}`);
export const getItem = (idOrName) => getJSON(`item/${idOrName}`);
export const getEncounters = (id) => getJSON(`pokemon/${id}/encounters`);
// Full name indexes for the lookup tabs — one request each, then the
// service worker keeps them (stale-while-revalidate).
export const getMoveIndex = () => getJSON('move?limit=2000');
export const getItemIndex = () => getJSON('item?limit=3000');
/** Run async `fn` over `items` with a bounded number of parallel workers. */
export async function mapLimit(items, limit, fn) {
const out = new Array(items.length);
let i = 0;
await Promise.all(
Array.from({ length: Math.min(limit, items.length) }, async () => {
while (i < items.length) {
const idx = i++;
out[idx] = await fn(items[idx], idx);
}
}),
);
return out;
}