| 120 | } |
| 121 | |
| 122 | function buildRateLimitMessage(res, payloadMessage) { |
| 123 | const remaining = res.headers.get('x-ratelimit-remaining'); |
| 124 | const reset = res.headers.get('x-ratelimit-reset'); |
| 125 | let resetInfo = ''; |
| 126 | if (reset) { |
| 127 | const resetMs = parseInt(reset, 10) * 1000; |
| 128 | const etaMin = Math.max(0, Math.round((resetMs - Date.now()) / 60000)); |
| 129 | const date = new Date(resetMs); |
| 130 | resetInfo = ` (resets around ${date.toLocaleTimeString()} in ~${etaMin}m)`; |
| 131 | } |
| 132 | const hasToken = !!((ghTokenInput && ghTokenInput.value) || localStorage.getItem('scl_gh_token')); |
| 133 | const tokenHint = hasToken ? 'Please wait for the limit to reset or use fewer bulk operations.' : 'Add a GitHub token (no scopes needed) in the header to raise limits.'; |
| 134 | const baseMsg = 'GitHub API rate limit exceeded'; |
| 135 | const payload = payloadMessage ? `: ${payloadMessage}` : ''; |
| 136 | const rem = remaining !== null ? ` (remaining: ${remaining})` : ''; |
| 137 | return `${baseMsg}${payload}${rem}${resetInfo}. ${tokenHint}`; |
| 138 | } |
| 139 | |
| 140 | async function fetchJSON(url, opts = {}) { |
| 141 | const res = await fetch(url, { headers: { ...authHeaders() }, ...opts }); |