import { write } from "bun"; const API_BASE = "https://eldenring.fanapis.com/api"; async function fetchAll(endpoint: string) { let allData: any[] = []; let page = 0; const limit = 100; while (true) { const res = await fetch(`${API_BASE}/${endpoint}?limit=${limit}&page=${page}`); if (!res.ok) { console.error(`Error fetching ${endpoint}:`, res.status); break; } const data = await res.json(); if (!data.data || data.data.length === 0) { break; } allData = allData.concat(data.data); console.log(`Fetched ${allData.length} ${endpoint}...`); if (data.data.length < limit) { break; } page++; } return allData; } function extractRegion(entityRegion: string | null | undefined, locationStr: string | null | undefined, locationLookup: Map, uniqueRegions: Set): string | null { // 1. If the entity has a region, use it (and clean up typos from API) if (entityRegion) { let r = entityRegion; if (r === "Liunia of the Lakes") r = "Liurnia of the Lakes"; if (r === "Mountaintop of the Giants") r = "Mountaintops of the Giants"; if (r === "Altus Pleateau") r = "Altus Plateau"; uniqueRegions.add(r); return r; } // 2. Lookup via locations map if (locationStr) { for (const [locName, regionName] of locationLookup.entries()) { if (locationStr.toLowerCase().includes(locName.toLowerCase())) { return regionName; } } // 3. Fallback: check if the location string itself contains a known dynamic region for (const region of uniqueRegions) { if (locationStr.toLowerCase().includes(region.toLowerCase())) { return region; } } } return null; } async function main() { console.log("Fetching locations..."); const locations = await fetchAll("locations"); const uniqueRegions = new Set(); const locationLookup = new Map(); // Normalize regions and build a lookup map: LocationName -> Region for (const loc of locations) { if (loc.region) { let r = loc.region; if (r === "Liunia of the Lakes" || r === "Liurnia") r = "Liurnia of the Lakes"; if (r === "Mountaintop of the Giants") r = "Mountaintops of the Giants"; if (r === "Altus Pleateau") r = "Altus Plateau"; uniqueRegions.add(r); locationLookup.set(loc.name, r); } } console.log("Fetching bosses..."); const bosses = await fetchAll("bosses"); console.log("Fetching creatures..."); const creatures = await fetchAll("creatures"); console.log("Fetching npcs..."); const npcs = await fetchAll("npcs"); const livingEntities = [ ...bosses.map(b => ({ ...b, _type: "boss" })), ...creatures.map(c => ({ ...c, _type: "creature" })), ...npcs.map(n => ({ ...n, _type: "npc" })) ]; const cleanedEntities = livingEntities.map(entity => { // combine all possible location info const cleanRegion = extractRegion(entity.region, entity.location, locationLookup, uniqueRegions); return { id: entity.id, name: entity.name, type: entity._type, cleanRegion: cleanRegion || "Unknown", originalLocation: entity.location, drops: entity.drops || [] }; }); // Save regions based on what was dynamically found in the API const regionsData = Array.from(uniqueRegions).map((r, i) => ({ id: i + 1, name: r })); console.log("Fetching items..."); const items = await fetchAll("items"); // you might also want weapons, armors etc depending on what "items" means to the user. // The fan API separates items, weapons, armors, etc. Let's just fetch items for now. const output = { regions: regionsData, entities: cleanedEntities, items: items.map(i => ({ id: i.id, name: i.name, type: i.type, effect: i.effect })) }; await write("./db/fanapi_data.json", JSON.stringify(output, null, 2)); console.log("Saved to db/fanapi_data.json"); } main().catch(console.error);