urltomarkdown/node_modules/express-rate-limit/dist/index.mjs

199 lines
6.8 KiB
JavaScript
Raw Normal View History

2022-01-09 15:20:59 +00:00
// source/memory-store.ts
var calculateNextResetTime = (windowMs) => {
const resetTime = new Date();
resetTime.setMilliseconds(resetTime.getMilliseconds() + windowMs);
return resetTime;
};
var MemoryStore = class {
init(options) {
this.windowMs = options.windowMs;
this.resetTime = calculateNextResetTime(this.windowMs);
this.hits = {};
const interval = setInterval(async () => {
await this.resetAll();
}, this.windowMs);
if (interval.unref) {
interval.unref();
}
}
async increment(key) {
const totalHits = (this.hits[key] ?? 0) + 1;
this.hits[key] = totalHits;
return {
totalHits,
resetTime: this.resetTime
};
}
async decrement(key) {
const current = this.hits[key];
if (current) {
this.hits[key] = current - 1;
}
}
async resetKey(key) {
delete this.hits[key];
}
async resetAll() {
this.hits = {};
this.resetTime = calculateNextResetTime(this.windowMs);
}
};
// source/lib.ts
var isLegacyStore = (store) => typeof store.incr === "function" && typeof store.increment !== "function";
var promisifyStore = (passedStore) => {
if (!isLegacyStore(passedStore)) {
return passedStore;
}
const legacyStore = passedStore;
class PromisifiedStore {
async increment(key) {
return new Promise((resolve, reject) => {
legacyStore.incr(key, (error, totalHits, resetTime) => {
if (error)
reject(error);
resolve({ totalHits, resetTime });
});
});
}
async decrement(key) {
return Promise.resolve(legacyStore.decrement(key));
}
async resetKey(key) {
return Promise.resolve(legacyStore.resetKey(key));
}
async resetAll() {
if (typeof legacyStore.resetAll === "function")
return Promise.resolve(legacyStore.resetAll());
}
}
return new PromisifiedStore();
};
var parseOptions = (passedOptions) => {
const options = {
windowMs: 60 * 1e3,
store: new MemoryStore(),
max: 5,
message: "Too many requests, please try again later.",
statusCode: 429,
legacyHeaders: passedOptions.headers ?? true,
standardHeaders: passedOptions.draft_polli_ratelimit_headers ?? false,
requestPropertyName: "rateLimit",
skipFailedRequests: false,
skipSuccessfulRequests: false,
requestWasSuccessful: (_request, response) => response.statusCode < 400,
skip: (_request, _response) => false,
keyGenerator: (request, _response) => {
if (!request.ip) {
console.error("WARN | `express-rate-limit` | `request.ip` is undefined. You can avoid this by providing a custom `keyGenerator` function, but it may be indicative of a larger issue.");
}
return request.ip;
},
handler: (_request, response, _next, _optionsUsed) => {
response.status(options.statusCode).send(options.message);
},
onLimitReached: (_request, _response, _optionsUsed) => {
},
...passedOptions
};
if (typeof options.store.incr !== "function" && typeof options.store.increment !== "function" || typeof options.store.decrement !== "function" || typeof options.store.resetKey !== "function" || typeof options.store.resetAll !== "undefined" && typeof options.store.resetAll !== "function" || typeof options.store.init !== "undefined" && typeof options.store.init !== "function") {
throw new TypeError("An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface.");
}
options.store = promisifyStore(options.store);
return options;
};
var handleAsyncErrors = (fn) => async (request, response, next) => {
try {
await Promise.resolve(fn(request, response, next)).catch(next);
} catch (error) {
next(error);
}
};
var rateLimit = (passedOptions) => {
const options = parseOptions(passedOptions ?? {});
if (typeof options.store.init === "function")
options.store.init(options);
const middleware = handleAsyncErrors(async (request, response, next) => {
const skip = await options.skip(request, response);
if (skip) {
next();
return;
}
const augmentedRequest = request;
const key = await options.keyGenerator(request, response);
const { totalHits, resetTime } = await options.store.increment(key);
const retrieveQuota = typeof options.max === "function" ? options.max(request, response) : options.max;
const maxHits = await retrieveQuota;
augmentedRequest[options.requestPropertyName] = {
limit: maxHits,
current: totalHits,
remaining: Math.max(maxHits - totalHits, 0),
resetTime
};
if (options.legacyHeaders && !response.headersSent) {
response.setHeader("X-RateLimit-Limit", maxHits);
response.setHeader("X-RateLimit-Remaining", augmentedRequest[options.requestPropertyName].remaining);
if (resetTime instanceof Date) {
response.setHeader("Date", new Date().toUTCString());
response.setHeader("X-RateLimit-Reset", Math.ceil(resetTime.getTime() / 1e3));
}
}
if (options.standardHeaders && !response.headersSent) {
response.setHeader("RateLimit-Limit", maxHits);
response.setHeader("RateLimit-Remaining", augmentedRequest[options.requestPropertyName].remaining);
if (resetTime) {
const deltaSeconds = Math.ceil((resetTime.getTime() - Date.now()) / 1e3);
response.setHeader("RateLimit-Reset", Math.max(0, deltaSeconds));
}
}
if (options.skipFailedRequests || options.skipSuccessfulRequests) {
let decremented = false;
const decrementKey = async () => {
if (!decremented) {
await options.store.decrement(key);
decremented = true;
}
};
if (options.skipFailedRequests) {
response.on("finish", async () => {
if (!options.requestWasSuccessful(request, response))
await decrementKey();
});
response.on("close", async () => {
if (!response.writableEnded)
await decrementKey();
});
response.on("error", async () => {
await decrementKey();
});
}
if (options.skipSuccessfulRequests) {
response.on("finish", async () => {
if (options.requestWasSuccessful(request, response))
await decrementKey();
});
}
}
if (maxHits && totalHits === maxHits + 1) {
options.onLimitReached(request, response, options);
}
if (maxHits && totalHits > maxHits) {
if ((options.legacyHeaders || options.standardHeaders) && !response.headersSent) {
response.setHeader("Retry-After", Math.ceil(options.windowMs / 1e3));
}
options.handler(request, response, next, options);
return;
}
next();
});
middleware.resetKey = options.store.resetKey.bind(options.store);
return middleware;
};
var lib_default = rateLimit;
// source/index.ts
var source_default = lib_default;
export {
source_default as default
};