MCPcopy
hub / github.com/cursor/community-plugins / fetchWithRateLimit

Function fetchWithRateLimit

apps/cursor/src/lib/github-plugin/parse.ts:142–179  ·  view source on GitHub ↗
(
  url: string,
  init: RequestInit & { maxWaitMs?: number } = {},
)

Source from the content-addressed store, hash-verified

140 * Bulk scripts can pass a larger budget.
141 */
142export async function fetchWithRateLimit(
143 url: string,
144 init: RequestInit & { maxWaitMs?: number } = {},
145): Promise<Response> {
146 const maxWaitMs = init.maxWaitMs ?? 30_000;
147 const maxAttempts = 4;
148 let totalWait = 0;
149 let lastRes: Response | null = null;
150
151 for (let attempt = 1; attempt <= maxAttempts; attempt++) {
152 const res = await fetch(url, init);
153 lastRes = res;
154
155 if (res.status !== 429 && res.status !== 403) return res;
156
157 // Distinguish "rate-limited" 403 from "forbidden" 403 by checking the
158 // X-RateLimit-Remaining header. A real auth/permission 403 has remaining > 0.
159 const remaining = Number(res.headers.get("x-ratelimit-remaining") ?? "");
160 if (res.status === 403 && Number.isFinite(remaining) && remaining > 0) {
161 return res;
162 }
163
164 const retryAfter = Number(res.headers.get("retry-after") ?? "");
165 const reset = Number(res.headers.get("x-ratelimit-reset") ?? "0") * 1000;
166 let waitMs =
167 Number.isFinite(retryAfter) && retryAfter > 0
168 ? retryAfter * 1000
169 : Math.max(reset - Date.now(), 1000);
170 waitMs = Math.min(waitMs, maxWaitMs - totalWait);
171
172 if (waitMs <= 0 || attempt === maxAttempts) return res;
173
174 totalWait += waitMs;
175 await sleep(waitMs);
176 }
177
178 return lastRes as Response;
179}
180
181/**
182 * Caller-controlled retry budget for GitHub API rate-limits.

Callers 3

cloneRepoFunction · 0.90
fetchGitHubTreeFunction · 0.85
fetchGitHubRepoMetaFunction · 0.85

Calls 1

sleepFunction · 0.70

Tested by

no test coverage detected