// -------------------- // Individual providers // -------------------- async function getHackerNewsSubmissions(url) { const endpoint = `https://hn.algolia.com/api/v1/search` + `?query=${encodeURIComponent(url)}` + `&restrictSearchableAttributes=url` + `&tags=story`; const response = await fetch(endpoint); if (!response.ok) { throw new Error(`HN search failed: ${response.status}`); } const data = await response.json(); return (data.hits || []).map(hit => ({ id: hit.objectID, title: hit.title, url: hit.url, author: hit.author, points: hit.points, comments: hit.num_comments, createdAt: hit.created_at, discussionUrl: `https://news.ycombinator.com/item?id=${hit.objectID}` })); } async function getLobstersSubmissions(url) { try { const endpoint = `https://lobste.rs/search.json?q=${encodeURIComponent(url)}`; const response = await fetch(endpoint); if (!response.ok) { return []; } const results = await response.json(); return (results || []) .filter(item => item.url === url) .map(item => ({ id: item.short_id, title: item.title, url: item.url, author: item.submitter_user?.username, points: item.score, comments: item.comment_count, createdAt: item.created_at, discussionUrl: `https://lobste.rs/s/${item.short_id}` })); } catch { return []; } } // -------------------- // Simple concurrency limiter // -------------------- async function mapLimit(items, limit, mapper) { const results = new Array(items.length); let nextIndex = 0; async function worker() { while (true) { const index = nextIndex++; if (index >= items.length) { return; } results[index] = await mapper(items[index], index); } } const workers = Array.from( { length: Math.min(limit, items.length) }, worker ); await Promise.all(workers); return results; } // -------------------- // Main batch function // -------------------- async function checkUrls(urls, concurrency = 5) { return mapLimit(urls, concurrency, async url => { const [hackerNews, lobsters] = await Promise.allSettled([ getHackerNewsSubmissions(url), getLobstersSubmissions(url) ]); return { url, hackerNews: hackerNews.status === "fulfilled" ? hackerNews.value : [], lobsters: lobsters.status === "fulfilled" ? lobsters.value : [] }; }); }