Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions lib/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,8 @@ export function displayName(profile: GitHubUserProfile): string {
* DATA FETCHING
* ========================================================================== */

const FETCH_TIMEOUT_MS = 4000;

export async function fetchGitHubContributions(
username: string,
options: FetchOptions = {}
Expand All @@ -537,16 +539,47 @@ export async function fetchGitHubContributions(
: true;
};

const load = () => fetchContributionsUncached(username, key, options);
const loadWithTimeout = async (): Promise<ExtendedContributionData> => {
const controller = new AbortController();
if (options.signal) {
if (options.signal.aborted) {
controller.abort();
} else {
options.signal.addEventListener('abort', () => controller.abort(), { once: true });
}
}

let timerId = null;
const timeoutPromise = new Promise<never>((_, reject) => {
timerId = setTimeout(() => {
controller.abort();
reject(new Error(`GitHub API request timed out after ${FETCH_TIMEOUT_MS / 1000}s`));
}, FETCH_TIMEOUT_MS);
if (timerId && typeof timerId.unref === 'function') {
timerId.unref();
}
});

try {
return await Promise.race([
fetchContributionsUncached(username, key, { ...options, signal: controller.signal }),
timeoutPromise,
]);
} finally {
if (timerId) {
clearTimeout(timerId);
}
}
};

if (options.bypassCache || options.forceRefresh) {
try {
return await load();
return await loadWithTimeout();
} catch (err: unknown) {
const staleData = await contributionsCache.get(key);
if (staleData) {
console.warn(
`[GitHub API] Fetch failed for "${username}", falling back to stale cache:`,
`[GitHub API] Fetch failed or timed out for "${username}", falling back to stale cache:`,
err
);
return {
Expand All @@ -559,12 +592,12 @@ export async function fetchGitHubContributions(
}

try {
return await contributionsCache.getOrSet(key, load, LONG_CACHE_TTL, shouldFetch);
return await contributionsCache.getOrSet(key, loadWithTimeout, LONG_CACHE_TTL, shouldFetch);
} catch (err: unknown) {
const staleData = await contributionsCache.get(key);
if (staleData) {
console.warn(
`[GitHub API] Fetch failed for "${username}", falling work back to stale cache:`,
`[GitHub API] Fetch failed or timed out for "${username}", falling back to stale cache:`,
err
);
return {
Expand Down
Loading