ratelimit-header-parser
Version:
Parse RateLimit headers of various forms, including the combined form from draft 7 of the IETF standard, into a normalized format.
160 lines (157 loc) • 5.04 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// source/index.ts
var source_exports = {};
__export(source_exports, {
parseRateLimit: () => parseRateLimit
});
module.exports = __toCommonJS(source_exports);
// source/ratelimit-header-parser.ts
function parseRateLimit(input, options) {
if ("headers" in input && typeof input.headers === "object" && !Array.isArray(input.headers)) {
return parseHeadersObject(input.headers, options);
}
if ("getHeaders" in input && typeof input.getHeaders === "function") {
return parseHeadersObject(input.getHeaders(), options);
}
return parseHeadersObject(input, options);
}
function parseHeadersObject(input, options) {
const combined = getHeader(input, "ratelimit");
if (combined)
return parseCombinedRateLimitHeader(combined);
let prefix;
if (getHeader(input, "ratelimit-remaining")) {
prefix = "ratelimit-";
} else if (getHeader(input, "x-ratelimit-remaining")) {
prefix = "x-ratelimit-";
} else if (getHeader(input, "x-rate-limit-remaining")) {
prefix = "x-rate-limit-";
} else {
return;
}
const limit = toInt(getHeader(input, `${prefix}limit`));
const used = toInt(getHeader(input, `${prefix}used`)) || toInt(getHeader(input, `${prefix}observed`));
const remaining = toInt(getHeader(input, `${prefix}remaining`));
let reset;
const resetRaw = getHeader(input, `${prefix}reset`);
const resetType = options == null ? void 0 : options.reset;
switch (resetType) {
case "date": {
reset = parseResetDate(resetRaw != null ? resetRaw : "");
break;
}
case "unix": {
reset = parseResetUnix(resetRaw != null ? resetRaw : "");
break;
}
case "seconds": {
reset = parseResetSeconds(resetRaw != null ? resetRaw : "");
break;
}
case "milliseconds": {
reset = parseResetMilliseconds(resetRaw != null ? resetRaw : "");
break;
}
default: {
if (resetRaw)
reset = parseResetAuto(resetRaw);
else {
const retryAfter = getHeader(input, "retry-after");
if (retryAfter) {
reset = parseResetUnix(retryAfter);
}
}
}
}
return {
limit: Number.isNaN(limit) ? used + remaining : limit,
// Reddit omits
used: Number.isNaN(used) ? limit - remaining : used,
// Most omit
remaining,
reset
};
}
var reLimit = /limit\s*=\s*(\d+)/i;
var reRemaining = /remaining\s*=\s*(\d+)/i;
var reReset = /reset\s*=\s*(\d+)/i;
function parseCombinedRateLimitHeader(input) {
var _a, _b, _c;
const limit = toInt((_a = reLimit.exec(input)) == null ? void 0 : _a[1]);
const remaining = toInt((_b = reRemaining.exec(input)) == null ? void 0 : _b[1]);
const resetSeconds = toInt((_c = reReset.exec(input)) == null ? void 0 : _c[1]);
const reset = secondsToDate(resetSeconds);
return {
limit,
used: limit - remaining,
remaining,
reset
};
}
function secondsToDate(seconds) {
const d = /* @__PURE__ */ new Date();
d.setSeconds(d.getSeconds() + seconds);
return d;
}
function toInt(input) {
if (typeof input === "number")
return input;
return Number.parseInt(input != null ? input : "", 10);
}
function getHeader(headers, name) {
var _a;
if ("get" in headers && typeof headers.get === "function") {
return (_a = headers.get(name)) != null ? _a : void 0;
}
if (name in headers && typeof headers[name] === "string") {
return headers[name];
}
return void 0;
}
function parseResetDate(resetRaw) {
return new Date(resetRaw);
}
function parseResetUnix(resetRaw) {
const resetNumber = toInt(resetRaw);
return new Date(resetNumber * 1e3);
}
function parseResetSeconds(resetRaw) {
const resetNumber = toInt(resetRaw);
return secondsToDate(resetNumber);
}
function parseResetMilliseconds(resetRaw) {
const resetNumber = toInt(resetRaw);
return secondsToDate(resetNumber / 1e3);
}
var reLetters = /[a-z]/i;
function parseResetAuto(resetRaw) {
if (reLetters.test(resetRaw)) {
return parseResetDate(resetRaw);
}
const resetNumber = toInt(resetRaw);
if (resetNumber && resetNumber > 1e9) {
return parseResetUnix(resetNumber);
}
return parseResetSeconds(resetNumber);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
parseRateLimit
});
;