const RETRYABLE_STATUS = new Set([429, 500, 502, 503, 504]);
class PermitcoreError extends Error {
constructor(status, code, message) {
super(message);
this.status = status;
this.code = code;
}
}
async function callPermitcore(path) {
const res = await fetch(`https://api.permitcore.io${path}`, {
headers: {
Authorization: `Bearer ${process.env.PERMITCORE_API_KEY}`,
Accept: "application/json",
},
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new PermitcoreError(res.status, body?.error?.code, body?.error?.message);
}
return res.json();
}
async function withRetries(fn, maxAttempts = 4) {
let lastErr;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
lastErr = err;
const retryable = err instanceof PermitcoreError && RETRYABLE_STATUS.has(err.status);
if (!retryable || attempt === maxAttempts) throw err;
const delay = Math.min(2 ** attempt * 1000, 30000);
await new Promise((r) => setTimeout(r, delay));
}
}
throw lastErr;
}
// Usage:
const data = await withRetries(() => callPermitcore("/v1/jurisdictions/nyc/cohorts/distribution"));