@prismicio/client
Version:
The official JavaScript + TypeScript client library for Prismic
74 lines (72 loc) • 2.93 kB
JavaScript
import { pLimit } from "./pLimit.js";
//#region src/lib/request.ts
/**
* The default number of milliseconds to wait before retrying a rate-limited
* `fetch()` request (429 response code). The default value is only used if the
* response does not include a `retry-after` header.
*/
const DEFAULT_RETRY_AFTER = 1500;
/**
* A record of URLs mapped to throttled task runners.
*/
const THROTTLED_RUNNERS = {};
/**
* A record of URLs mapped to active deduplicated jobs. Jobs are keyed by their
* optional signal.
*/
const DEDUPLICATED_JOBS = {};
async function memoizeResponse(response) {
const blob = await response.blob();
const memoized = {
ok: response.ok,
status: response.status,
headers: response.headers,
url: response.url,
text: async () => blob.text(),
json: async () => JSON.parse(await blob.text()),
blob: async () => blob,
clone: () => memoized
};
return memoized;
}
/**
* Makes an HTTP request with automatic retry for rate limits and request
* deduplication.
*
* @param url - The URL to request.
* @param init - Fetch options.
* @param fetchFn - The fetch function to use.
*
* @returns The response from the fetch request.
*/
async function request(url, init, fetchFn) {
const stringURL = url.toString();
let job;
if (init === null || init === void 0 ? void 0 : init.body) {
var _url$hostname;
job = (THROTTLED_RUNNERS[_url$hostname = url.hostname] || (THROTTLED_RUNNERS[_url$hostname] = pLimit({ interval: DEFAULT_RETRY_AFTER })))(() => fetchFn(stringURL, init));
} else {
var _DEDUPLICATED_JOBS$st;
const existingJob = (_DEDUPLICATED_JOBS$st = DEDUPLICATED_JOBS[stringURL]) === null || _DEDUPLICATED_JOBS$st === void 0 ? void 0 : _DEDUPLICATED_JOBS$st.get(init === null || init === void 0 ? void 0 : init.signal);
if (existingJob) job = existingJob;
else {
job = fetchFn(stringURL, init).then(memoizeResponse).finally(() => {
var _DEDUPLICATED_JOBS$st2, _DEDUPLICATED_JOBS$st3;
(_DEDUPLICATED_JOBS$st2 = DEDUPLICATED_JOBS[stringURL]) === null || _DEDUPLICATED_JOBS$st2 === void 0 || _DEDUPLICATED_JOBS$st2.delete(init === null || init === void 0 ? void 0 : init.signal);
if (((_DEDUPLICATED_JOBS$st3 = DEDUPLICATED_JOBS[stringURL]) === null || _DEDUPLICATED_JOBS$st3 === void 0 ? void 0 : _DEDUPLICATED_JOBS$st3.size) === 0) delete DEDUPLICATED_JOBS[stringURL];
});
(DEDUPLICATED_JOBS[stringURL] || (DEDUPLICATED_JOBS[stringURL] = /* @__PURE__ */ new Map())).set(init === null || init === void 0 ? void 0 : init.signal, job);
}
}
const response = await job;
if (response.status === 429) {
const retryAfter = Number(response.headers.get("retry-after"));
const resolvedRetryAfter = Number.isNaN(retryAfter) ? DEFAULT_RETRY_AFTER : retryAfter * 1e3;
await new Promise((resolve) => setTimeout(resolve, resolvedRetryAfter));
return request(url, init, fetchFn);
}
return response;
}
//#endregion
export { request };
//# sourceMappingURL=request.js.map