/** * Gamemaster Client Utility * Use this in any app on the site to access gamemaster data * * Usage: * import { GamemasterClient } from './gamemaster-client.js'; * const gm = new GamemasterClient('/api/gamemaster'); * * // Get filtered pokemon * const pokemon = await gm.getPokemon(); * * // Get all forms with costumes * const allForms = await gm.getAllForms(); * * // Get moves * const moves = await gm.getMoves(); * * // Get raw unmodified data * const raw = await gm.getRaw(); * * // Check what's available * const status = await gm.getStatus(); */ export class GamemasterClient { constructor(baseUrl = '/api/gamemaster') { this.baseUrl = baseUrl; this.cache = new Map(); } /** * Make a request to the gamemaster API * @private * @param {string} endpoint * @param {Object} options * @returns {Promise} */ async request(endpoint, options = {}) { const url = `${this.baseUrl}${endpoint}`; try { const response = await fetch(url, { method: 'GET', ...options }); if (!response.ok) { const error = await response.json(); throw new Error(error.error || `HTTP ${response.status}`); } return await response.json(); } catch (error) { console.error(`Gamemaster API Error [${endpoint}]:`, error.message); throw error; } } /** * Get status of available files * @returns {Promise} */ async getStatus() { return this.request('/status'); } /** * Get filtered pokemon data (base forms + regional variants) * @param {Object} options * @param {boolean} options.useCache - Use cached data if available * @returns {Promise} */ async getPokemon(options = {}) { if (options.useCache && this.cache.has('pokemon')) { return this.cache.get('pokemon'); } const data = await this.request('/pokemon'); this.cache.set('pokemon', data); return data; } /** * Get all pokemon forms including costumes, shadows, events, etc. * @param {Object} options * @param {boolean} options.useCache - Use cached data if available * @returns {Promise} */ async getAllForms(options = {}) { if (options.useCache && this.cache.has('allForms')) { return this.cache.get('allForms'); } const data = await this.request('/pokemon/allForms'); this.cache.set('allForms', data); return data; } /** * Get all pokemon moves * @param {Object} options * @param {boolean} options.useCache - Use cached data if available * @returns {Promise} */ async getMoves(options = {}) { if (options.useCache && this.cache.has('moves')) { return this.cache.get('moves'); } const data = await this.request('/moves'); this.cache.set('moves', data); return data; } /** * Get raw unmodified gamemaster data * @param {Object} options * @param {boolean} options.useCache - Use cached data if available * @returns {Promise} */ async getRaw(options = {}) { if (options.useCache && this.cache.has('raw')) { return this.cache.get('raw'); } const data = await this.request('/raw'); this.cache.set('raw', data); return data; } /** * Download a file directly * @param {string} filename - One of: pokemon.json, pokemon-allFormsCostumes.json, pokemon-moves.json, latest-raw.json * @returns {Promise} */ async downloadFile(filename) { const url = `${this.baseUrl}/download/${filename}`; const response = await fetch(url); if (!response.ok) { throw new Error(`Failed to download ${filename}`); } const blob = await response.blob(); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); link.download = filename; document.body.appendChild(link); link.click(); document.body.removeChild(link); URL.revokeObjectURL(link.href); } /** * Clear cached data * @param {string} key - Optional specific key to clear, or clears all if not provided */ clearCache(key = null) { if (key) { this.cache.delete(key); } else { this.cache.clear(); } } /** * Get a specific pokemon from the filtered dataset * @param {string} pokemonId * @returns {Promise} */ async getPokemonById(pokemonId) { const pokemon = await this.getPokemon({ useCache: true }); return ( pokemon.find(p => p.data?.pokemonSettings?.pokemonId === pokemonId) || null ); } /** * Get a specific move * @param {string} moveId * @returns {Promise} */ async getMoveById(moveId) { const moves = await this.getMoves({ useCache: true }); return moves.find(m => m.data?.moveSettings?.movementId === moveId) || null; } } /** * Singleton instance - use this for most cases */ export const gamemasterClient = new GamemasterClient();