/** * API Client * Fetch wrappers for /api/* endpoints with offline support */ class APIClient { constructor(baseURL = '/api') { this.baseURL = baseURL; this.interceptors = { request: [], response: [] }; } /** * Add request interceptor * @param {Function} interceptor - (config) => config */ addRequestInterceptor(interceptor) { this.interceptors.request.push(interceptor); } /** * Add response interceptor * @param {Function} interceptor - (response) => response */ addResponseInterceptor(interceptor) { this.interceptors.response.push(interceptor); } /** * Execute request interceptors * @param {RequestInit} config * @returns {RequestInit} */ _applyRequestInterceptors(config) { return this.interceptors.request.reduce( (acc, interceptor) => interceptor(acc), config ); } /** * Execute response interceptors * @param {Response} response * @returns {Response} */ _applyResponseInterceptors(response) { return this.interceptors.response.reduce( (acc, interceptor) => interceptor(acc), response ); } /** * Resolve an endpoint against the configured API base URL * @param {string} endpoint * @returns {string} */ resolveURL(endpoint = '') { return `${this.baseURL}${endpoint}`; } /** * Replace the configured API base URL for future requests. * @param {string} baseURL */ setBaseURL(baseURL = '/api') { this.baseURL = baseURL || '/api'; } /** * Make API request * @param {string} endpoint * @param {RequestInit} options * @returns {Promise} */ async request(endpoint, options = {}) { const url = this.resolveURL(endpoint); const config = this._applyRequestInterceptors({ ...options, headers: { 'Content-Type': 'application/json', ...options.headers } }); try { const response = await fetch(url, config); return this._applyResponseInterceptors(response); } catch (error) { // TODO: Implement offline queue management throw error; } } /** * GET request * @param {string} endpoint * @param {RequestInit} options * @returns {Promise} */ async get(endpoint, options = {}) { return this.request(endpoint, { ...options, method: 'GET' }); } /** * POST request * @param {string} endpoint * @param {any} data * @param {RequestInit} options * @returns {Promise} */ async post(endpoint, data, options = {}) { return this.request(endpoint, { ...options, method: 'POST', body: JSON.stringify(data) }); } /** * PUT request * @param {string} endpoint * @param {any} data * @param {RequestInit} options * @returns {Promise} */ async put(endpoint, data, options = {}) { return this.request(endpoint, { ...options, method: 'PUT', body: JSON.stringify(data) }); } /** * DELETE request * @param {string} endpoint * @param {RequestInit} options * @returns {Promise} */ async delete(endpoint, options = {}) { return this.request(endpoint, { ...options, method: 'DELETE' }); } } // Export singleton instance export const api = new APIClient();