diff --git a/code/websites/pokedex.online/src/utilities/gamemaster-client.js b/code/websites/pokedex.online/src/utilities/gamemaster-client.js new file mode 100644 index 0000000..f87b45a --- /dev/null +++ b/code/websites/pokedex.online/src/utilities/gamemaster-client.js @@ -0,0 +1,189 @@ +/** + * 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();