UNPKG

avocavo

Version:

Avocavo CLI - Structured nutrition data API with USDA-based calculations where applicable. Consistent nutrition estimates for apps and workflows. For informational use only. Not affiliated with or endorsed by USDA.

208 lines (180 loc) 5.61 kB
const axios = require('axios'); const chalk = require('chalk'); class NutritionAPI { constructor(apiKey, baseUrl = 'https://app.avocavo.app', timeout = 30000, auth = null) { this.apiKey = apiKey; this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash this.timeout = timeout; this.auth = auth; // Create axios instance with default configuration this.client = axios.create({ timeout: this.timeout, headers: { 'Content-Type': 'application/json', 'User-Agent': 'avocavo-cli/1.1.0' } }); // Set authentication headers if (this.apiKey) { if (this.apiKey.startsWith('eyJ') || this.apiKey.includes('.')) { // JWT token this.client.defaults.headers['Authorization'] = `Bearer ${this.apiKey}`; } else { // API key this.client.defaults.headers['X-API-Key'] = this.apiKey; } } } async analyzeIngredient(ingredient, verify = false, verbose = false) { try { const response = await this.client.post(`${this.baseUrl}/api/v2/nutrition/ingredient`, { ingredient: ingredient }); return response.data; } catch (error) { throw this.handleError(error); } } async analyzeText(text, verbose = false) { try { const response = await this.client.post(`${this.baseUrl}/api/v2/nutrition/analyze`, { text: text }); return response.data; } catch (error) { throw this.handleError(error); } } async analyzeRecipe(ingredients, servings = 1, verbose = false) { try { const response = await this.client.post(`${this.baseUrl}/api/v2/nutrition/recipe`, { ingredients: ingredients, servings: servings }); return response.data; } catch (error) { throw this.handleError(error); } } async analyzeBatch(ingredients, verbose = false) { try { // Transform array of strings to array of objects expected by batch endpoint const ingredientObjects = ingredients.map((ingredient, index) => ({ ingredient: ingredient, id: `item_${index + 1}` })); const response = await this.client.post(`${this.baseUrl}/api/v2/nutrition/batch`, { ingredients: ingredientObjects }); return response.data; } catch (error) { throw this.handleError(error); } } async getAccountUsage() { try { const response = await this.client.get(`${this.baseUrl}/api/v2/nutrition/account/usage`); return response.data; } catch (error) { throw this.handleError(error); } } async healthCheck() { try { // Remove auth headers for health check const headers = { ...this.client.defaults.headers }; delete headers['X-API-Key']; delete headers['Authorization']; const response = await this.client.get(`${this.baseUrl}/api/v2/nutrition/health`, { headers: headers }); return response.data; } catch (error) { throw this.handleError(error); } } // NEW UPC FUNCTIONALITY async searchUPC(upc) { try { const response = await this.client.post(`${this.baseUrl}/api/v2/upc/ingredient`, { upc: upc }); return response.data; } catch (error) { throw this.handleError(error); } } async searchUPCBatch(upcs) { try { const response = await this.client.post(`${this.baseUrl}/api/v2/upc/batch`, { upcs: upcs }); return response.data; } catch (error) { throw this.handleError(error); } } async upcHealthCheck() { try { // Remove auth headers for health check const headers = { ...this.client.defaults.headers }; delete headers['X-API-Key']; delete headers['Authorization']; const response = await this.client.get(`${this.baseUrl}/api/upc/health`, { headers: headers }); return response.data; } catch (error) { throw this.handleError(error); } } handleError(error) { if (error.response) { const status = error.response.status; const data = error.response.data; // Extract error message let message = data?.error || data?.message || `HTTP ${status} Error`; // Handle specific status codes switch (status) { case 401: message = 'Invalid API key or authentication failed'; break; case 402: message = 'Payment required - upgrade your plan or add credits'; break; case 403: message = data?.error || 'Access denied - feature not available on your plan'; break; case 429: const limit = data?.limit; const usage = data?.usage; message = `Rate limit exceeded${limit ? ` (${usage}/${limit})` : ''}`; break; case 500: case 502: case 503: message = 'Server error - please try again later'; break; } const apiError = new Error(message); apiError.status = status; apiError.response = data; return apiError; } else if (error.request) { return new Error('Network error - please check your connection'); } else { return new Error(error.message || 'Unknown error occurred'); } } } // Helper function to format response time function formatResponseTime(ms) { if (ms < 1000) { return chalk.green(`${ms}ms`); } else if (ms < 3000) { return chalk.yellow(`${ms}ms`); } else { return chalk.red(`${ms}ms`); } } module.exports = { NutritionAPI, formatResponseTime };