jokeapi.js•2.01 kB
import { fetchWithRetry } from './sharedFetch.js';
const BASE_URL = 'https://v2.jokeapi.dev';
const CATEGORY_MAP = {
programming: 'Programming',
general: 'Miscellaneous',
pun: 'Pun',
spooky: 'Spooky',
christmas: 'Christmas',
};
const SUPPORTED_LANGS = new Set(['en', 'cs', 'de', 'es', 'fr', 'pt']);
export async function getJoke(preferences, context = {}) {
const { category, lang, blacklist = [] } = preferences;
const { timeoutMs = 2000, retries = 0, fetchImpl, allowNet = true } = context;
if (!allowNet) {
throw new Error('Network access disabled for JokeAPI');
}
const categorySegment = CATEGORY_MAP[category] ?? 'Any';
const params = new URLSearchParams({ format: 'json' });
if (SUPPORTED_LANGS.has(lang)) {
params.set('lang', lang);
}
if (blacklist.length > 0) {
params.set('blacklistFlags', blacklist.join(','));
}
params.set('type', 'single');
const url = `${BASE_URL}/joke/${categorySegment}?${params.toString()}`;
const response = await fetchWithRetry(url, { headers: { Accept: 'application/json' } }, { timeoutMs, retries, fetchImpl });
const data = await response.json();
if (data.error) {
const reason = data.message ?? 'Unknown error';
const err = new Error(`JokeAPI returned an error: ${reason}`);
err.code = 'JOKEAPI_ERROR';
throw err;
}
const text = data.type === 'single'
? data.joke
: `${data.setup ?? ''}\n${data.delivery ?? ''}`.trim();
return {
text,
category: toInternalCategory(data.category) ?? category,
source: 'jokeapi',
};
}
function toInternalCategory(category) {
if (!category) return undefined;
const normalized = String(category).trim().toLowerCase();
switch (normalized) {
case 'programming':
return 'programming';
case 'misc':
case 'miscellaneous':
return 'general';
case 'pun':
return 'pun';
case 'spooky':
return 'spooky';
case 'christmas':
return 'christmas';
default:
return undefined;
}
}