Initial commit

This commit is contained in:
2025-12-07 14:32:46 +00:00
commit 0a0969b8af
4726 changed files with 536089 additions and 0 deletions

959
node_modules/express-rate-limit/dist/index.cjs generated vendored Normal file
View File

@@ -0,0 +1,959 @@
"use strict";
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 index_exports = {};
__export(index_exports, {
MemoryStore: () => MemoryStore,
default: () => rate_limit_default,
ipKeyGenerator: () => ipKeyGenerator,
rateLimit: () => rate_limit_default
});
module.exports = __toCommonJS(index_exports);
// source/ip-key-generator.ts
var import_node_net = require("node:net");
var import_ip_address = require("ip-address");
function ipKeyGenerator(ip, ipv6Subnet = 56) {
if (ipv6Subnet && (0, import_node_net.isIPv6)(ip)) {
return `${new import_ip_address.Address6(`${ip}/${ipv6Subnet}`).startAddress().correctForm()}/${ipv6Subnet}`;
}
return ip;
}
// source/memory-store.ts
var MemoryStore = class {
constructor(validations2) {
this.validations = validations2;
/**
* These two maps store usage (requests) and reset time by key (for example, IP
* addresses or API keys).
*
* They are split into two to avoid having to iterate through the entire set to
* determine which ones need reset. Instead, `Client`s are moved from `previous`
* to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
* left in `previous`, i.e., those that have not made any recent requests, are
* known to be expired and can be deleted in bulk.
*/
this.previous = /* @__PURE__ */ new Map();
this.current = /* @__PURE__ */ new Map();
/**
* Confirmation that the keys incremented in once instance of MemoryStore
* cannot affect other instances.
*/
this.localKeys = true;
}
/**
* Method that initializes the store.
*
* @param options {Options} - The options used to setup the middleware.
*/
init(options) {
this.windowMs = options.windowMs;
this.validations?.windowMs(this.windowMs);
if (this.interval) clearInterval(this.interval);
this.interval = setInterval(() => {
this.clearExpired();
}, this.windowMs);
this.interval.unref?.();
}
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
*
* @public
*/
async get(key) {
return this.current.get(key) ?? this.previous.get(key);
}
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*
* @public
*/
async increment(key) {
const client = this.getClient(key);
const now = Date.now();
if (client.resetTime.getTime() <= now) {
this.resetClient(client, now);
}
client.totalHits++;
return client;
}
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
async decrement(key) {
const client = this.getClient(key);
if (client.totalHits > 0) client.totalHits--;
}
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
async resetKey(key) {
this.current.delete(key);
this.previous.delete(key);
}
/**
* Method to reset everyone's hit counter.
*
* @public
*/
async resetAll() {
this.current.clear();
this.previous.clear();
}
/**
* Method to stop the timer (if currently running) and prevent any memory
* leaks.
*
* @public
*/
shutdown() {
clearInterval(this.interval);
void this.resetAll();
}
/**
* Recycles a client by setting its hit count to zero, and reset time to
* `windowMs` milliseconds from now.
*
* NOT to be confused with `#resetKey()`, which removes a client from both the
* `current` and `previous` maps.
*
* @param client {Client} - The client to recycle.
* @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
*
* @return {Client} - The modified client that was passed in, to allow for chaining.
*/
resetClient(client, now = Date.now()) {
client.totalHits = 0;
client.resetTime.setTime(now + this.windowMs);
return client;
}
/**
* Retrieves or creates a client, given a key. Also ensures that the client being
* returned is in the `current` map.
*
* @param key {string} - The key under which the client is (or is to be) stored.
*
* @returns {Client} - The requested client.
*/
getClient(key) {
if (this.current.has(key)) return this.current.get(key);
let client;
if (this.previous.has(key)) {
client = this.previous.get(key);
this.previous.delete(key);
} else {
client = { totalHits: 0, resetTime: /* @__PURE__ */ new Date() };
this.resetClient(client);
}
this.current.set(key, client);
return client;
}
/**
* Move current clients to previous, create a new map for current.
*
* This function is called every `windowMs`.
*/
clearExpired() {
this.previous = this.current;
this.current = /* @__PURE__ */ new Map();
}
};
// source/rate-limit.ts
var import_node_net3 = require("node:net");
// source/headers.ts
var import_node_buffer = require("node:buffer");
var import_node_crypto = require("node:crypto");
var SUPPORTED_DRAFT_VERSIONS = [
"draft-6",
"draft-7",
"draft-8"
];
var getResetSeconds = (windowMs, resetTime) => {
let resetSeconds;
if (resetTime) {
const deltaSeconds = Math.ceil((resetTime.getTime() - Date.now()) / 1e3);
resetSeconds = Math.max(0, deltaSeconds);
} else {
resetSeconds = Math.ceil(windowMs / 1e3);
}
return resetSeconds;
};
var getPartitionKey = (key) => {
const hash = (0, import_node_crypto.createHash)("sha256");
hash.update(key);
const partitionKey = hash.digest("hex").slice(0, 12);
return import_node_buffer.Buffer.from(partitionKey).toString("base64");
};
var setLegacyHeaders = (response, info) => {
if (response.headersSent) return;
response.setHeader("X-RateLimit-Limit", info.limit.toString());
response.setHeader("X-RateLimit-Remaining", info.remaining.toString());
if (info.resetTime instanceof Date) {
response.setHeader("Date", (/* @__PURE__ */ new Date()).toUTCString());
response.setHeader(
"X-RateLimit-Reset",
Math.ceil(info.resetTime.getTime() / 1e3).toString()
);
}
};
var setDraft6Headers = (response, info, windowMs) => {
if (response.headersSent) return;
const windowSeconds = Math.ceil(windowMs / 1e3);
const resetSeconds = getResetSeconds(windowMs, info.resetTime);
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
response.setHeader("RateLimit-Limit", info.limit.toString());
response.setHeader("RateLimit-Remaining", info.remaining.toString());
if (typeof resetSeconds === "number")
response.setHeader("RateLimit-Reset", resetSeconds.toString());
};
var setDraft7Headers = (response, info, windowMs) => {
if (response.headersSent) return;
const windowSeconds = Math.ceil(windowMs / 1e3);
const resetSeconds = getResetSeconds(windowMs, info.resetTime);
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
response.setHeader(
"RateLimit",
`limit=${info.limit}, remaining=${info.remaining}, reset=${resetSeconds}`
);
};
var setDraft8Headers = (response, info, windowMs, name, key) => {
if (response.headersSent) return;
const windowSeconds = Math.ceil(windowMs / 1e3);
const resetSeconds = getResetSeconds(windowMs, info.resetTime);
const partitionKey = getPartitionKey(key);
const header = `r=${info.remaining}; t=${resetSeconds}`;
const policy = `q=${info.limit}; w=${windowSeconds}; pk=:${partitionKey}:`;
response.append("RateLimit", `"${name}"; ${header}`);
response.append("RateLimit-Policy", `"${name}"; ${policy}`);
};
var setRetryAfterHeader = (response, info, windowMs) => {
if (response.headersSent) return;
const resetSeconds = getResetSeconds(windowMs, info.resetTime);
response.setHeader("Retry-After", resetSeconds.toString());
};
// source/utils.ts
var omitUndefinedProperties = (passedOptions) => {
const omittedOptions = {};
for (const k of Object.keys(passedOptions)) {
const key = k;
if (passedOptions[key] !== void 0) {
omittedOptions[key] = passedOptions[key];
}
}
return omittedOptions;
};
// source/validations.ts
var import_node_net2 = require("node:net");
var ValidationError = class extends Error {
/**
* The code must be a string, in snake case and all capital, that starts with
* the substring `ERR_ERL_`.
*
* The message must be a string, starting with an uppercase character,
* describing the issue in detail.
*/
constructor(code, message) {
const url = `https://express-rate-limit.github.io/${code}/`;
super(`${message} See ${url} for more information.`);
this.name = this.constructor.name;
this.code = code;
this.help = url;
}
};
var ChangeWarning = class extends ValidationError {
};
var usedStores = /* @__PURE__ */ new Set();
var singleCountKeys = /* @__PURE__ */ new WeakMap();
var validations = {
enabled: {
default: true
},
// Should be EnabledValidations type, but that's a circular reference
disable() {
for (const k of Object.keys(this.enabled)) this.enabled[k] = false;
},
/**
* Checks whether the IP address is valid, and that it does not have a port
* number in it.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
*
* @param ip {string | undefined} - The IP address provided by Express as request.ip.
*
* @returns {void}
*/
ip(ip) {
if (ip === void 0) {
throw new ValidationError(
"ERR_ERL_UNDEFINED_IP_ADDRESS",
`An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.`
);
}
if (!(0, import_node_net2.isIP)(ip)) {
throw new ValidationError(
"ERR_ERL_INVALID_IP_ADDRESS",
`An invalid 'request.ip' (${ip}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`
);
}
},
/**
* Makes sure the trust proxy setting is not set to `true`.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
trustProxy(request) {
if (request.app.get("trust proxy") === true) {
throw new ValidationError(
"ERR_ERL_PERMISSIVE_TRUST_PROXY",
`The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.`
);
}
},
/**
* Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
* header is present.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
xForwardedForHeader(request) {
if (request.headers["x-forwarded-for"] && request.app.get("trust proxy") === false) {
throw new ValidationError(
"ERR_ERL_UNEXPECTED_X_FORWARDED_FOR",
`The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.`
);
}
},
/**
* Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
forwardedHeader(request) {
if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {
throw new ValidationError(
"ERR_ERL_FORWARDED_HEADER",
`The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`
);
}
},
/**
* Ensures totalHits value from store is a positive integer.
*
* @param hits {any} - The `totalHits` returned by the store.
*/
positiveHits(hits) {
if (typeof hits !== "number" || hits < 1 || hits !== Math.round(hits)) {
throw new ValidationError(
"ERR_ERL_INVALID_HITS",
`The totalHits value returned from the store must be a positive integer, got ${hits}`
);
}
},
/**
* Ensures a single store instance is not used with multiple express-rate-limit instances
*/
unsharedStore(store) {
if (usedStores.has(store)) {
const maybeUniquePrefix = store?.localKeys ? "" : " (with a unique prefix)";
throw new ValidationError(
"ERR_ERL_STORE_REUSE",
`A Store instance must not be shared across multiple rate limiters. Create a new instance of ${store.constructor.name}${maybeUniquePrefix} for each limiter instead.`
);
}
usedStores.add(store);
},
/**
* Ensures a given key is incremented only once per request.
*
* @param request {Request} - The Express request object.
* @param store {Store} - The store class.
* @param key {string} - The key used to store the client's hit count.
*
* @returns {void}
*/
singleCount(request, store, key) {
let storeKeys = singleCountKeys.get(request);
if (!storeKeys) {
storeKeys = /* @__PURE__ */ new Map();
singleCountKeys.set(request, storeKeys);
}
const storeKey = store.localKeys ? store : store.constructor.name;
let keys = storeKeys.get(storeKey);
if (!keys) {
keys = [];
storeKeys.set(storeKey, keys);
}
const prefixedKey = `${store.prefix ?? ""}${key}`;
if (keys.includes(prefixedKey)) {
throw new ValidationError(
"ERR_ERL_DOUBLE_COUNT",
`The hit count for ${key} was incremented more than once for a single request.`
);
}
keys.push(prefixedKey);
},
/**
* Warns the user that the behaviour for `max: 0` / `limit: 0` is
* changing in the next major release.
*
* @param limit {number} - The maximum number of hits per client.
*
* @returns {void}
*/
limit(limit) {
if (limit === 0) {
throw new ChangeWarning(
"WRN_ERL_MAX_ZERO",
"Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7"
);
}
},
/**
* Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
* and will be removed in the next major release.
*
* @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
*
* @returns {void}
*/
draftPolliHeaders(draft_polli_ratelimit_headers) {
if (draft_polli_ratelimit_headers) {
throw new ChangeWarning(
"WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS",
`The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.`
);
}
},
/**
* Warns the user that the `onLimitReached` option is deprecated and
* will be removed in the next major release.
*
* @param onLimitReached {any | undefined} - The maximum number of hits per client.
*
* @returns {void}
*/
onLimitReached(onLimitReached) {
if (onLimitReached) {
throw new ChangeWarning(
"WRN_ERL_DEPRECATED_ON_LIMIT_REACHED",
"The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7."
);
}
},
/**
* Warns the user when an invalid/unsupported version of the draft spec is passed.
*
* @param version {any | undefined} - The version passed by the user.
*
* @returns {void}
*/
headersDraftVersion(version) {
if (typeof version !== "string" || // @ts-expect-error This is fine. If version is not in the array, it will just return false.
!SUPPORTED_DRAFT_VERSIONS.includes(version)) {
const versionString = SUPPORTED_DRAFT_VERSIONS.join(", ");
throw new ValidationError(
"ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION",
`standardHeaders: only the following versions of the IETF draft specification are supported: ${versionString}.`
);
}
},
/**
* Warns the user when the selected headers option requires a reset time but
* the store does not provide one.
*
* @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.
*
* @returns {void}
*/
headersResetTime(resetTime) {
if (!resetTime) {
throw new ValidationError(
"ERR_ERL_HEADERS_NO_RESET",
`standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.`
);
}
},
knownOptions(passedOptions) {
if (!passedOptions) return;
const optionsMap = {
windowMs: true,
limit: true,
message: true,
statusCode: true,
legacyHeaders: true,
standardHeaders: true,
identifier: true,
requestPropertyName: true,
skipFailedRequests: true,
skipSuccessfulRequests: true,
keyGenerator: true,
ipv6Subnet: true,
handler: true,
skip: true,
requestWasSuccessful: true,
store: true,
validate: true,
headers: true,
max: true,
passOnStoreError: true
};
const validOptions = Object.keys(optionsMap).concat(
"draft_polli_ratelimit_headers",
// not a valid option anymore, but we have a more specific check for this one, so don't warn for it here
// from express-slow-down - https://github.com/express-rate-limit/express-slow-down/blob/main/source/types.ts#L65
"delayAfter",
"delayMs",
"maxDelayMs"
);
for (const key of Object.keys(passedOptions)) {
if (!validOptions.includes(key)) {
throw new ValidationError(
"ERR_ERL_UNKNOWN_OPTION",
`Unexpected configuration option: ${key}`
// todo: suggest a valid option with a short levenstein distance?
);
}
}
},
/**
* Checks the options.validate setting to ensure that only recognized
* validations are enabled or disabled.
*
* If any unrecognized values are found, an error is logged that
* includes the list of supported validations.
*/
validationsConfig() {
const supportedValidations = Object.keys(this).filter(
(k) => !["enabled", "disable"].includes(k)
);
supportedValidations.push("default");
for (const key of Object.keys(this.enabled)) {
if (!supportedValidations.includes(key)) {
throw new ValidationError(
"ERR_ERL_UNKNOWN_VALIDATION",
`options.validate.${key} is not recognized. Supported validate options are: ${supportedValidations.join(
", "
)}.`
);
}
}
},
/**
* Checks to see if the instance was created inside of a request handler,
* which would prevent it from working correctly, with the default memory
* store (or any other store with localKeys.)
*/
creationStack(store) {
const { stack } = new Error(
"express-rate-limit validation check (set options.validate.creationStack=false to disable)"
);
if (stack?.includes("Layer.handle [as handle_request]") || // express v4
stack?.includes("Layer.handleRequest")) {
if (!store.localKeys) {
throw new ValidationError(
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
"express-rate-limit instance should *usually* be created at app initialization, not when responding to a request."
);
}
throw new ValidationError(
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
"express-rate-limit instance should be created at app initialization, not when responding to a request."
);
}
},
ipv6Subnet(ipv6Subnet) {
if (ipv6Subnet === false) {
return;
}
if (!Number.isInteger(ipv6Subnet) || ipv6Subnet < 32 || ipv6Subnet > 64) {
throw new ValidationError(
"ERR_ERL_IPV6_SUBNET",
`Unexpected ipv6Subnet value: ${ipv6Subnet}. Expected an integer between 32 and 64 (usually 48-64).`
);
}
},
ipv6SubnetOrKeyGenerator(options) {
if (options.ipv6Subnet !== void 0 && options.keyGenerator) {
throw new ValidationError(
"ERR_ERL_IPV6SUBNET_OR_KEYGENERATOR",
`Incompatible options: the 'ipv6Subnet' option is ignored when a custom 'keyGenerator' function is also set.`
);
}
},
keyGeneratorIpFallback(keyGenerator) {
if (!keyGenerator) {
return;
}
const src = keyGenerator.toString();
if ((src.includes("req.ip") || src.includes("request.ip")) && !src.includes("ipKeyGenerator")) {
throw new ValidationError(
"ERR_ERL_KEY_GEN_IPV6",
"Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
);
}
},
/**
* Checks to see if the window duration is greater than 2^32 - 1. This is only
* called by the default MemoryStore, since it uses Node's setInterval method.
*
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
*/
windowMs(windowMs) {
const SET_TIMEOUT_MAX = 2 ** 31 - 1;
if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {
throw new ValidationError(
"ERR_ERL_WINDOW_MS",
`Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`
);
}
}
};
var getValidations = (_enabled) => {
let enabled;
if (typeof _enabled === "boolean") {
enabled = {
default: _enabled
};
} else {
enabled = {
default: true,
..._enabled
};
}
const wrappedValidations = { enabled };
for (const [name, validation] of Object.entries(validations)) {
if (typeof validation === "function")
wrappedValidations[name] = (...args) => {
if (!(enabled[name] ?? enabled.default)) {
return;
}
try {
;
validation.apply(
wrappedValidations,
args
);
} catch (error) {
if (error instanceof ChangeWarning) console.warn(error);
else console.error(error);
}
};
}
return wrappedValidations;
};
// source/rate-limit.ts
var isLegacyStore = (store) => (
// Check that `incr` exists but `increment` does not - store authors might want
// to keep both around for backwards compatibility.
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 legacyStore.decrement(key);
}
async resetKey(key) {
return legacyStore.resetKey(key);
}
/* istanbul ignore next */
async resetAll() {
if (typeof legacyStore.resetAll === "function")
return legacyStore.resetAll();
}
}
return new PromisifiedStore();
};
var getOptionsFromConfig = (config) => {
const { validations: validations2, ...directlyPassableEntries } = config;
return {
...directlyPassableEntries,
validate: validations2.enabled
};
};
var parseOptions = (passedOptions) => {
const notUndefinedOptions = omitUndefinedProperties(passedOptions);
const validations2 = getValidations(notUndefinedOptions?.validate ?? true);
validations2.validationsConfig();
validations2.knownOptions(passedOptions);
validations2.draftPolliHeaders(
// @ts-expect-error see the note above.
notUndefinedOptions.draft_polli_ratelimit_headers
);
validations2.onLimitReached(notUndefinedOptions.onLimitReached);
if (notUndefinedOptions.ipv6Subnet !== void 0 && typeof notUndefinedOptions.ipv6Subnet !== "function") {
validations2.ipv6Subnet(notUndefinedOptions.ipv6Subnet);
}
validations2.keyGeneratorIpFallback(notUndefinedOptions.keyGenerator);
validations2.ipv6SubnetOrKeyGenerator(notUndefinedOptions);
let standardHeaders = notUndefinedOptions.standardHeaders ?? false;
if (standardHeaders === true) standardHeaders = "draft-6";
const config = {
windowMs: 60 * 1e3,
limit: passedOptions.max ?? 5,
// `max` is deprecated, but support it anyways.
message: "Too many requests, please try again later.",
statusCode: 429,
legacyHeaders: passedOptions.headers ?? true,
identifier(request, _response) {
let duration = "";
const property = config.requestPropertyName;
const { limit } = request[property];
const seconds = config.windowMs / 1e3;
const minutes = config.windowMs / (1e3 * 60);
const hours = config.windowMs / (1e3 * 60 * 60);
const days = config.windowMs / (1e3 * 60 * 60 * 24);
if (seconds < 60) duration = `${seconds}sec`;
else if (minutes < 60) duration = `${minutes}min`;
else if (hours < 24) duration = `${hours}hr${hours > 1 ? "s" : ""}`;
else duration = `${days}day${days > 1 ? "s" : ""}`;
return `${limit}-in-${duration}`;
},
requestPropertyName: "rateLimit",
skipFailedRequests: false,
skipSuccessfulRequests: false,
requestWasSuccessful: (_request, response) => response.statusCode < 400,
skip: (_request, _response) => false,
async keyGenerator(request, response) {
validations2.ip(request.ip);
validations2.trustProxy(request);
validations2.xForwardedForHeader(request);
validations2.forwardedHeader(request);
const ip = request.ip;
let subnet = 56;
if ((0, import_node_net3.isIPv6)(ip)) {
subnet = typeof config.ipv6Subnet === "function" ? await config.ipv6Subnet(request, response) : config.ipv6Subnet;
if (typeof config.ipv6Subnet === "function")
validations2.ipv6Subnet(subnet);
}
return ipKeyGenerator(ip, subnet);
},
ipv6Subnet: 56,
async handler(request, response, _next, _optionsUsed) {
response.status(config.statusCode);
const message = typeof config.message === "function" ? await config.message(
request,
response
) : config.message;
if (!response.writableEnded) response.send(message);
},
passOnStoreError: false,
// Allow the default options to be overridden by the passed options.
...notUndefinedOptions,
// `standardHeaders` is resolved into a draft version above, use that.
standardHeaders,
// Note that this field is declared after the user's options are spread in,
// so that this field doesn't get overridden with an un-promisified store!
store: promisifyStore(
notUndefinedOptions.store ?? new MemoryStore(validations2)
),
// Print an error to the console if a few known misconfigurations are detected.
validations: validations2
};
if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || config.store.resetAll !== void 0 && typeof config.store.resetAll !== "function" || config.store.init !== void 0 && typeof config.store.init !== "function") {
throw new TypeError(
"An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface."
);
}
return config;
};
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 config = parseOptions(passedOptions ?? {});
const options = getOptionsFromConfig(config);
config.validations.creationStack(config.store);
config.validations.unsharedStore(config.store);
if (typeof config.store.init === "function") config.store.init(options);
const middleware = handleAsyncErrors(
async (request, response, next) => {
const skip = await config.skip(request, response);
if (skip) {
next();
return;
}
const augmentedRequest = request;
const key = await config.keyGenerator(request, response);
let totalHits = 0;
let resetTime;
try {
const incrementResult = await config.store.increment(key);
totalHits = incrementResult.totalHits;
resetTime = incrementResult.resetTime;
} catch (error) {
if (config.passOnStoreError) {
console.error(
"express-rate-limit: error from store, allowing request without rate-limiting.",
error
);
next();
return;
}
throw error;
}
config.validations.positiveHits(totalHits);
config.validations.singleCount(request, config.store, key);
const retrieveLimit = typeof config.limit === "function" ? config.limit(request, response) : config.limit;
const limit = await retrieveLimit;
config.validations.limit(limit);
const info = {
limit,
used: totalHits,
remaining: Math.max(limit - totalHits, 0),
resetTime,
key
};
Object.defineProperty(info, "current", {
configurable: false,
enumerable: false,
value: totalHits
});
augmentedRequest[config.requestPropertyName] = info;
if (config.legacyHeaders && !response.headersSent) {
setLegacyHeaders(response, info);
}
if (config.standardHeaders && !response.headersSent) {
switch (config.standardHeaders) {
case "draft-6": {
setDraft6Headers(response, info, config.windowMs);
break;
}
case "draft-7": {
config.validations.headersResetTime(info.resetTime);
setDraft7Headers(response, info, config.windowMs);
break;
}
case "draft-8": {
const retrieveName = typeof config.identifier === "function" ? config.identifier(request, response) : config.identifier;
const name = await retrieveName;
config.validations.headersResetTime(info.resetTime);
setDraft8Headers(response, info, config.windowMs, name, key);
break;
}
default: {
config.validations.headersDraftVersion(config.standardHeaders);
break;
}
}
}
if (config.skipFailedRequests || config.skipSuccessfulRequests) {
let decremented = false;
const decrementKey = async () => {
if (!decremented) {
await config.store.decrement(key);
decremented = true;
}
};
if (config.skipFailedRequests) {
response.on("finish", async () => {
if (!await config.requestWasSuccessful(request, response))
await decrementKey();
});
response.on("close", async () => {
if (!response.writableEnded) await decrementKey();
});
response.on("error", async () => {
await decrementKey();
});
}
if (config.skipSuccessfulRequests) {
response.on("finish", async () => {
if (await config.requestWasSuccessful(request, response))
await decrementKey();
});
}
}
config.validations.disable();
if (totalHits > limit) {
if (config.legacyHeaders || config.standardHeaders) {
setRetryAfterHeader(response, info, config.windowMs);
}
config.handler(request, response, next, options);
return;
}
next();
}
);
const getThrowFn = () => {
throw new Error("The current store does not support the get/getKey method");
};
middleware.resetKey = config.store.resetKey.bind(config.store);
middleware.getKey = typeof config.store.get === "function" ? config.store.get.bind(config.store) : getThrowFn;
return middleware;
};
var rate_limit_default = rateLimit;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
MemoryStore,
ipKeyGenerator,
rateLimit
});
module.exports = Object.assign(rateLimit, module.exports);

642
node_modules/express-rate-limit/dist/index.d.cts generated vendored Normal file
View File

@@ -0,0 +1,642 @@
// Generated by dts-bundle-generator v8.1.2
import { NextFunction, Request, RequestHandler, Response } from 'express';
/**
* Returns the IP address itself for IPv4, or a CIDR-notation subnet for IPv6.
*
* If you write a custom keyGenerator that allows a fallback to IP address for
* unauthenticated users, return ipKeyGenerator(req.ip) rather than just req.ip.
*
* For more information, {@see Options.ipv6Subnet}.
*
* @param ip {string} - The IP address to process, usually request.ip.
* @param ipv6Subnet {number | false} - The subnet mask for IPv6 addresses.
*
* @returns {string} - The key generated from the IP address
*
* @public
*/
export declare function ipKeyGenerator(ip: string, ipv6Subnet?: number | false): string;
declare const SUPPORTED_DRAFT_VERSIONS: readonly [
"draft-6",
"draft-7",
"draft-8"
];
declare const validations: {
enabled: {
[key: string]: boolean;
};
disable(): void;
/**
* Checks whether the IP address is valid, and that it does not have a port
* number in it.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
*
* @param ip {string | undefined} - The IP address provided by Express as request.ip.
*
* @returns {void}
*/
ip(ip: string | undefined): void;
/**
* Makes sure the trust proxy setting is not set to `true`.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
trustProxy(request: Request): void;
/**
* Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
* header is present.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
xForwardedForHeader(request: Request): void;
/**
* Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
forwardedHeader(request: Request): void;
/**
* Ensures totalHits value from store is a positive integer.
*
* @param hits {any} - The `totalHits` returned by the store.
*/
positiveHits(hits: any): void;
/**
* Ensures a single store instance is not used with multiple express-rate-limit instances
*/
unsharedStore(store: Store): void;
/**
* Ensures a given key is incremented only once per request.
*
* @param request {Request} - The Express request object.
* @param store {Store} - The store class.
* @param key {string} - The key used to store the client's hit count.
*
* @returns {void}
*/
singleCount(request: Request, store: Store, key: string): void;
/**
* Warns the user that the behaviour for `max: 0` / `limit: 0` is
* changing in the next major release.
*
* @param limit {number} - The maximum number of hits per client.
*
* @returns {void}
*/
limit(limit: number): void;
/**
* Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
* and will be removed in the next major release.
*
* @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
*
* @returns {void}
*/
draftPolliHeaders(draft_polli_ratelimit_headers?: any): void;
/**
* Warns the user that the `onLimitReached` option is deprecated and
* will be removed in the next major release.
*
* @param onLimitReached {any | undefined} - The maximum number of hits per client.
*
* @returns {void}
*/
onLimitReached(onLimitReached?: any): void;
/**
* Warns the user when an invalid/unsupported version of the draft spec is passed.
*
* @param version {any | undefined} - The version passed by the user.
*
* @returns {void}
*/
headersDraftVersion(version?: any): void;
/**
* Warns the user when the selected headers option requires a reset time but
* the store does not provide one.
*
* @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.
*
* @returns {void}
*/
headersResetTime(resetTime?: Date): void;
knownOptions(passedOptions?: Partial<Options>): void;
/**
* Checks the options.validate setting to ensure that only recognized
* validations are enabled or disabled.
*
* If any unrecognized values are found, an error is logged that
* includes the list of supported validations.
*/
validationsConfig(): void;
/**
* Checks to see if the instance was created inside of a request handler,
* which would prevent it from working correctly, with the default memory
* store (or any other store with localKeys.)
*/
creationStack(store: Store): void;
ipv6Subnet(ipv6Subnet?: any): void;
ipv6SubnetOrKeyGenerator(options: Partial<Options>): void;
keyGeneratorIpFallback(keyGenerator?: ValueDeterminingMiddleware<string>): void;
/**
* Checks to see if the window duration is greater than 2^32 - 1. This is only
* called by the default MemoryStore, since it uses Node's setInterval method.
*
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
*/
windowMs(windowMs: number): void;
};
export type Validations = typeof validations;
/**
* Callback that fires when a client's hit counter is incremented.
*
* @param error {Error | undefined} - The error that occurred, if any.
* @param totalHits {number} - The number of hits for that client so far.
* @param resetTime {Date | undefined} - The time when the counter resets.
*/
export type IncrementCallback = (error: Error | undefined, totalHits: number, resetTime: Date | undefined) => void;
/**
* Method (in the form of middleware) to generate/retrieve a value based on the
* incoming request.
*
* @param request {Request} - The Express request object.
* @param response {Response} - The Express response object.
*
* @returns {T} - The value needed.
*/
export type ValueDeterminingMiddleware<T> = (request: Request, response: Response) => T | Promise<T>;
/**
* Express request handler that sends back a response when a client is
* rate-limited.
*
* @param request {Request} - The Express request object.
* @param response {Response} - The Express response object.
* @param next {NextFunction} - The Express `next` function, can be called to skip responding.
* @param optionsUsed {Options} - The options used to set up the middleware.
*/
export type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
/**
* Event callback that is triggered on a client's first request that exceeds the limit
* but not for subsequent requests. May be used for logging, etc. Should *not*
* send a response.
*
* @param request {Request} - The Express request object.
* @param response {Response} - The Express response object.
* @param optionsUsed {Options} - The options used to set up the middleware.
*/
export type RateLimitReachedEventHandler = (request: Request, response: Response, optionsUsed: Options) => void;
/**
* Data returned from the `Store` when a client's hit counter is incremented.
*
* @property totalHits {number} - The number of hits for that client so far.
* @property resetTime {Date | undefined} - The time when the counter resets.
*/
export type ClientRateLimitInfo = {
totalHits: number;
resetTime: Date | undefined;
};
export type IncrementResponse = ClientRateLimitInfo;
/**
* A modified Express request handler with the rate limit functions.
*/
export type RateLimitRequestHandler = RequestHandler & {
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
resetKey: (key: string) => void;
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*/
getKey: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
};
/**
* An interface that all hit counter stores must implement.
*
* @deprecated 6.x - Implement the `Store` interface instead.
*/
export type LegacyStore = {
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
* @param callback {IncrementCallback} - The callback to call once the counter is incremented.
*/
incr: (key: string, callback: IncrementCallback) => void;
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
decrement: (key: string) => void;
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
resetKey: (key: string) => void;
/**
* Method to reset everyone's hit counter.
*/
resetAll?: () => void;
};
/**
* An interface that all hit counter stores must implement.
*/
export type Store = {
/**
* Method that initializes the store, and has access to the options passed to
* the middleware too.
*
* @param options {Options} - The options used to setup the middleware.
*/
init?: (options: Options) => void;
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*/
get?: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @returns {IncrementResponse | undefined} - The number of hits and reset time for that client.
*/
increment: (key: string) => Promise<IncrementResponse> | IncrementResponse;
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
decrement: (key: string) => Promise<void> | void;
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
resetKey: (key: string) => Promise<void> | void;
/**
* Method to reset everyone's hit counter.
*/
resetAll?: () => Promise<void> | void;
/**
* Method to shutdown the store, stop timers, and release all resources.
*/
shutdown?: () => Promise<void> | void;
/**
* Flag to indicate that keys incremented in one instance of this store can
* not affect other instances. Typically false if a database is used, true for
* MemoryStore.
*
* Used to help detect double-counting misconfigurations.
*/
localKeys?: boolean;
/**
* Optional value that the store prepends to keys
*
* Used by the double-count check to avoid false-positives when a key is counted
* twice, but with different prefixes.
*/
prefix?: string;
};
export type DraftHeadersVersion = (typeof SUPPORTED_DRAFT_VERSIONS)[number];
/**
* Validate configuration object for enabling or disabling specific validations.
*
* The keys must also be keys in the validations object, except `enable`, `disable`,
* and `default`.
*/
export type EnabledValidations = {
[key in keyof Omit<Validations, "enabled" | "disable"> | "default"]?: boolean;
};
/**
* The configuration options for the rate limiter.
*/
export type Options = {
/**
* How long we should remember the requests.
*
* Defaults to `60000` ms (= 1 minute).
*/
windowMs: number;
/**
* The maximum number of connections to allow during the `window` before
* rate limiting the client.
*
* Can be the limit itself as a number or express middleware that parses
* the request and then figures out the limit.
*
* Defaults to `5`.
*/
limit: number | ValueDeterminingMiddleware<number>;
/**
* The response body to send back when a client is rate limited.
*
* Defaults to `'Too many requests, please try again later.'`
*/
message: any | ValueDeterminingMiddleware<any>;
/**
* The HTTP status code to send back when a client is rate limited.
*
* Defaults to `HTTP 429 Too Many Requests` (RFC 6585).
*/
statusCode: number;
/**
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
* of requests.
*
* Defaults to `true` (for backward compatibility).
*/
legacyHeaders: boolean;
/**
* Whether to enable support for the standardized rate limit headers (`RateLimit-*`).
*
* Defaults to `false` (for backward compatibility, but its use is recommended).
*/
standardHeaders: boolean | DraftHeadersVersion;
/**
* The name used to identify the quota policy in the `RateLimit` headers as per
* the 8th draft of the IETF specification.
*
* Defaults to `{limit}-in-{window}`.
*/
identifier: string | ValueDeterminingMiddleware<string>;
/**
* The name of the property on the request object to store the rate limit info.
*
* Defaults to `rateLimit`.
*/
requestPropertyName: string;
/**
* If `true`, the library will (by default) skip all requests that have a 4XX
* or 5XX status.
*
* Defaults to `false`.
*/
skipFailedRequests: boolean;
/**
* If `true`, the library will (by default) skip all requests that have a
* status code less than 400.
*
* Defaults to `false`.
*/
skipSuccessfulRequests: boolean;
/**
* Method to generate custom identifiers for clients.
*
* By default, the client's IP address is used.
*/
keyGenerator: ValueDeterminingMiddleware<string>;
/**
* IPv6 subnet mask applied to IPv6 addresses in the default keyGenerator.
*
* Default is 56. The valid range is technically 1-128 but the value should
* generally be in the 32-64 range.
*
* Smaller numbers are more aggressive, larger numbers are more lenient. Try
* bumping to 60 or 64 if you see evidence of users being blocked incorrectly.
*
* May also be set to a function that returns a number based on the request.
*
* See the documentation for more info:
* https://express-rate-limit.mintlify.app/reference/configuration#ipv6subnet.
*/
ipv6Subnet: 64 | 60 | 56 | 52 | 50 | 48 | 32 | number | ValueDeterminingMiddleware<number> | false;
/**
* Express request handler that sends back a response when a client is
* rate-limited.
*
* By default, sends back the `statusCode` and `message` set via the options.
*/
handler: RateLimitExceededEventHandler;
/**
* Method (in the form of middleware) to determine whether or not this request
* counts towards a client's quota.
*
* By default, skips no requests.
*/
skip: ValueDeterminingMiddleware<boolean>;
/**
* Method to determine whether or not the request counts as 'successful'. Used
* when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
*
* By default, requests with a response status code less than 400 are considered
* successful.
*/
requestWasSuccessful: ValueDeterminingMiddleware<boolean>;
/**
* The `Store` to use to store the hit count for each client.
*
* By default, the built-in `MemoryStore` will be used.
*/
store: Store | LegacyStore;
/**
* The list of validation checks that should run.
*/
validate: boolean | EnabledValidations;
/**
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
* of requests.
*
* @deprecated 6.x - This option was renamed to `legacyHeaders`.
*/
headers?: boolean;
/**
* The maximum number of connections to allow during the `window` before
* rate limiting the client.
*
* Can be the limit itself as a number or express middleware that parses
* the request and then figures out the limit.
*
* @deprecated 7.x - This option was renamed to `limit`. However, it will not
* be removed from the library in the foreseeable future.
*/
max?: number | ValueDeterminingMiddleware<number>;
/**
* If the Store generates an error, allow the request to pass.
*/
passOnStoreError: boolean;
};
/**
* The extended request object that includes information about the client's
* rate limit.
*/
export type AugmentedRequest = Request & {
[key: string]: RateLimitInfo;
};
/**
* The rate limit related information for each client included in the
* Express request object.
*/
export type RateLimitInfo = {
limit: number;
used: number;
remaining: number;
resetTime: Date | undefined;
key: string;
};
/**
* The record that stores information about a client - namely, how many times
* they have hit the endpoint, and when their hit count resets.
*
* Similar to `ClientRateLimitInfo`, except `resetTime` is a compulsory field.
*/
export type Client = {
totalHits: number;
resetTime: Date;
};
/**
* A `Store` that stores the hit count for each client in memory.
*
* @public
*/
export declare class MemoryStore implements Store {
private validations?;
/**
* The duration of time before which all hit counts are reset (in milliseconds).
*/
windowMs: number;
/**
* These two maps store usage (requests) and reset time by key (for example, IP
* addresses or API keys).
*
* They are split into two to avoid having to iterate through the entire set to
* determine which ones need reset. Instead, `Client`s are moved from `previous`
* to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
* left in `previous`, i.e., those that have not made any recent requests, are
* known to be expired and can be deleted in bulk.
*/
previous: Map<string, Client>;
current: Map<string, Client>;
/**
* A reference to the active timer.
*/
interval?: NodeJS.Timeout;
/**
* Confirmation that the keys incremented in once instance of MemoryStore
* cannot affect other instances.
*/
localKeys: boolean;
constructor(validations?: Validations | undefined);
/**
* Method that initializes the store.
*
* @param options {Options} - The options used to setup the middleware.
*/
init(options: Options): void;
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
*
* @public
*/
get(key: string): Promise<ClientRateLimitInfo | undefined>;
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*
* @public
*/
increment(key: string): Promise<ClientRateLimitInfo>;
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
decrement(key: string): Promise<void>;
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
resetKey(key: string): Promise<void>;
/**
* Method to reset everyone's hit counter.
*
* @public
*/
resetAll(): Promise<void>;
/**
* Method to stop the timer (if currently running) and prevent any memory
* leaks.
*
* @public
*/
shutdown(): void;
/**
* Recycles a client by setting its hit count to zero, and reset time to
* `windowMs` milliseconds from now.
*
* NOT to be confused with `#resetKey()`, which removes a client from both the
* `current` and `previous` maps.
*
* @param client {Client} - The client to recycle.
* @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
*
* @return {Client} - The modified client that was passed in, to allow for chaining.
*/
private resetClient;
/**
* Retrieves or creates a client, given a key. Also ensures that the client being
* returned is in the `current` map.
*
* @param key {string} - The key under which the client is (or is to be) stored.
*
* @returns {Client} - The requested client.
*/
private getClient;
/**
* Move current clients to previous, create a new map for current.
*
* This function is called every `windowMs`.
*/
private clearExpired;
}
/**
*
* Create an instance of IP rate-limiting middleware for Express.
*
* @param passedOptions {Options} - Options to configure the rate limiter.
*
* @returns {RateLimitRequestHandler} - The middleware that rate-limits clients based on your configuration.
*
* @public
*/
export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
export {
rateLimit as default,
};
export {};

642
node_modules/express-rate-limit/dist/index.d.mts generated vendored Normal file
View File

@@ -0,0 +1,642 @@
// Generated by dts-bundle-generator v8.1.2
import { NextFunction, Request, RequestHandler, Response } from 'express';
/**
* Returns the IP address itself for IPv4, or a CIDR-notation subnet for IPv6.
*
* If you write a custom keyGenerator that allows a fallback to IP address for
* unauthenticated users, return ipKeyGenerator(req.ip) rather than just req.ip.
*
* For more information, {@see Options.ipv6Subnet}.
*
* @param ip {string} - The IP address to process, usually request.ip.
* @param ipv6Subnet {number | false} - The subnet mask for IPv6 addresses.
*
* @returns {string} - The key generated from the IP address
*
* @public
*/
export declare function ipKeyGenerator(ip: string, ipv6Subnet?: number | false): string;
declare const SUPPORTED_DRAFT_VERSIONS: readonly [
"draft-6",
"draft-7",
"draft-8"
];
declare const validations: {
enabled: {
[key: string]: boolean;
};
disable(): void;
/**
* Checks whether the IP address is valid, and that it does not have a port
* number in it.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
*
* @param ip {string | undefined} - The IP address provided by Express as request.ip.
*
* @returns {void}
*/
ip(ip: string | undefined): void;
/**
* Makes sure the trust proxy setting is not set to `true`.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
trustProxy(request: Request): void;
/**
* Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
* header is present.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
xForwardedForHeader(request: Request): void;
/**
* Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
forwardedHeader(request: Request): void;
/**
* Ensures totalHits value from store is a positive integer.
*
* @param hits {any} - The `totalHits` returned by the store.
*/
positiveHits(hits: any): void;
/**
* Ensures a single store instance is not used with multiple express-rate-limit instances
*/
unsharedStore(store: Store): void;
/**
* Ensures a given key is incremented only once per request.
*
* @param request {Request} - The Express request object.
* @param store {Store} - The store class.
* @param key {string} - The key used to store the client's hit count.
*
* @returns {void}
*/
singleCount(request: Request, store: Store, key: string): void;
/**
* Warns the user that the behaviour for `max: 0` / `limit: 0` is
* changing in the next major release.
*
* @param limit {number} - The maximum number of hits per client.
*
* @returns {void}
*/
limit(limit: number): void;
/**
* Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
* and will be removed in the next major release.
*
* @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
*
* @returns {void}
*/
draftPolliHeaders(draft_polli_ratelimit_headers?: any): void;
/**
* Warns the user that the `onLimitReached` option is deprecated and
* will be removed in the next major release.
*
* @param onLimitReached {any | undefined} - The maximum number of hits per client.
*
* @returns {void}
*/
onLimitReached(onLimitReached?: any): void;
/**
* Warns the user when an invalid/unsupported version of the draft spec is passed.
*
* @param version {any | undefined} - The version passed by the user.
*
* @returns {void}
*/
headersDraftVersion(version?: any): void;
/**
* Warns the user when the selected headers option requires a reset time but
* the store does not provide one.
*
* @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.
*
* @returns {void}
*/
headersResetTime(resetTime?: Date): void;
knownOptions(passedOptions?: Partial<Options>): void;
/**
* Checks the options.validate setting to ensure that only recognized
* validations are enabled or disabled.
*
* If any unrecognized values are found, an error is logged that
* includes the list of supported validations.
*/
validationsConfig(): void;
/**
* Checks to see if the instance was created inside of a request handler,
* which would prevent it from working correctly, with the default memory
* store (or any other store with localKeys.)
*/
creationStack(store: Store): void;
ipv6Subnet(ipv6Subnet?: any): void;
ipv6SubnetOrKeyGenerator(options: Partial<Options>): void;
keyGeneratorIpFallback(keyGenerator?: ValueDeterminingMiddleware<string>): void;
/**
* Checks to see if the window duration is greater than 2^32 - 1. This is only
* called by the default MemoryStore, since it uses Node's setInterval method.
*
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
*/
windowMs(windowMs: number): void;
};
export type Validations = typeof validations;
/**
* Callback that fires when a client's hit counter is incremented.
*
* @param error {Error | undefined} - The error that occurred, if any.
* @param totalHits {number} - The number of hits for that client so far.
* @param resetTime {Date | undefined} - The time when the counter resets.
*/
export type IncrementCallback = (error: Error | undefined, totalHits: number, resetTime: Date | undefined) => void;
/**
* Method (in the form of middleware) to generate/retrieve a value based on the
* incoming request.
*
* @param request {Request} - The Express request object.
* @param response {Response} - The Express response object.
*
* @returns {T} - The value needed.
*/
export type ValueDeterminingMiddleware<T> = (request: Request, response: Response) => T | Promise<T>;
/**
* Express request handler that sends back a response when a client is
* rate-limited.
*
* @param request {Request} - The Express request object.
* @param response {Response} - The Express response object.
* @param next {NextFunction} - The Express `next` function, can be called to skip responding.
* @param optionsUsed {Options} - The options used to set up the middleware.
*/
export type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
/**
* Event callback that is triggered on a client's first request that exceeds the limit
* but not for subsequent requests. May be used for logging, etc. Should *not*
* send a response.
*
* @param request {Request} - The Express request object.
* @param response {Response} - The Express response object.
* @param optionsUsed {Options} - The options used to set up the middleware.
*/
export type RateLimitReachedEventHandler = (request: Request, response: Response, optionsUsed: Options) => void;
/**
* Data returned from the `Store` when a client's hit counter is incremented.
*
* @property totalHits {number} - The number of hits for that client so far.
* @property resetTime {Date | undefined} - The time when the counter resets.
*/
export type ClientRateLimitInfo = {
totalHits: number;
resetTime: Date | undefined;
};
export type IncrementResponse = ClientRateLimitInfo;
/**
* A modified Express request handler with the rate limit functions.
*/
export type RateLimitRequestHandler = RequestHandler & {
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
resetKey: (key: string) => void;
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*/
getKey: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
};
/**
* An interface that all hit counter stores must implement.
*
* @deprecated 6.x - Implement the `Store` interface instead.
*/
export type LegacyStore = {
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
* @param callback {IncrementCallback} - The callback to call once the counter is incremented.
*/
incr: (key: string, callback: IncrementCallback) => void;
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
decrement: (key: string) => void;
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
resetKey: (key: string) => void;
/**
* Method to reset everyone's hit counter.
*/
resetAll?: () => void;
};
/**
* An interface that all hit counter stores must implement.
*/
export type Store = {
/**
* Method that initializes the store, and has access to the options passed to
* the middleware too.
*
* @param options {Options} - The options used to setup the middleware.
*/
init?: (options: Options) => void;
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*/
get?: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @returns {IncrementResponse | undefined} - The number of hits and reset time for that client.
*/
increment: (key: string) => Promise<IncrementResponse> | IncrementResponse;
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
decrement: (key: string) => Promise<void> | void;
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
resetKey: (key: string) => Promise<void> | void;
/**
* Method to reset everyone's hit counter.
*/
resetAll?: () => Promise<void> | void;
/**
* Method to shutdown the store, stop timers, and release all resources.
*/
shutdown?: () => Promise<void> | void;
/**
* Flag to indicate that keys incremented in one instance of this store can
* not affect other instances. Typically false if a database is used, true for
* MemoryStore.
*
* Used to help detect double-counting misconfigurations.
*/
localKeys?: boolean;
/**
* Optional value that the store prepends to keys
*
* Used by the double-count check to avoid false-positives when a key is counted
* twice, but with different prefixes.
*/
prefix?: string;
};
export type DraftHeadersVersion = (typeof SUPPORTED_DRAFT_VERSIONS)[number];
/**
* Validate configuration object for enabling or disabling specific validations.
*
* The keys must also be keys in the validations object, except `enable`, `disable`,
* and `default`.
*/
export type EnabledValidations = {
[key in keyof Omit<Validations, "enabled" | "disable"> | "default"]?: boolean;
};
/**
* The configuration options for the rate limiter.
*/
export type Options = {
/**
* How long we should remember the requests.
*
* Defaults to `60000` ms (= 1 minute).
*/
windowMs: number;
/**
* The maximum number of connections to allow during the `window` before
* rate limiting the client.
*
* Can be the limit itself as a number or express middleware that parses
* the request and then figures out the limit.
*
* Defaults to `5`.
*/
limit: number | ValueDeterminingMiddleware<number>;
/**
* The response body to send back when a client is rate limited.
*
* Defaults to `'Too many requests, please try again later.'`
*/
message: any | ValueDeterminingMiddleware<any>;
/**
* The HTTP status code to send back when a client is rate limited.
*
* Defaults to `HTTP 429 Too Many Requests` (RFC 6585).
*/
statusCode: number;
/**
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
* of requests.
*
* Defaults to `true` (for backward compatibility).
*/
legacyHeaders: boolean;
/**
* Whether to enable support for the standardized rate limit headers (`RateLimit-*`).
*
* Defaults to `false` (for backward compatibility, but its use is recommended).
*/
standardHeaders: boolean | DraftHeadersVersion;
/**
* The name used to identify the quota policy in the `RateLimit` headers as per
* the 8th draft of the IETF specification.
*
* Defaults to `{limit}-in-{window}`.
*/
identifier: string | ValueDeterminingMiddleware<string>;
/**
* The name of the property on the request object to store the rate limit info.
*
* Defaults to `rateLimit`.
*/
requestPropertyName: string;
/**
* If `true`, the library will (by default) skip all requests that have a 4XX
* or 5XX status.
*
* Defaults to `false`.
*/
skipFailedRequests: boolean;
/**
* If `true`, the library will (by default) skip all requests that have a
* status code less than 400.
*
* Defaults to `false`.
*/
skipSuccessfulRequests: boolean;
/**
* Method to generate custom identifiers for clients.
*
* By default, the client's IP address is used.
*/
keyGenerator: ValueDeterminingMiddleware<string>;
/**
* IPv6 subnet mask applied to IPv6 addresses in the default keyGenerator.
*
* Default is 56. The valid range is technically 1-128 but the value should
* generally be in the 32-64 range.
*
* Smaller numbers are more aggressive, larger numbers are more lenient. Try
* bumping to 60 or 64 if you see evidence of users being blocked incorrectly.
*
* May also be set to a function that returns a number based on the request.
*
* See the documentation for more info:
* https://express-rate-limit.mintlify.app/reference/configuration#ipv6subnet.
*/
ipv6Subnet: 64 | 60 | 56 | 52 | 50 | 48 | 32 | number | ValueDeterminingMiddleware<number> | false;
/**
* Express request handler that sends back a response when a client is
* rate-limited.
*
* By default, sends back the `statusCode` and `message` set via the options.
*/
handler: RateLimitExceededEventHandler;
/**
* Method (in the form of middleware) to determine whether or not this request
* counts towards a client's quota.
*
* By default, skips no requests.
*/
skip: ValueDeterminingMiddleware<boolean>;
/**
* Method to determine whether or not the request counts as 'successful'. Used
* when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
*
* By default, requests with a response status code less than 400 are considered
* successful.
*/
requestWasSuccessful: ValueDeterminingMiddleware<boolean>;
/**
* The `Store` to use to store the hit count for each client.
*
* By default, the built-in `MemoryStore` will be used.
*/
store: Store | LegacyStore;
/**
* The list of validation checks that should run.
*/
validate: boolean | EnabledValidations;
/**
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
* of requests.
*
* @deprecated 6.x - This option was renamed to `legacyHeaders`.
*/
headers?: boolean;
/**
* The maximum number of connections to allow during the `window` before
* rate limiting the client.
*
* Can be the limit itself as a number or express middleware that parses
* the request and then figures out the limit.
*
* @deprecated 7.x - This option was renamed to `limit`. However, it will not
* be removed from the library in the foreseeable future.
*/
max?: number | ValueDeterminingMiddleware<number>;
/**
* If the Store generates an error, allow the request to pass.
*/
passOnStoreError: boolean;
};
/**
* The extended request object that includes information about the client's
* rate limit.
*/
export type AugmentedRequest = Request & {
[key: string]: RateLimitInfo;
};
/**
* The rate limit related information for each client included in the
* Express request object.
*/
export type RateLimitInfo = {
limit: number;
used: number;
remaining: number;
resetTime: Date | undefined;
key: string;
};
/**
* The record that stores information about a client - namely, how many times
* they have hit the endpoint, and when their hit count resets.
*
* Similar to `ClientRateLimitInfo`, except `resetTime` is a compulsory field.
*/
export type Client = {
totalHits: number;
resetTime: Date;
};
/**
* A `Store` that stores the hit count for each client in memory.
*
* @public
*/
export declare class MemoryStore implements Store {
private validations?;
/**
* The duration of time before which all hit counts are reset (in milliseconds).
*/
windowMs: number;
/**
* These two maps store usage (requests) and reset time by key (for example, IP
* addresses or API keys).
*
* They are split into two to avoid having to iterate through the entire set to
* determine which ones need reset. Instead, `Client`s are moved from `previous`
* to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
* left in `previous`, i.e., those that have not made any recent requests, are
* known to be expired and can be deleted in bulk.
*/
previous: Map<string, Client>;
current: Map<string, Client>;
/**
* A reference to the active timer.
*/
interval?: NodeJS.Timeout;
/**
* Confirmation that the keys incremented in once instance of MemoryStore
* cannot affect other instances.
*/
localKeys: boolean;
constructor(validations?: Validations | undefined);
/**
* Method that initializes the store.
*
* @param options {Options} - The options used to setup the middleware.
*/
init(options: Options): void;
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
*
* @public
*/
get(key: string): Promise<ClientRateLimitInfo | undefined>;
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*
* @public
*/
increment(key: string): Promise<ClientRateLimitInfo>;
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
decrement(key: string): Promise<void>;
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
resetKey(key: string): Promise<void>;
/**
* Method to reset everyone's hit counter.
*
* @public
*/
resetAll(): Promise<void>;
/**
* Method to stop the timer (if currently running) and prevent any memory
* leaks.
*
* @public
*/
shutdown(): void;
/**
* Recycles a client by setting its hit count to zero, and reset time to
* `windowMs` milliseconds from now.
*
* NOT to be confused with `#resetKey()`, which removes a client from both the
* `current` and `previous` maps.
*
* @param client {Client} - The client to recycle.
* @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
*
* @return {Client} - The modified client that was passed in, to allow for chaining.
*/
private resetClient;
/**
* Retrieves or creates a client, given a key. Also ensures that the client being
* returned is in the `current` map.
*
* @param key {string} - The key under which the client is (or is to be) stored.
*
* @returns {Client} - The requested client.
*/
private getClient;
/**
* Move current clients to previous, create a new map for current.
*
* This function is called every `windowMs`.
*/
private clearExpired;
}
/**
*
* Create an instance of IP rate-limiting middleware for Express.
*
* @param passedOptions {Options} - Options to configure the rate limiter.
*
* @returns {RateLimitRequestHandler} - The middleware that rate-limits clients based on your configuration.
*
* @public
*/
export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
export {
rateLimit as default,
};
export {};

642
node_modules/express-rate-limit/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,642 @@
// Generated by dts-bundle-generator v8.1.2
import { NextFunction, Request, RequestHandler, Response } from 'express';
/**
* Returns the IP address itself for IPv4, or a CIDR-notation subnet for IPv6.
*
* If you write a custom keyGenerator that allows a fallback to IP address for
* unauthenticated users, return ipKeyGenerator(req.ip) rather than just req.ip.
*
* For more information, {@see Options.ipv6Subnet}.
*
* @param ip {string} - The IP address to process, usually request.ip.
* @param ipv6Subnet {number | false} - The subnet mask for IPv6 addresses.
*
* @returns {string} - The key generated from the IP address
*
* @public
*/
export declare function ipKeyGenerator(ip: string, ipv6Subnet?: number | false): string;
declare const SUPPORTED_DRAFT_VERSIONS: readonly [
"draft-6",
"draft-7",
"draft-8"
];
declare const validations: {
enabled: {
[key: string]: boolean;
};
disable(): void;
/**
* Checks whether the IP address is valid, and that it does not have a port
* number in it.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
*
* @param ip {string | undefined} - The IP address provided by Express as request.ip.
*
* @returns {void}
*/
ip(ip: string | undefined): void;
/**
* Makes sure the trust proxy setting is not set to `true`.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
trustProxy(request: Request): void;
/**
* Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
* header is present.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
xForwardedForHeader(request: Request): void;
/**
* Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
forwardedHeader(request: Request): void;
/**
* Ensures totalHits value from store is a positive integer.
*
* @param hits {any} - The `totalHits` returned by the store.
*/
positiveHits(hits: any): void;
/**
* Ensures a single store instance is not used with multiple express-rate-limit instances
*/
unsharedStore(store: Store): void;
/**
* Ensures a given key is incremented only once per request.
*
* @param request {Request} - The Express request object.
* @param store {Store} - The store class.
* @param key {string} - The key used to store the client's hit count.
*
* @returns {void}
*/
singleCount(request: Request, store: Store, key: string): void;
/**
* Warns the user that the behaviour for `max: 0` / `limit: 0` is
* changing in the next major release.
*
* @param limit {number} - The maximum number of hits per client.
*
* @returns {void}
*/
limit(limit: number): void;
/**
* Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
* and will be removed in the next major release.
*
* @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
*
* @returns {void}
*/
draftPolliHeaders(draft_polli_ratelimit_headers?: any): void;
/**
* Warns the user that the `onLimitReached` option is deprecated and
* will be removed in the next major release.
*
* @param onLimitReached {any | undefined} - The maximum number of hits per client.
*
* @returns {void}
*/
onLimitReached(onLimitReached?: any): void;
/**
* Warns the user when an invalid/unsupported version of the draft spec is passed.
*
* @param version {any | undefined} - The version passed by the user.
*
* @returns {void}
*/
headersDraftVersion(version?: any): void;
/**
* Warns the user when the selected headers option requires a reset time but
* the store does not provide one.
*
* @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.
*
* @returns {void}
*/
headersResetTime(resetTime?: Date): void;
knownOptions(passedOptions?: Partial<Options>): void;
/**
* Checks the options.validate setting to ensure that only recognized
* validations are enabled or disabled.
*
* If any unrecognized values are found, an error is logged that
* includes the list of supported validations.
*/
validationsConfig(): void;
/**
* Checks to see if the instance was created inside of a request handler,
* which would prevent it from working correctly, with the default memory
* store (or any other store with localKeys.)
*/
creationStack(store: Store): void;
ipv6Subnet(ipv6Subnet?: any): void;
ipv6SubnetOrKeyGenerator(options: Partial<Options>): void;
keyGeneratorIpFallback(keyGenerator?: ValueDeterminingMiddleware<string>): void;
/**
* Checks to see if the window duration is greater than 2^32 - 1. This is only
* called by the default MemoryStore, since it uses Node's setInterval method.
*
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
*/
windowMs(windowMs: number): void;
};
export type Validations = typeof validations;
/**
* Callback that fires when a client's hit counter is incremented.
*
* @param error {Error | undefined} - The error that occurred, if any.
* @param totalHits {number} - The number of hits for that client so far.
* @param resetTime {Date | undefined} - The time when the counter resets.
*/
export type IncrementCallback = (error: Error | undefined, totalHits: number, resetTime: Date | undefined) => void;
/**
* Method (in the form of middleware) to generate/retrieve a value based on the
* incoming request.
*
* @param request {Request} - The Express request object.
* @param response {Response} - The Express response object.
*
* @returns {T} - The value needed.
*/
export type ValueDeterminingMiddleware<T> = (request: Request, response: Response) => T | Promise<T>;
/**
* Express request handler that sends back a response when a client is
* rate-limited.
*
* @param request {Request} - The Express request object.
* @param response {Response} - The Express response object.
* @param next {NextFunction} - The Express `next` function, can be called to skip responding.
* @param optionsUsed {Options} - The options used to set up the middleware.
*/
export type RateLimitExceededEventHandler = (request: Request, response: Response, next: NextFunction, optionsUsed: Options) => void;
/**
* Event callback that is triggered on a client's first request that exceeds the limit
* but not for subsequent requests. May be used for logging, etc. Should *not*
* send a response.
*
* @param request {Request} - The Express request object.
* @param response {Response} - The Express response object.
* @param optionsUsed {Options} - The options used to set up the middleware.
*/
export type RateLimitReachedEventHandler = (request: Request, response: Response, optionsUsed: Options) => void;
/**
* Data returned from the `Store` when a client's hit counter is incremented.
*
* @property totalHits {number} - The number of hits for that client so far.
* @property resetTime {Date | undefined} - The time when the counter resets.
*/
export type ClientRateLimitInfo = {
totalHits: number;
resetTime: Date | undefined;
};
export type IncrementResponse = ClientRateLimitInfo;
/**
* A modified Express request handler with the rate limit functions.
*/
export type RateLimitRequestHandler = RequestHandler & {
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
resetKey: (key: string) => void;
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*/
getKey: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
};
/**
* An interface that all hit counter stores must implement.
*
* @deprecated 6.x - Implement the `Store` interface instead.
*/
export type LegacyStore = {
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
* @param callback {IncrementCallback} - The callback to call once the counter is incremented.
*/
incr: (key: string, callback: IncrementCallback) => void;
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
decrement: (key: string) => void;
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
resetKey: (key: string) => void;
/**
* Method to reset everyone's hit counter.
*/
resetAll?: () => void;
};
/**
* An interface that all hit counter stores must implement.
*/
export type Store = {
/**
* Method that initializes the store, and has access to the options passed to
* the middleware too.
*
* @param options {Options} - The options used to setup the middleware.
*/
init?: (options: Options) => void;
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*/
get?: (key: string) => Promise<ClientRateLimitInfo | undefined> | ClientRateLimitInfo | undefined;
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @returns {IncrementResponse | undefined} - The number of hits and reset time for that client.
*/
increment: (key: string) => Promise<IncrementResponse> | IncrementResponse;
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
decrement: (key: string) => Promise<void> | void;
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*/
resetKey: (key: string) => Promise<void> | void;
/**
* Method to reset everyone's hit counter.
*/
resetAll?: () => Promise<void> | void;
/**
* Method to shutdown the store, stop timers, and release all resources.
*/
shutdown?: () => Promise<void> | void;
/**
* Flag to indicate that keys incremented in one instance of this store can
* not affect other instances. Typically false if a database is used, true for
* MemoryStore.
*
* Used to help detect double-counting misconfigurations.
*/
localKeys?: boolean;
/**
* Optional value that the store prepends to keys
*
* Used by the double-count check to avoid false-positives when a key is counted
* twice, but with different prefixes.
*/
prefix?: string;
};
export type DraftHeadersVersion = (typeof SUPPORTED_DRAFT_VERSIONS)[number];
/**
* Validate configuration object for enabling or disabling specific validations.
*
* The keys must also be keys in the validations object, except `enable`, `disable`,
* and `default`.
*/
export type EnabledValidations = {
[key in keyof Omit<Validations, "enabled" | "disable"> | "default"]?: boolean;
};
/**
* The configuration options for the rate limiter.
*/
export type Options = {
/**
* How long we should remember the requests.
*
* Defaults to `60000` ms (= 1 minute).
*/
windowMs: number;
/**
* The maximum number of connections to allow during the `window` before
* rate limiting the client.
*
* Can be the limit itself as a number or express middleware that parses
* the request and then figures out the limit.
*
* Defaults to `5`.
*/
limit: number | ValueDeterminingMiddleware<number>;
/**
* The response body to send back when a client is rate limited.
*
* Defaults to `'Too many requests, please try again later.'`
*/
message: any | ValueDeterminingMiddleware<any>;
/**
* The HTTP status code to send back when a client is rate limited.
*
* Defaults to `HTTP 429 Too Many Requests` (RFC 6585).
*/
statusCode: number;
/**
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
* of requests.
*
* Defaults to `true` (for backward compatibility).
*/
legacyHeaders: boolean;
/**
* Whether to enable support for the standardized rate limit headers (`RateLimit-*`).
*
* Defaults to `false` (for backward compatibility, but its use is recommended).
*/
standardHeaders: boolean | DraftHeadersVersion;
/**
* The name used to identify the quota policy in the `RateLimit` headers as per
* the 8th draft of the IETF specification.
*
* Defaults to `{limit}-in-{window}`.
*/
identifier: string | ValueDeterminingMiddleware<string>;
/**
* The name of the property on the request object to store the rate limit info.
*
* Defaults to `rateLimit`.
*/
requestPropertyName: string;
/**
* If `true`, the library will (by default) skip all requests that have a 4XX
* or 5XX status.
*
* Defaults to `false`.
*/
skipFailedRequests: boolean;
/**
* If `true`, the library will (by default) skip all requests that have a
* status code less than 400.
*
* Defaults to `false`.
*/
skipSuccessfulRequests: boolean;
/**
* Method to generate custom identifiers for clients.
*
* By default, the client's IP address is used.
*/
keyGenerator: ValueDeterminingMiddleware<string>;
/**
* IPv6 subnet mask applied to IPv6 addresses in the default keyGenerator.
*
* Default is 56. The valid range is technically 1-128 but the value should
* generally be in the 32-64 range.
*
* Smaller numbers are more aggressive, larger numbers are more lenient. Try
* bumping to 60 or 64 if you see evidence of users being blocked incorrectly.
*
* May also be set to a function that returns a number based on the request.
*
* See the documentation for more info:
* https://express-rate-limit.mintlify.app/reference/configuration#ipv6subnet.
*/
ipv6Subnet: 64 | 60 | 56 | 52 | 50 | 48 | 32 | number | ValueDeterminingMiddleware<number> | false;
/**
* Express request handler that sends back a response when a client is
* rate-limited.
*
* By default, sends back the `statusCode` and `message` set via the options.
*/
handler: RateLimitExceededEventHandler;
/**
* Method (in the form of middleware) to determine whether or not this request
* counts towards a client's quota.
*
* By default, skips no requests.
*/
skip: ValueDeterminingMiddleware<boolean>;
/**
* Method to determine whether or not the request counts as 'successful'. Used
* when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
*
* By default, requests with a response status code less than 400 are considered
* successful.
*/
requestWasSuccessful: ValueDeterminingMiddleware<boolean>;
/**
* The `Store` to use to store the hit count for each client.
*
* By default, the built-in `MemoryStore` will be used.
*/
store: Store | LegacyStore;
/**
* The list of validation checks that should run.
*/
validate: boolean | EnabledValidations;
/**
* Whether to send `X-RateLimit-*` headers with the rate limit and the number
* of requests.
*
* @deprecated 6.x - This option was renamed to `legacyHeaders`.
*/
headers?: boolean;
/**
* The maximum number of connections to allow during the `window` before
* rate limiting the client.
*
* Can be the limit itself as a number or express middleware that parses
* the request and then figures out the limit.
*
* @deprecated 7.x - This option was renamed to `limit`. However, it will not
* be removed from the library in the foreseeable future.
*/
max?: number | ValueDeterminingMiddleware<number>;
/**
* If the Store generates an error, allow the request to pass.
*/
passOnStoreError: boolean;
};
/**
* The extended request object that includes information about the client's
* rate limit.
*/
export type AugmentedRequest = Request & {
[key: string]: RateLimitInfo;
};
/**
* The rate limit related information for each client included in the
* Express request object.
*/
export type RateLimitInfo = {
limit: number;
used: number;
remaining: number;
resetTime: Date | undefined;
key: string;
};
/**
* The record that stores information about a client - namely, how many times
* they have hit the endpoint, and when their hit count resets.
*
* Similar to `ClientRateLimitInfo`, except `resetTime` is a compulsory field.
*/
export type Client = {
totalHits: number;
resetTime: Date;
};
/**
* A `Store` that stores the hit count for each client in memory.
*
* @public
*/
export declare class MemoryStore implements Store {
private validations?;
/**
* The duration of time before which all hit counts are reset (in milliseconds).
*/
windowMs: number;
/**
* These two maps store usage (requests) and reset time by key (for example, IP
* addresses or API keys).
*
* They are split into two to avoid having to iterate through the entire set to
* determine which ones need reset. Instead, `Client`s are moved from `previous`
* to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
* left in `previous`, i.e., those that have not made any recent requests, are
* known to be expired and can be deleted in bulk.
*/
previous: Map<string, Client>;
current: Map<string, Client>;
/**
* A reference to the active timer.
*/
interval?: NodeJS.Timeout;
/**
* Confirmation that the keys incremented in once instance of MemoryStore
* cannot affect other instances.
*/
localKeys: boolean;
constructor(validations?: Validations | undefined);
/**
* Method that initializes the store.
*
* @param options {Options} - The options used to setup the middleware.
*/
init(options: Options): void;
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
*
* @public
*/
get(key: string): Promise<ClientRateLimitInfo | undefined>;
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*
* @public
*/
increment(key: string): Promise<ClientRateLimitInfo>;
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
decrement(key: string): Promise<void>;
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
resetKey(key: string): Promise<void>;
/**
* Method to reset everyone's hit counter.
*
* @public
*/
resetAll(): Promise<void>;
/**
* Method to stop the timer (if currently running) and prevent any memory
* leaks.
*
* @public
*/
shutdown(): void;
/**
* Recycles a client by setting its hit count to zero, and reset time to
* `windowMs` milliseconds from now.
*
* NOT to be confused with `#resetKey()`, which removes a client from both the
* `current` and `previous` maps.
*
* @param client {Client} - The client to recycle.
* @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
*
* @return {Client} - The modified client that was passed in, to allow for chaining.
*/
private resetClient;
/**
* Retrieves or creates a client, given a key. Also ensures that the client being
* returned is in the `current` map.
*
* @param key {string} - The key under which the client is (or is to be) stored.
*
* @returns {Client} - The requested client.
*/
private getClient;
/**
* Move current clients to previous, create a new map for current.
*
* This function is called every `windowMs`.
*/
private clearExpired;
}
/**
*
* Create an instance of IP rate-limiting middleware for Express.
*
* @param passedOptions {Options} - Options to configure the rate limiter.
*
* @returns {RateLimitRequestHandler} - The middleware that rate-limits clients based on your configuration.
*
* @public
*/
export declare const rateLimit: (passedOptions?: Partial<Options>) => RateLimitRequestHandler;
export {
rateLimit as default,
};
export {};

929
node_modules/express-rate-limit/dist/index.mjs generated vendored Normal file
View File

@@ -0,0 +1,929 @@
// source/ip-key-generator.ts
import { isIPv6 } from "node:net";
import { Address6 } from "ip-address";
function ipKeyGenerator(ip, ipv6Subnet = 56) {
if (ipv6Subnet && isIPv6(ip)) {
return `${new Address6(`${ip}/${ipv6Subnet}`).startAddress().correctForm()}/${ipv6Subnet}`;
}
return ip;
}
// source/memory-store.ts
var MemoryStore = class {
constructor(validations2) {
this.validations = validations2;
/**
* These two maps store usage (requests) and reset time by key (for example, IP
* addresses or API keys).
*
* They are split into two to avoid having to iterate through the entire set to
* determine which ones need reset. Instead, `Client`s are moved from `previous`
* to `current` as they hit the endpoint. Once `windowMs` has elapsed, all clients
* left in `previous`, i.e., those that have not made any recent requests, are
* known to be expired and can be deleted in bulk.
*/
this.previous = /* @__PURE__ */ new Map();
this.current = /* @__PURE__ */ new Map();
/**
* Confirmation that the keys incremented in once instance of MemoryStore
* cannot affect other instances.
*/
this.localKeys = true;
}
/**
* Method that initializes the store.
*
* @param options {Options} - The options used to setup the middleware.
*/
init(options) {
this.windowMs = options.windowMs;
this.validations?.windowMs(this.windowMs);
if (this.interval) clearInterval(this.interval);
this.interval = setInterval(() => {
this.clearExpired();
}, this.windowMs);
this.interval.unref?.();
}
/**
* Method to fetch a client's hit count and reset time.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo | undefined} - The number of hits and reset time for that client.
*
* @public
*/
async get(key) {
return this.current.get(key) ?? this.previous.get(key);
}
/**
* Method to increment a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @returns {ClientRateLimitInfo} - The number of hits and reset time for that client.
*
* @public
*/
async increment(key) {
const client = this.getClient(key);
const now = Date.now();
if (client.resetTime.getTime() <= now) {
this.resetClient(client, now);
}
client.totalHits++;
return client;
}
/**
* Method to decrement a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
async decrement(key) {
const client = this.getClient(key);
if (client.totalHits > 0) client.totalHits--;
}
/**
* Method to reset a client's hit counter.
*
* @param key {string} - The identifier for a client.
*
* @public
*/
async resetKey(key) {
this.current.delete(key);
this.previous.delete(key);
}
/**
* Method to reset everyone's hit counter.
*
* @public
*/
async resetAll() {
this.current.clear();
this.previous.clear();
}
/**
* Method to stop the timer (if currently running) and prevent any memory
* leaks.
*
* @public
*/
shutdown() {
clearInterval(this.interval);
void this.resetAll();
}
/**
* Recycles a client by setting its hit count to zero, and reset time to
* `windowMs` milliseconds from now.
*
* NOT to be confused with `#resetKey()`, which removes a client from both the
* `current` and `previous` maps.
*
* @param client {Client} - The client to recycle.
* @param now {number} - The current time, to which the `windowMs` is added to get the `resetTime` for the client.
*
* @return {Client} - The modified client that was passed in, to allow for chaining.
*/
resetClient(client, now = Date.now()) {
client.totalHits = 0;
client.resetTime.setTime(now + this.windowMs);
return client;
}
/**
* Retrieves or creates a client, given a key. Also ensures that the client being
* returned is in the `current` map.
*
* @param key {string} - The key under which the client is (or is to be) stored.
*
* @returns {Client} - The requested client.
*/
getClient(key) {
if (this.current.has(key)) return this.current.get(key);
let client;
if (this.previous.has(key)) {
client = this.previous.get(key);
this.previous.delete(key);
} else {
client = { totalHits: 0, resetTime: /* @__PURE__ */ new Date() };
this.resetClient(client);
}
this.current.set(key, client);
return client;
}
/**
* Move current clients to previous, create a new map for current.
*
* This function is called every `windowMs`.
*/
clearExpired() {
this.previous = this.current;
this.current = /* @__PURE__ */ new Map();
}
};
// source/rate-limit.ts
import { isIPv6 as isIPv62 } from "node:net";
// source/headers.ts
import { Buffer } from "node:buffer";
import { createHash } from "node:crypto";
var SUPPORTED_DRAFT_VERSIONS = [
"draft-6",
"draft-7",
"draft-8"
];
var getResetSeconds = (windowMs, resetTime) => {
let resetSeconds;
if (resetTime) {
const deltaSeconds = Math.ceil((resetTime.getTime() - Date.now()) / 1e3);
resetSeconds = Math.max(0, deltaSeconds);
} else {
resetSeconds = Math.ceil(windowMs / 1e3);
}
return resetSeconds;
};
var getPartitionKey = (key) => {
const hash = createHash("sha256");
hash.update(key);
const partitionKey = hash.digest("hex").slice(0, 12);
return Buffer.from(partitionKey).toString("base64");
};
var setLegacyHeaders = (response, info) => {
if (response.headersSent) return;
response.setHeader("X-RateLimit-Limit", info.limit.toString());
response.setHeader("X-RateLimit-Remaining", info.remaining.toString());
if (info.resetTime instanceof Date) {
response.setHeader("Date", (/* @__PURE__ */ new Date()).toUTCString());
response.setHeader(
"X-RateLimit-Reset",
Math.ceil(info.resetTime.getTime() / 1e3).toString()
);
}
};
var setDraft6Headers = (response, info, windowMs) => {
if (response.headersSent) return;
const windowSeconds = Math.ceil(windowMs / 1e3);
const resetSeconds = getResetSeconds(windowMs, info.resetTime);
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
response.setHeader("RateLimit-Limit", info.limit.toString());
response.setHeader("RateLimit-Remaining", info.remaining.toString());
if (typeof resetSeconds === "number")
response.setHeader("RateLimit-Reset", resetSeconds.toString());
};
var setDraft7Headers = (response, info, windowMs) => {
if (response.headersSent) return;
const windowSeconds = Math.ceil(windowMs / 1e3);
const resetSeconds = getResetSeconds(windowMs, info.resetTime);
response.setHeader("RateLimit-Policy", `${info.limit};w=${windowSeconds}`);
response.setHeader(
"RateLimit",
`limit=${info.limit}, remaining=${info.remaining}, reset=${resetSeconds}`
);
};
var setDraft8Headers = (response, info, windowMs, name, key) => {
if (response.headersSent) return;
const windowSeconds = Math.ceil(windowMs / 1e3);
const resetSeconds = getResetSeconds(windowMs, info.resetTime);
const partitionKey = getPartitionKey(key);
const header = `r=${info.remaining}; t=${resetSeconds}`;
const policy = `q=${info.limit}; w=${windowSeconds}; pk=:${partitionKey}:`;
response.append("RateLimit", `"${name}"; ${header}`);
response.append("RateLimit-Policy", `"${name}"; ${policy}`);
};
var setRetryAfterHeader = (response, info, windowMs) => {
if (response.headersSent) return;
const resetSeconds = getResetSeconds(windowMs, info.resetTime);
response.setHeader("Retry-After", resetSeconds.toString());
};
// source/utils.ts
var omitUndefinedProperties = (passedOptions) => {
const omittedOptions = {};
for (const k of Object.keys(passedOptions)) {
const key = k;
if (passedOptions[key] !== void 0) {
omittedOptions[key] = passedOptions[key];
}
}
return omittedOptions;
};
// source/validations.ts
import { isIP } from "node:net";
var ValidationError = class extends Error {
/**
* The code must be a string, in snake case and all capital, that starts with
* the substring `ERR_ERL_`.
*
* The message must be a string, starting with an uppercase character,
* describing the issue in detail.
*/
constructor(code, message) {
const url = `https://express-rate-limit.github.io/${code}/`;
super(`${message} See ${url} for more information.`);
this.name = this.constructor.name;
this.code = code;
this.help = url;
}
};
var ChangeWarning = class extends ValidationError {
};
var usedStores = /* @__PURE__ */ new Set();
var singleCountKeys = /* @__PURE__ */ new WeakMap();
var validations = {
enabled: {
default: true
},
// Should be EnabledValidations type, but that's a circular reference
disable() {
for (const k of Object.keys(this.enabled)) this.enabled[k] = false;
},
/**
* Checks whether the IP address is valid, and that it does not have a port
* number in it.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_invalid_ip_address.
*
* @param ip {string | undefined} - The IP address provided by Express as request.ip.
*
* @returns {void}
*/
ip(ip) {
if (ip === void 0) {
throw new ValidationError(
"ERR_ERL_UNDEFINED_IP_ADDRESS",
`An undefined 'request.ip' was detected. This might indicate a misconfiguration or the connection being destroyed prematurely.`
);
}
if (!isIP(ip)) {
throw new ValidationError(
"ERR_ERL_INVALID_IP_ADDRESS",
`An invalid 'request.ip' (${ip}) was detected. Consider passing a custom 'keyGenerator' function to the rate limiter.`
);
}
},
/**
* Makes sure the trust proxy setting is not set to `true`.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_permissive_trust_proxy.
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
trustProxy(request) {
if (request.app.get("trust proxy") === true) {
throw new ValidationError(
"ERR_ERL_PERMISSIVE_TRUST_PROXY",
`The Express 'trust proxy' setting is true, which allows anyone to trivially bypass IP-based rate limiting.`
);
}
},
/**
* Makes sure the trust proxy setting is set in case the `X-Forwarded-For`
* header is present.
*
* See https://github.com/express-rate-limit/express-rate-limit/wiki/Error-Codes#err_erl_unset_trust_proxy.
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
xForwardedForHeader(request) {
if (request.headers["x-forwarded-for"] && request.app.get("trust proxy") === false) {
throw new ValidationError(
"ERR_ERL_UNEXPECTED_X_FORWARDED_FOR",
`The 'X-Forwarded-For' header is set but the Express 'trust proxy' setting is false (default). This could indicate a misconfiguration which would prevent express-rate-limit from accurately identifying users.`
);
}
},
/**
* Alert the user if the Forwarded header is set (standardized version of X-Forwarded-For - not supported by express as of version 5.1.0)
*
* @param request {Request} - The Express request object.
*
* @returns {void}
*/
forwardedHeader(request) {
if (request.headers.forwarded && request.ip === request.socket?.remoteAddress) {
throw new ValidationError(
"ERR_ERL_FORWARDED_HEADER",
`The 'Forwarded' header (standardized X-Forwarded-For) is set but currently being ignored. Add a custom keyGenerator to use a value from this header.`
);
}
},
/**
* Ensures totalHits value from store is a positive integer.
*
* @param hits {any} - The `totalHits` returned by the store.
*/
positiveHits(hits) {
if (typeof hits !== "number" || hits < 1 || hits !== Math.round(hits)) {
throw new ValidationError(
"ERR_ERL_INVALID_HITS",
`The totalHits value returned from the store must be a positive integer, got ${hits}`
);
}
},
/**
* Ensures a single store instance is not used with multiple express-rate-limit instances
*/
unsharedStore(store) {
if (usedStores.has(store)) {
const maybeUniquePrefix = store?.localKeys ? "" : " (with a unique prefix)";
throw new ValidationError(
"ERR_ERL_STORE_REUSE",
`A Store instance must not be shared across multiple rate limiters. Create a new instance of ${store.constructor.name}${maybeUniquePrefix} for each limiter instead.`
);
}
usedStores.add(store);
},
/**
* Ensures a given key is incremented only once per request.
*
* @param request {Request} - The Express request object.
* @param store {Store} - The store class.
* @param key {string} - The key used to store the client's hit count.
*
* @returns {void}
*/
singleCount(request, store, key) {
let storeKeys = singleCountKeys.get(request);
if (!storeKeys) {
storeKeys = /* @__PURE__ */ new Map();
singleCountKeys.set(request, storeKeys);
}
const storeKey = store.localKeys ? store : store.constructor.name;
let keys = storeKeys.get(storeKey);
if (!keys) {
keys = [];
storeKeys.set(storeKey, keys);
}
const prefixedKey = `${store.prefix ?? ""}${key}`;
if (keys.includes(prefixedKey)) {
throw new ValidationError(
"ERR_ERL_DOUBLE_COUNT",
`The hit count for ${key} was incremented more than once for a single request.`
);
}
keys.push(prefixedKey);
},
/**
* Warns the user that the behaviour for `max: 0` / `limit: 0` is
* changing in the next major release.
*
* @param limit {number} - The maximum number of hits per client.
*
* @returns {void}
*/
limit(limit) {
if (limit === 0) {
throw new ChangeWarning(
"WRN_ERL_MAX_ZERO",
"Setting limit or max to 0 disables rate limiting in express-rate-limit v6 and older, but will cause all requests to be blocked in v7"
);
}
},
/**
* Warns the user that the `draft_polli_ratelimit_headers` option is deprecated
* and will be removed in the next major release.
*
* @param draft_polli_ratelimit_headers {any | undefined} - The now-deprecated setting that was used to enable standard headers.
*
* @returns {void}
*/
draftPolliHeaders(draft_polli_ratelimit_headers) {
if (draft_polli_ratelimit_headers) {
throw new ChangeWarning(
"WRN_ERL_DEPRECATED_DRAFT_POLLI_HEADERS",
`The draft_polli_ratelimit_headers configuration option is deprecated and has been removed in express-rate-limit v7, please set standardHeaders: 'draft-6' instead.`
);
}
},
/**
* Warns the user that the `onLimitReached` option is deprecated and
* will be removed in the next major release.
*
* @param onLimitReached {any | undefined} - The maximum number of hits per client.
*
* @returns {void}
*/
onLimitReached(onLimitReached) {
if (onLimitReached) {
throw new ChangeWarning(
"WRN_ERL_DEPRECATED_ON_LIMIT_REACHED",
"The onLimitReached configuration option is deprecated and has been removed in express-rate-limit v7."
);
}
},
/**
* Warns the user when an invalid/unsupported version of the draft spec is passed.
*
* @param version {any | undefined} - The version passed by the user.
*
* @returns {void}
*/
headersDraftVersion(version) {
if (typeof version !== "string" || // @ts-expect-error This is fine. If version is not in the array, it will just return false.
!SUPPORTED_DRAFT_VERSIONS.includes(version)) {
const versionString = SUPPORTED_DRAFT_VERSIONS.join(", ");
throw new ValidationError(
"ERR_ERL_HEADERS_UNSUPPORTED_DRAFT_VERSION",
`standardHeaders: only the following versions of the IETF draft specification are supported: ${versionString}.`
);
}
},
/**
* Warns the user when the selected headers option requires a reset time but
* the store does not provide one.
*
* @param resetTime {Date | undefined} - The timestamp when the client's hit count will be reset.
*
* @returns {void}
*/
headersResetTime(resetTime) {
if (!resetTime) {
throw new ValidationError(
"ERR_ERL_HEADERS_NO_RESET",
`standardHeaders: 'draft-7' requires a 'resetTime', but the store did not provide one. The 'windowMs' value will be used instead, which may cause clients to wait longer than necessary.`
);
}
},
knownOptions(passedOptions) {
if (!passedOptions) return;
const optionsMap = {
windowMs: true,
limit: true,
message: true,
statusCode: true,
legacyHeaders: true,
standardHeaders: true,
identifier: true,
requestPropertyName: true,
skipFailedRequests: true,
skipSuccessfulRequests: true,
keyGenerator: true,
ipv6Subnet: true,
handler: true,
skip: true,
requestWasSuccessful: true,
store: true,
validate: true,
headers: true,
max: true,
passOnStoreError: true
};
const validOptions = Object.keys(optionsMap).concat(
"draft_polli_ratelimit_headers",
// not a valid option anymore, but we have a more specific check for this one, so don't warn for it here
// from express-slow-down - https://github.com/express-rate-limit/express-slow-down/blob/main/source/types.ts#L65
"delayAfter",
"delayMs",
"maxDelayMs"
);
for (const key of Object.keys(passedOptions)) {
if (!validOptions.includes(key)) {
throw new ValidationError(
"ERR_ERL_UNKNOWN_OPTION",
`Unexpected configuration option: ${key}`
// todo: suggest a valid option with a short levenstein distance?
);
}
}
},
/**
* Checks the options.validate setting to ensure that only recognized
* validations are enabled or disabled.
*
* If any unrecognized values are found, an error is logged that
* includes the list of supported validations.
*/
validationsConfig() {
const supportedValidations = Object.keys(this).filter(
(k) => !["enabled", "disable"].includes(k)
);
supportedValidations.push("default");
for (const key of Object.keys(this.enabled)) {
if (!supportedValidations.includes(key)) {
throw new ValidationError(
"ERR_ERL_UNKNOWN_VALIDATION",
`options.validate.${key} is not recognized. Supported validate options are: ${supportedValidations.join(
", "
)}.`
);
}
}
},
/**
* Checks to see if the instance was created inside of a request handler,
* which would prevent it from working correctly, with the default memory
* store (or any other store with localKeys.)
*/
creationStack(store) {
const { stack } = new Error(
"express-rate-limit validation check (set options.validate.creationStack=false to disable)"
);
if (stack?.includes("Layer.handle [as handle_request]") || // express v4
stack?.includes("Layer.handleRequest")) {
if (!store.localKeys) {
throw new ValidationError(
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
"express-rate-limit instance should *usually* be created at app initialization, not when responding to a request."
);
}
throw new ValidationError(
"ERR_ERL_CREATED_IN_REQUEST_HANDLER",
"express-rate-limit instance should be created at app initialization, not when responding to a request."
);
}
},
ipv6Subnet(ipv6Subnet) {
if (ipv6Subnet === false) {
return;
}
if (!Number.isInteger(ipv6Subnet) || ipv6Subnet < 32 || ipv6Subnet > 64) {
throw new ValidationError(
"ERR_ERL_IPV6_SUBNET",
`Unexpected ipv6Subnet value: ${ipv6Subnet}. Expected an integer between 32 and 64 (usually 48-64).`
);
}
},
ipv6SubnetOrKeyGenerator(options) {
if (options.ipv6Subnet !== void 0 && options.keyGenerator) {
throw new ValidationError(
"ERR_ERL_IPV6SUBNET_OR_KEYGENERATOR",
`Incompatible options: the 'ipv6Subnet' option is ignored when a custom 'keyGenerator' function is also set.`
);
}
},
keyGeneratorIpFallback(keyGenerator) {
if (!keyGenerator) {
return;
}
const src = keyGenerator.toString();
if ((src.includes("req.ip") || src.includes("request.ip")) && !src.includes("ipKeyGenerator")) {
throw new ValidationError(
"ERR_ERL_KEY_GEN_IPV6",
"Custom keyGenerator appears to use request IP without calling the ipKeyGenerator helper function for IPv6 addresses. This could allow IPv6 users to bypass limits."
);
}
},
/**
* Checks to see if the window duration is greater than 2^32 - 1. This is only
* called by the default MemoryStore, since it uses Node's setInterval method.
*
* See https://nodejs.org/api/timers.html#setintervalcallback-delay-args.
*/
windowMs(windowMs) {
const SET_TIMEOUT_MAX = 2 ** 31 - 1;
if (typeof windowMs !== "number" || Number.isNaN(windowMs) || windowMs < 1 || windowMs > SET_TIMEOUT_MAX) {
throw new ValidationError(
"ERR_ERL_WINDOW_MS",
`Invalid windowMs value: ${windowMs}${typeof windowMs !== "number" ? ` (${typeof windowMs})` : ""}, must be a number between 1 and ${SET_TIMEOUT_MAX} when using the default MemoryStore`
);
}
}
};
var getValidations = (_enabled) => {
let enabled;
if (typeof _enabled === "boolean") {
enabled = {
default: _enabled
};
} else {
enabled = {
default: true,
..._enabled
};
}
const wrappedValidations = { enabled };
for (const [name, validation] of Object.entries(validations)) {
if (typeof validation === "function")
wrappedValidations[name] = (...args) => {
if (!(enabled[name] ?? enabled.default)) {
return;
}
try {
;
validation.apply(
wrappedValidations,
args
);
} catch (error) {
if (error instanceof ChangeWarning) console.warn(error);
else console.error(error);
}
};
}
return wrappedValidations;
};
// source/rate-limit.ts
var isLegacyStore = (store) => (
// Check that `incr` exists but `increment` does not - store authors might want
// to keep both around for backwards compatibility.
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 legacyStore.decrement(key);
}
async resetKey(key) {
return legacyStore.resetKey(key);
}
/* istanbul ignore next */
async resetAll() {
if (typeof legacyStore.resetAll === "function")
return legacyStore.resetAll();
}
}
return new PromisifiedStore();
};
var getOptionsFromConfig = (config) => {
const { validations: validations2, ...directlyPassableEntries } = config;
return {
...directlyPassableEntries,
validate: validations2.enabled
};
};
var parseOptions = (passedOptions) => {
const notUndefinedOptions = omitUndefinedProperties(passedOptions);
const validations2 = getValidations(notUndefinedOptions?.validate ?? true);
validations2.validationsConfig();
validations2.knownOptions(passedOptions);
validations2.draftPolliHeaders(
// @ts-expect-error see the note above.
notUndefinedOptions.draft_polli_ratelimit_headers
);
validations2.onLimitReached(notUndefinedOptions.onLimitReached);
if (notUndefinedOptions.ipv6Subnet !== void 0 && typeof notUndefinedOptions.ipv6Subnet !== "function") {
validations2.ipv6Subnet(notUndefinedOptions.ipv6Subnet);
}
validations2.keyGeneratorIpFallback(notUndefinedOptions.keyGenerator);
validations2.ipv6SubnetOrKeyGenerator(notUndefinedOptions);
let standardHeaders = notUndefinedOptions.standardHeaders ?? false;
if (standardHeaders === true) standardHeaders = "draft-6";
const config = {
windowMs: 60 * 1e3,
limit: passedOptions.max ?? 5,
// `max` is deprecated, but support it anyways.
message: "Too many requests, please try again later.",
statusCode: 429,
legacyHeaders: passedOptions.headers ?? true,
identifier(request, _response) {
let duration = "";
const property = config.requestPropertyName;
const { limit } = request[property];
const seconds = config.windowMs / 1e3;
const minutes = config.windowMs / (1e3 * 60);
const hours = config.windowMs / (1e3 * 60 * 60);
const days = config.windowMs / (1e3 * 60 * 60 * 24);
if (seconds < 60) duration = `${seconds}sec`;
else if (minutes < 60) duration = `${minutes}min`;
else if (hours < 24) duration = `${hours}hr${hours > 1 ? "s" : ""}`;
else duration = `${days}day${days > 1 ? "s" : ""}`;
return `${limit}-in-${duration}`;
},
requestPropertyName: "rateLimit",
skipFailedRequests: false,
skipSuccessfulRequests: false,
requestWasSuccessful: (_request, response) => response.statusCode < 400,
skip: (_request, _response) => false,
async keyGenerator(request, response) {
validations2.ip(request.ip);
validations2.trustProxy(request);
validations2.xForwardedForHeader(request);
validations2.forwardedHeader(request);
const ip = request.ip;
let subnet = 56;
if (isIPv62(ip)) {
subnet = typeof config.ipv6Subnet === "function" ? await config.ipv6Subnet(request, response) : config.ipv6Subnet;
if (typeof config.ipv6Subnet === "function")
validations2.ipv6Subnet(subnet);
}
return ipKeyGenerator(ip, subnet);
},
ipv6Subnet: 56,
async handler(request, response, _next, _optionsUsed) {
response.status(config.statusCode);
const message = typeof config.message === "function" ? await config.message(
request,
response
) : config.message;
if (!response.writableEnded) response.send(message);
},
passOnStoreError: false,
// Allow the default options to be overridden by the passed options.
...notUndefinedOptions,
// `standardHeaders` is resolved into a draft version above, use that.
standardHeaders,
// Note that this field is declared after the user's options are spread in,
// so that this field doesn't get overridden with an un-promisified store!
store: promisifyStore(
notUndefinedOptions.store ?? new MemoryStore(validations2)
),
// Print an error to the console if a few known misconfigurations are detected.
validations: validations2
};
if (typeof config.store.increment !== "function" || typeof config.store.decrement !== "function" || typeof config.store.resetKey !== "function" || config.store.resetAll !== void 0 && typeof config.store.resetAll !== "function" || config.store.init !== void 0 && typeof config.store.init !== "function") {
throw new TypeError(
"An invalid store was passed. Please ensure that the store is a class that implements the `Store` interface."
);
}
return config;
};
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 config = parseOptions(passedOptions ?? {});
const options = getOptionsFromConfig(config);
config.validations.creationStack(config.store);
config.validations.unsharedStore(config.store);
if (typeof config.store.init === "function") config.store.init(options);
const middleware = handleAsyncErrors(
async (request, response, next) => {
const skip = await config.skip(request, response);
if (skip) {
next();
return;
}
const augmentedRequest = request;
const key = await config.keyGenerator(request, response);
let totalHits = 0;
let resetTime;
try {
const incrementResult = await config.store.increment(key);
totalHits = incrementResult.totalHits;
resetTime = incrementResult.resetTime;
} catch (error) {
if (config.passOnStoreError) {
console.error(
"express-rate-limit: error from store, allowing request without rate-limiting.",
error
);
next();
return;
}
throw error;
}
config.validations.positiveHits(totalHits);
config.validations.singleCount(request, config.store, key);
const retrieveLimit = typeof config.limit === "function" ? config.limit(request, response) : config.limit;
const limit = await retrieveLimit;
config.validations.limit(limit);
const info = {
limit,
used: totalHits,
remaining: Math.max(limit - totalHits, 0),
resetTime,
key
};
Object.defineProperty(info, "current", {
configurable: false,
enumerable: false,
value: totalHits
});
augmentedRequest[config.requestPropertyName] = info;
if (config.legacyHeaders && !response.headersSent) {
setLegacyHeaders(response, info);
}
if (config.standardHeaders && !response.headersSent) {
switch (config.standardHeaders) {
case "draft-6": {
setDraft6Headers(response, info, config.windowMs);
break;
}
case "draft-7": {
config.validations.headersResetTime(info.resetTime);
setDraft7Headers(response, info, config.windowMs);
break;
}
case "draft-8": {
const retrieveName = typeof config.identifier === "function" ? config.identifier(request, response) : config.identifier;
const name = await retrieveName;
config.validations.headersResetTime(info.resetTime);
setDraft8Headers(response, info, config.windowMs, name, key);
break;
}
default: {
config.validations.headersDraftVersion(config.standardHeaders);
break;
}
}
}
if (config.skipFailedRequests || config.skipSuccessfulRequests) {
let decremented = false;
const decrementKey = async () => {
if (!decremented) {
await config.store.decrement(key);
decremented = true;
}
};
if (config.skipFailedRequests) {
response.on("finish", async () => {
if (!await config.requestWasSuccessful(request, response))
await decrementKey();
});
response.on("close", async () => {
if (!response.writableEnded) await decrementKey();
});
response.on("error", async () => {
await decrementKey();
});
}
if (config.skipSuccessfulRequests) {
response.on("finish", async () => {
if (await config.requestWasSuccessful(request, response))
await decrementKey();
});
}
}
config.validations.disable();
if (totalHits > limit) {
if (config.legacyHeaders || config.standardHeaders) {
setRetryAfterHeader(response, info, config.windowMs);
}
config.handler(request, response, next, options);
return;
}
next();
}
);
const getThrowFn = () => {
throw new Error("The current store does not support the get/getKey method");
};
middleware.resetKey = config.store.resetKey.bind(config.store);
middleware.getKey = typeof config.store.get === "function" ? config.store.get.bind(config.store) : getThrowFn;
return middleware;
};
var rate_limit_default = rateLimit;
export {
MemoryStore,
rate_limit_default as default,
ipKeyGenerator,
rate_limit_default as rateLimit
};

20
node_modules/express-rate-limit/license.md generated vendored Normal file
View File

@@ -0,0 +1,20 @@
# MIT License
Copyright 2023 Nathan Friedly, Vedant K
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,19 @@
Copyright (C) 2011 by Beau Gunderson
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@@ -0,0 +1,105 @@
[![CircleCI](https://dl.circleci.com/status-badge/img/circleci/9fJmTZfn8d8p7GtVt688PY/JjriGjhcxBD6zYKygMZaet/tree/master.svg?style=svg&circle-token=7baede7efd3db5f1f25fb439e97d5f695ff76318)](https://dl.circleci.com/status-badge/redirect/circleci/9fJmTZfn8d8p7GtVt688PY/JjriGjhcxBD6zYKygMZaet/tree/master)
[![codecov]](https://codecov.io/github/beaugunderson/ip-address?branch=master)
[![downloads]](https://www.npmjs.com/package/ip-address)
[![npm]](https://www.npmjs.com/package/ip-address)
[![snyk]](https://snyk.io/test/github/beaugunderson/ip-address)
[codecov]: https://codecov.io/github/beaugunderson/ip-address/coverage.svg?branch=master
[downloads]: https://img.shields.io/npm/dm/ip-address.svg
[npm]: https://img.shields.io/npm/v/ip-address.svg
[snyk]: https://snyk.io/test/github/beaugunderson/ip-address/badge.svg
## ip-address
`ip-address` is a library for validating and manipulating IPv4 and IPv6
addresses in JavaScript.
### Upgrading from 9.x to 10.x
The dependency on `jsbn` was removed thanks to
[michal-kocarek](https://github.com/michal-kocarek). Thanks Michal! For
clarity, all methods with BigInteger in the name were renamed to BigInt.
#### Breaking changes
- `#fromBigInteger()``#fromBigInt()`; now returns a native BigInt
- `#bigInteger()``#bigInt()`; now returns a native BigInt
### Documentation
Documentation is available at [ip-address.js.org](http://ip-address.js.org/).
### Examples
```js
var Address6 = require('ip-address').Address6;
var address = new Address6('2001:0:ce49:7601:e866:efff:62c3:fffe');
var teredo = address.inspectTeredo();
teredo.client4; // '157.60.0.1'
```
### Features
- Usable via CommonJS or ESM
- Parsing of all IPv6 notations
- Parsing of IPv6 addresses and ports from URLs with `Address6.fromURL(url)`
- Validity checking
- Decoding of the [Teredo
information](http://en.wikipedia.org/wiki/Teredo_tunneling#IPv6_addressing)
in an address
- Whether one address is a valid subnet of another
- What special properties a given address has (multicast prefix, unique
local address prefix, etc.)
- Number of subnets of a certain size in a given address
- Display methods
- Hex, binary, and decimal
- Canonical form
- Correct form
- IPv4-compatible (i.e. `::ffff:192.168.0.1`)
- Works in [node](http://nodejs.org/) and the browser (with browserify)
- ~1,600 test cases
### Used by
- [anon](https://github.com/edsu/anon) which powers
[@congressedits](https://twitter.com/congressedits), among
[many others](https://github.com/edsu/anon#community)
- [base85](https://github.com/noseglid/base85): base85 encoding/decoding
- [contrail-web-core](https://github.com/Juniper/contrail-web-core): part of
Contrail, a network virtualization solution made by Juniper Networks
- [dhcpjs](https://github.com/apaprocki/node-dhcpjs): a DHCP client and server
- [epochtalk](https://github.com/epochtalk/epochtalk): next generation forum
software
- [geoip-web](https://github.com/tfrce/node-geoip-web): a server for
quickly geolocating IP addresses
- [hexabus](https://github.com/mysmartgrid/hexabus): an IPv6-based home
automation bus
- [hubot-deploy](https://github.com/atmos/hubot-deploy): GitHub Flow via hubot
- [heroku-portscanner](https://github.com/robison/heroku-portscanner): nmap
hosted on Heroku
- [ipfs-swarm](https://github.com/diasdavid/node-ipfs-swarm): a swarm
implementation based on IPFS
- [javascript-x-server](https://github.com/GothAck/javascript-x-server): an X
server written in JavaScript
- [libnmap](https://github.com/jas-/node-libnmap): a node API for nmap
- [mail-io](https://github.com/mofux/mail-io): a lightweight SMTP server
- [maxmind-db-reader](https://github.com/PaddeK/node-maxmind-db): a library for
reading MaxMind database files
- [proxy-protocol-v2](https://github.com/ably/proxy-protocol-v2): a proxy
protocol encoder/decoder built by [Ably](https://www.ably.io/)
- [Samsara](https://github.com/mariusGundersen/Samsara): a Docker web interface
- [sis-api](https://github.com/sis-cmdb/sis-api): a configuration management
database API
- [socks5-client](https://github.com/mattcg/socks5-client): a SOCKS v5 client
- [socksified](https://github.com/vially/node-socksified): a SOCKS v5 client
- [socksv5](https://github.com/mscdex/socksv5): a SOCKS v5 server/client
- [ssdapi](https://github.com/rsolomou/ssdapi): an API created by the
University of Portsmouth
- [SwitchyOmega](https://github.com/FelisCatus/SwitchyOmega): a [Chrome
extension](https://chrome.google.com/webstore/detail/padekgcemlokbadohgkifijomclgjgif)
for switching between multiple proxies with ~311k users!
- [swiz](https://github.com/racker/node-swiz): a serialization framework built
and used by [Rackspace](http://www.rackspace.com/)

View File

@@ -0,0 +1,5 @@
export declare class AddressError extends Error {
parseMessage?: string;
constructor(message: string, parseMessage?: string);
}
//# sourceMappingURL=address-error.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"address-error.d.ts","sourceRoot":"","sources":["../src/address-error.ts"],"names":[],"mappings":"AAAA,qBAAa,YAAa,SAAQ,KAAK;IACrC,YAAY,CAAC,EAAE,MAAM,CAAC;gBAEV,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM;CAOnD"}

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AddressError = void 0;
class AddressError extends Error {
constructor(message, parseMessage) {
super(message);
this.name = 'AddressError';
this.parseMessage = parseMessage;
}
}
exports.AddressError = AddressError;
//# sourceMappingURL=address-error.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"address-error.js","sourceRoot":"","sources":["../src/address-error.ts"],"names":[],"mappings":";;;AAAA,MAAa,YAAa,SAAQ,KAAK;IAGrC,YAAY,OAAe,EAAE,YAAqB;QAChD,KAAK,CAAC,OAAO,CAAC,CAAC;QAEf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAE3B,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACnC,CAAC;CACF;AAVD,oCAUC"}

View File

@@ -0,0 +1,15 @@
import { Address4 } from './ipv4';
import { Address6 } from './ipv6';
export interface ReverseFormOptions {
omitSuffix?: boolean;
}
export declare function isInSubnet(this: Address4 | Address6, address: Address4 | Address6): boolean;
export declare function isCorrect(defaultBits: number): (this: Address4 | Address6) => boolean;
export declare function numberToPaddedHex(number: number): string;
export declare function stringToPaddedHex(numberString: string): string;
/**
* @param binaryValue Binary representation of a value (e.g. `10`)
* @param position Byte position, where 0 is the least significant bit
*/
export declare function testBit(binaryValue: string, position: number): boolean;
//# sourceMappingURL=common.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../src/common.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAG,QAAQ,WAUjF;AAED,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,UACpB,QAAQ,GAAG,QAAQ,aAW3C;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,UAE/C;AAED,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,UAErD;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAStE"}

View File

@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.isInSubnet = isInSubnet;
exports.isCorrect = isCorrect;
exports.numberToPaddedHex = numberToPaddedHex;
exports.stringToPaddedHex = stringToPaddedHex;
exports.testBit = testBit;
function isInSubnet(address) {
if (this.subnetMask < address.subnetMask) {
return false;
}
if (this.mask(address.subnetMask) === address.mask()) {
return true;
}
return false;
}
function isCorrect(defaultBits) {
return function () {
if (this.addressMinusSuffix !== this.correctForm()) {
return false;
}
if (this.subnetMask === defaultBits && !this.parsedSubnet) {
return true;
}
return this.parsedSubnet === String(this.subnetMask);
};
}
function numberToPaddedHex(number) {
return number.toString(16).padStart(2, '0');
}
function stringToPaddedHex(numberString) {
return numberToPaddedHex(parseInt(numberString, 10));
}
/**
* @param binaryValue Binary representation of a value (e.g. `10`)
* @param position Byte position, where 0 is the least significant bit
*/
function testBit(binaryValue, position) {
const { length } = binaryValue;
if (position > length) {
return false;
}
const positionInString = length - position;
return binaryValue.substring(positionInString, positionInString + 1) === '1';
}
//# sourceMappingURL=common.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"common.js","sourceRoot":"","sources":["../src/common.ts"],"names":[],"mappings":";;AAOA,gCAUC;AAED,8BAYC;AAED,8CAEC;AAED,8CAEC;AAMD,0BASC;AA/CD,SAAgB,UAAU,CAA4B,OAA4B;IAChF,IAAI,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;QACzC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC;QACrD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAgB,SAAS,CAAC,WAAmB;IAC3C,OAAO;QACL,IAAI,IAAI,CAAC,kBAAkB,KAAK,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YACnD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,KAAK,WAAW,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,CAAC,YAAY,KAAK,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACvD,CAAC,CAAC;AACJ,CAAC;AAED,SAAgB,iBAAiB,CAAC,MAAc;IAC9C,OAAO,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC9C,CAAC;AAED,SAAgB,iBAAiB,CAAC,YAAoB;IACpD,OAAO,iBAAiB,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC,CAAC;AACvD,CAAC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,WAAmB,EAAE,QAAgB;IAC3D,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC;IAE/B,IAAI,QAAQ,GAAG,MAAM,EAAE,CAAC;QACtB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,gBAAgB,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC3C,OAAO,WAAW,CAAC,SAAS,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC;AAC/E,CAAC"}

View File

@@ -0,0 +1,8 @@
export { Address4 } from './ipv4';
export { Address6 } from './ipv6';
export { AddressError } from './address-error';
import * as helpers from './v6/helpers';
export declare const v6: {
helpers: typeof helpers;
};
//# sourceMappingURL=ip-address.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ip-address.d.ts","sourceRoot":"","sources":["../src/ip-address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,OAAO,KAAK,OAAO,MAAM,cAAc,CAAC;AAExC,eAAO,MAAM,EAAE;;CAAc,CAAC"}

View File

@@ -0,0 +1,35 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.v6 = exports.AddressError = exports.Address6 = exports.Address4 = void 0;
var ipv4_1 = require("./ipv4");
Object.defineProperty(exports, "Address4", { enumerable: true, get: function () { return ipv4_1.Address4; } });
var ipv6_1 = require("./ipv6");
Object.defineProperty(exports, "Address6", { enumerable: true, get: function () { return ipv6_1.Address6; } });
var address_error_1 = require("./address-error");
Object.defineProperty(exports, "AddressError", { enumerable: true, get: function () { return address_error_1.AddressError; } });
const helpers = __importStar(require("./v6/helpers"));
exports.v6 = { helpers };
//# sourceMappingURL=ip-address.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ip-address.js","sourceRoot":"","sources":["../src/ip-address.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+BAAkC;AAAzB,gGAAA,QAAQ,OAAA;AACjB,+BAAkC;AAAzB,gGAAA,QAAQ,OAAA;AACjB,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AAErB,sDAAwC;AAE3B,QAAA,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC"}

View File

@@ -0,0 +1,193 @@
import * as common from './common';
/**
* Represents an IPv4 address
* @class Address4
* @param {string} address - An IPv4 address string
*/
export declare class Address4 {
address: string;
addressMinusSuffix?: string;
groups: number;
parsedAddress: string[];
parsedSubnet: string;
subnet: string;
subnetMask: number;
v4: boolean;
constructor(address: string);
static isValid(address: string): boolean;
parse(address: string): string[];
/**
* Returns the correct form of an address
* @memberof Address4
* @instance
* @returns {String}
*/
correctForm(): string;
/**
* Returns true if the address is correct, false otherwise
* @memberof Address4
* @instance
* @returns {Boolean}
*/
isCorrect: (this: Address4 | import("./ipv6").Address6) => boolean;
/**
* Converts a hex string to an IPv4 address object
* @memberof Address4
* @static
* @param {string} hex - a hex string to convert
* @returns {Address4}
*/
static fromHex(hex: string): Address4;
/**
* Converts an integer into a IPv4 address object
* @memberof Address4
* @static
* @param {integer} integer - a number to convert
* @returns {Address4}
*/
static fromInteger(integer: number): Address4;
/**
* Return an address from in-addr.arpa form
* @memberof Address4
* @static
* @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address
* @returns {Adress4}
* @example
* var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.)
* address.correctForm(); // '192.0.2.42'
*/
static fromArpa(arpaFormAddress: string): Address4;
/**
* Converts an IPv4 address object to a hex string
* @memberof Address4
* @instance
* @returns {String}
*/
toHex(): string;
/**
* Converts an IPv4 address object to an array of bytes
* @memberof Address4
* @instance
* @returns {Array}
*/
toArray(): number[];
/**
* Converts an IPv4 address object to an IPv6 address group
* @memberof Address4
* @instance
* @returns {String}
*/
toGroup6(): string;
/**
* Returns the address as a `bigint`
* @memberof Address4
* @instance
* @returns {bigint}
*/
bigInt(): bigint;
/**
* Helper function getting start address.
* @memberof Address4
* @instance
* @returns {bigint}
*/
_startAddress(): bigint;
/**
* The first address in the range given by this address' subnet.
* Often referred to as the Network Address.
* @memberof Address4
* @instance
* @returns {Address4}
*/
startAddress(): Address4;
/**
* The first host address in the range given by this address's subnet ie
* the first address after the Network Address
* @memberof Address4
* @instance
* @returns {Address4}
*/
startAddressExclusive(): Address4;
/**
* Helper function getting end address.
* @memberof Address4
* @instance
* @returns {bigint}
*/
_endAddress(): bigint;
/**
* The last address in the range given by this address' subnet
* Often referred to as the Broadcast
* @memberof Address4
* @instance
* @returns {Address4}
*/
endAddress(): Address4;
/**
* The last host address in the range given by this address's subnet ie
* the last address prior to the Broadcast Address
* @memberof Address4
* @instance
* @returns {Address4}
*/
endAddressExclusive(): Address4;
/**
* Converts a BigInt to a v4 address object
* @memberof Address4
* @static
* @param {bigint} bigInt - a BigInt to convert
* @returns {Address4}
*/
static fromBigInt(bigInt: bigint): Address4;
/**
* Returns the first n bits of the address, defaulting to the
* subnet mask
* @memberof Address4
* @instance
* @returns {String}
*/
mask(mask?: number): string;
/**
* Returns the bits in the given range as a base-2 string
* @memberof Address4
* @instance
* @returns {string}
*/
getBitsBase2(start: number, end: number): string;
/**
* Return the reversed ip6.arpa form of the address
* @memberof Address4
* @param {Object} options
* @param {boolean} options.omitSuffix - omit the "in-addr.arpa" suffix
* @instance
* @returns {String}
*/
reverseForm(options?: common.ReverseFormOptions): string;
/**
* Returns true if the given address is in the subnet of the current address
* @memberof Address4
* @instance
* @returns {boolean}
*/
isInSubnet: typeof common.isInSubnet;
/**
* Returns true if the given address is a multicast address
* @memberof Address4
* @instance
* @returns {boolean}
*/
isMulticast(): boolean;
/**
* Returns a zero-padded base-2 string representation of the address
* @memberof Address4
* @instance
* @returns {string}
*/
binaryZeroPad(): string;
/**
* Groups an IPv4 address for inclusion at the end of an IPv6 address
* @returns {String}
*/
groupForV6(): string;
}
//# sourceMappingURL=ipv4.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ipv4.d.ts","sourceRoot":"","sources":["../src/ipv4.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AAInC;;;;GAIG;AACH,qBAAa,QAAQ;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAoB;IAClC,aAAa,EAAE,MAAM,EAAE,CAAM;IAC7B,YAAY,EAAE,MAAM,CAAM;IAC1B,MAAM,EAAE,MAAM,CAAS;IACvB,UAAU,EAAE,MAAM,CAAM;IACxB,EAAE,EAAE,OAAO,CAAQ;gBAEP,OAAO,EAAE,MAAM;IAsB3B,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAcxC,KAAK,CAAC,OAAO,EAAE,MAAM;IAUrB;;;;;OAKG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;;OAKG;IACH,SAAS,0DAAoC;IAE7C;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,QAAQ;IAcrC;;;;;;OAMG;IACH,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ;IAI7C;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,QAAQ;IASlD;;;;;OAKG;IACH,KAAK,IAAI,MAAM;IAIf;;;;;OAKG;IACH,OAAO,IAAI,MAAM,EAAE;IAInB;;;;;OAKG;IACH,QAAQ,IAAI,MAAM;IAelB;;;;;OAKG;IACH,MAAM,IAAI,MAAM;IAIhB;;;;;OAKG;IACH,aAAa,IAAI,MAAM;IAIvB;;;;;;OAMG;IACH,YAAY,IAAI,QAAQ;IAIxB;;;;;;OAMG;IACH,qBAAqB,IAAI,QAAQ;IAKjC;;;;;OAKG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;;;OAMG;IACH,UAAU,IAAI,QAAQ;IAItB;;;;;;OAMG;IACH,mBAAmB,IAAI,QAAQ;IAK/B;;;;;;OAMG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAI3C;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM;IAQ3B;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAIhD;;;;;;;OAOG;IACH,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,kBAAkB,GAAG,MAAM;IAcxD;;;;;OAKG;IACH,UAAU,2BAAqB;IAE/B;;;;;OAKG;IACH,WAAW,IAAI,OAAO;IAItB;;;;;OAKG;IACH,aAAa,IAAI,MAAM;IAIvB;;;OAGG;IACH,UAAU,IAAI,MAAM;CAYrB"}

View File

@@ -0,0 +1,327 @@
"use strict";
/* eslint-disable no-param-reassign */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Address4 = void 0;
const common = __importStar(require("./common"));
const constants = __importStar(require("./v4/constants"));
const address_error_1 = require("./address-error");
/**
* Represents an IPv4 address
* @class Address4
* @param {string} address - An IPv4 address string
*/
class Address4 {
constructor(address) {
this.groups = constants.GROUPS;
this.parsedAddress = [];
this.parsedSubnet = '';
this.subnet = '/32';
this.subnetMask = 32;
this.v4 = true;
/**
* Returns true if the address is correct, false otherwise
* @memberof Address4
* @instance
* @returns {Boolean}
*/
this.isCorrect = common.isCorrect(constants.BITS);
/**
* Returns true if the given address is in the subnet of the current address
* @memberof Address4
* @instance
* @returns {boolean}
*/
this.isInSubnet = common.isInSubnet;
this.address = address;
const subnet = constants.RE_SUBNET_STRING.exec(address);
if (subnet) {
this.parsedSubnet = subnet[0].replace('/', '');
this.subnetMask = parseInt(this.parsedSubnet, 10);
this.subnet = `/${this.subnetMask}`;
if (this.subnetMask < 0 || this.subnetMask > constants.BITS) {
throw new address_error_1.AddressError('Invalid subnet mask.');
}
address = address.replace(constants.RE_SUBNET_STRING, '');
}
this.addressMinusSuffix = address;
this.parsedAddress = this.parse(address);
}
static isValid(address) {
try {
// eslint-disable-next-line no-new
new Address4(address);
return true;
}
catch (e) {
return false;
}
}
/*
* Parses a v4 address
*/
parse(address) {
const groups = address.split('.');
if (!address.match(constants.RE_ADDRESS)) {
throw new address_error_1.AddressError('Invalid IPv4 address.');
}
return groups;
}
/**
* Returns the correct form of an address
* @memberof Address4
* @instance
* @returns {String}
*/
correctForm() {
return this.parsedAddress.map((part) => parseInt(part, 10)).join('.');
}
/**
* Converts a hex string to an IPv4 address object
* @memberof Address4
* @static
* @param {string} hex - a hex string to convert
* @returns {Address4}
*/
static fromHex(hex) {
const padded = hex.replace(/:/g, '').padStart(8, '0');
const groups = [];
let i;
for (i = 0; i < 8; i += 2) {
const h = padded.slice(i, i + 2);
groups.push(parseInt(h, 16));
}
return new Address4(groups.join('.'));
}
/**
* Converts an integer into a IPv4 address object
* @memberof Address4
* @static
* @param {integer} integer - a number to convert
* @returns {Address4}
*/
static fromInteger(integer) {
return Address4.fromHex(integer.toString(16));
}
/**
* Return an address from in-addr.arpa form
* @memberof Address4
* @static
* @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address
* @returns {Adress4}
* @example
* var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.)
* address.correctForm(); // '192.0.2.42'
*/
static fromArpa(arpaFormAddress) {
// remove ending ".in-addr.arpa." or just "."
const leader = arpaFormAddress.replace(/(\.in-addr\.arpa)?\.$/, '');
const address = leader.split('.').reverse().join('.');
return new Address4(address);
}
/**
* Converts an IPv4 address object to a hex string
* @memberof Address4
* @instance
* @returns {String}
*/
toHex() {
return this.parsedAddress.map((part) => common.stringToPaddedHex(part)).join(':');
}
/**
* Converts an IPv4 address object to an array of bytes
* @memberof Address4
* @instance
* @returns {Array}
*/
toArray() {
return this.parsedAddress.map((part) => parseInt(part, 10));
}
/**
* Converts an IPv4 address object to an IPv6 address group
* @memberof Address4
* @instance
* @returns {String}
*/
toGroup6() {
const output = [];
let i;
for (i = 0; i < constants.GROUPS; i += 2) {
output.push(`${common.stringToPaddedHex(this.parsedAddress[i])}${common.stringToPaddedHex(this.parsedAddress[i + 1])}`);
}
return output.join(':');
}
/**
* Returns the address as a `bigint`
* @memberof Address4
* @instance
* @returns {bigint}
*/
bigInt() {
return BigInt(`0x${this.parsedAddress.map((n) => common.stringToPaddedHex(n)).join('')}`);
}
/**
* Helper function getting start address.
* @memberof Address4
* @instance
* @returns {bigint}
*/
_startAddress() {
return BigInt(`0b${this.mask() + '0'.repeat(constants.BITS - this.subnetMask)}`);
}
/**
* The first address in the range given by this address' subnet.
* Often referred to as the Network Address.
* @memberof Address4
* @instance
* @returns {Address4}
*/
startAddress() {
return Address4.fromBigInt(this._startAddress());
}
/**
* The first host address in the range given by this address's subnet ie
* the first address after the Network Address
* @memberof Address4
* @instance
* @returns {Address4}
*/
startAddressExclusive() {
const adjust = BigInt('1');
return Address4.fromBigInt(this._startAddress() + adjust);
}
/**
* Helper function getting end address.
* @memberof Address4
* @instance
* @returns {bigint}
*/
_endAddress() {
return BigInt(`0b${this.mask() + '1'.repeat(constants.BITS - this.subnetMask)}`);
}
/**
* The last address in the range given by this address' subnet
* Often referred to as the Broadcast
* @memberof Address4
* @instance
* @returns {Address4}
*/
endAddress() {
return Address4.fromBigInt(this._endAddress());
}
/**
* The last host address in the range given by this address's subnet ie
* the last address prior to the Broadcast Address
* @memberof Address4
* @instance
* @returns {Address4}
*/
endAddressExclusive() {
const adjust = BigInt('1');
return Address4.fromBigInt(this._endAddress() - adjust);
}
/**
* Converts a BigInt to a v4 address object
* @memberof Address4
* @static
* @param {bigint} bigInt - a BigInt to convert
* @returns {Address4}
*/
static fromBigInt(bigInt) {
return Address4.fromHex(bigInt.toString(16));
}
/**
* Returns the first n bits of the address, defaulting to the
* subnet mask
* @memberof Address4
* @instance
* @returns {String}
*/
mask(mask) {
if (mask === undefined) {
mask = this.subnetMask;
}
return this.getBitsBase2(0, mask);
}
/**
* Returns the bits in the given range as a base-2 string
* @memberof Address4
* @instance
* @returns {string}
*/
getBitsBase2(start, end) {
return this.binaryZeroPad().slice(start, end);
}
/**
* Return the reversed ip6.arpa form of the address
* @memberof Address4
* @param {Object} options
* @param {boolean} options.omitSuffix - omit the "in-addr.arpa" suffix
* @instance
* @returns {String}
*/
reverseForm(options) {
if (!options) {
options = {};
}
const reversed = this.correctForm().split('.').reverse().join('.');
if (options.omitSuffix) {
return reversed;
}
return `${reversed}.in-addr.arpa.`;
}
/**
* Returns true if the given address is a multicast address
* @memberof Address4
* @instance
* @returns {boolean}
*/
isMulticast() {
return this.isInSubnet(new Address4('224.0.0.0/4'));
}
/**
* Returns a zero-padded base-2 string representation of the address
* @memberof Address4
* @instance
* @returns {string}
*/
binaryZeroPad() {
return this.bigInt().toString(2).padStart(constants.BITS, '0');
}
/**
* Groups an IPv4 address for inclusion at the end of an IPv6 address
* @returns {String}
*/
groupForV6() {
const segments = this.parsedAddress;
return this.address.replace(constants.RE_ADDRESS, `<span class="hover-group group-v4 group-6">${segments
.slice(0, 2)
.join('.')}</span>.<span class="hover-group group-v4 group-7">${segments
.slice(2, 4)
.join('.')}</span>`);
}
}
exports.Address4 = Address4;
//# sourceMappingURL=ipv4.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,428 @@
import * as common from './common';
import { Address4 } from './ipv4';
interface SixToFourProperties {
prefix: string;
gateway: string;
}
interface TeredoProperties {
prefix: string;
server4: string;
client4: string;
flags: string;
coneNat: boolean;
microsoft: {
reserved: boolean;
universalLocal: boolean;
groupIndividual: boolean;
nonce: string;
};
udpPort: string;
}
/**
* Represents an IPv6 address
* @class Address6
* @param {string} address - An IPv6 address string
* @param {number} [groups=8] - How many octets to parse
* @example
* var address = new Address6('2001::/32');
*/
export declare class Address6 {
address4?: Address4;
address: string;
addressMinusSuffix: string;
elidedGroups?: number;
elisionBegin?: number;
elisionEnd?: number;
groups: number;
parsedAddress4?: string;
parsedAddress: string[];
parsedSubnet: string;
subnet: string;
subnetMask: number;
v4: boolean;
zone: string;
constructor(address: string, optionalGroups?: number);
static isValid(address: string): boolean;
/**
* Convert a BigInt to a v6 address object
* @memberof Address6
* @static
* @param {bigint} bigInt - a BigInt to convert
* @returns {Address6}
* @example
* var bigInt = BigInt('1000000000000');
* var address = Address6.fromBigInt(bigInt);
* address.correctForm(); // '::e8:d4a5:1000'
*/
static fromBigInt(bigInt: bigint): Address6;
/**
* Convert a URL (with optional port number) to an address object
* @memberof Address6
* @static
* @param {string} url - a URL with optional port number
* @example
* var addressAndPort = Address6.fromURL('http://[ffff::]:8080/foo/');
* addressAndPort.address.correctForm(); // 'ffff::'
* addressAndPort.port; // 8080
*/
static fromURL(url: string): {
error: string;
address: null;
port: null;
} | {
address: Address6;
port: number | null;
error?: undefined;
};
/**
* Create an IPv6-mapped address given an IPv4 address
* @memberof Address6
* @static
* @param {string} address - An IPv4 address string
* @returns {Address6}
* @example
* var address = Address6.fromAddress4('192.168.0.1');
* address.correctForm(); // '::ffff:c0a8:1'
* address.to4in6(); // '::ffff:192.168.0.1'
*/
static fromAddress4(address: string): Address6;
/**
* Return an address from ip6.arpa form
* @memberof Address6
* @static
* @param {string} arpaFormAddress - an 'ip6.arpa' form address
* @returns {Adress6}
* @example
* var address = Address6.fromArpa(e.f.f.f.3.c.2.6.f.f.f.e.6.6.8.e.1.0.6.7.9.4.e.c.0.0.0.0.1.0.0.2.ip6.arpa.)
* address.correctForm(); // '2001:0:ce49:7601:e866:efff:62c3:fffe'
*/
static fromArpa(arpaFormAddress: string): Address6;
/**
* Return the Microsoft UNC transcription of the address
* @memberof Address6
* @instance
* @returns {String} the Microsoft UNC transcription of the address
*/
microsoftTranscription(): string;
/**
* Return the first n bits of the address, defaulting to the subnet mask
* @memberof Address6
* @instance
* @param {number} [mask=subnet] - the number of bits to mask
* @returns {String} the first n bits of the address as a string
*/
mask(mask?: number): string;
/**
* Return the number of possible subnets of a given size in the address
* @memberof Address6
* @instance
* @param {number} [subnetSize=128] - the subnet size
* @returns {String}
*/
possibleSubnets(subnetSize?: number): string;
/**
* Helper function getting start address.
* @memberof Address6
* @instance
* @returns {bigint}
*/
_startAddress(): bigint;
/**
* The first address in the range given by this address' subnet
* Often referred to as the Network Address.
* @memberof Address6
* @instance
* @returns {Address6}
*/
startAddress(): Address6;
/**
* The first host address in the range given by this address's subnet ie
* the first address after the Network Address
* @memberof Address6
* @instance
* @returns {Address6}
*/
startAddressExclusive(): Address6;
/**
* Helper function getting end address.
* @memberof Address6
* @instance
* @returns {bigint}
*/
_endAddress(): bigint;
/**
* The last address in the range given by this address' subnet
* Often referred to as the Broadcast
* @memberof Address6
* @instance
* @returns {Address6}
*/
endAddress(): Address6;
/**
* The last host address in the range given by this address's subnet ie
* the last address prior to the Broadcast Address
* @memberof Address6
* @instance
* @returns {Address6}
*/
endAddressExclusive(): Address6;
/**
* Return the scope of the address
* @memberof Address6
* @instance
* @returns {String}
*/
getScope(): string;
/**
* Return the type of the address
* @memberof Address6
* @instance
* @returns {String}
*/
getType(): string;
/**
* Return the bits in the given range as a BigInt
* @memberof Address6
* @instance
* @returns {bigint}
*/
getBits(start: number, end: number): bigint;
/**
* Return the bits in the given range as a base-2 string
* @memberof Address6
* @instance
* @returns {String}
*/
getBitsBase2(start: number, end: number): string;
/**
* Return the bits in the given range as a base-16 string
* @memberof Address6
* @instance
* @returns {String}
*/
getBitsBase16(start: number, end: number): string;
/**
* Return the bits that are set past the subnet mask length
* @memberof Address6
* @instance
* @returns {String}
*/
getBitsPastSubnet(): string;
/**
* Return the reversed ip6.arpa form of the address
* @memberof Address6
* @param {Object} options
* @param {boolean} options.omitSuffix - omit the "ip6.arpa" suffix
* @instance
* @returns {String}
*/
reverseForm(options?: common.ReverseFormOptions): string;
/**
* Return the correct form of the address
* @memberof Address6
* @instance
* @returns {String}
*/
correctForm(): string;
/**
* Return a zero-padded base-2 string representation of the address
* @memberof Address6
* @instance
* @returns {String}
* @example
* var address = new Address6('2001:4860:4001:803::1011');
* address.binaryZeroPad();
* // '0010000000000001010010000110000001000000000000010000100000000011
* // 0000000000000000000000000000000000000000000000000001000000010001'
*/
binaryZeroPad(): string;
parse4in6(address: string): string;
parse(address: string): string[];
/**
* Return the canonical form of the address
* @memberof Address6
* @instance
* @returns {String}
*/
canonicalForm(): string;
/**
* Return the decimal form of the address
* @memberof Address6
* @instance
* @returns {String}
*/
decimal(): string;
/**
* Return the address as a BigInt
* @memberof Address6
* @instance
* @returns {bigint}
*/
bigInt(): bigint;
/**
* Return the last two groups of this address as an IPv4 address string
* @memberof Address6
* @instance
* @returns {Address4}
* @example
* var address = new Address6('2001:4860:4001::1825:bf11');
* address.to4().correctForm(); // '24.37.191.17'
*/
to4(): Address4;
/**
* Return the v4-in-v6 form of the address
* @memberof Address6
* @instance
* @returns {String}
*/
to4in6(): string;
/**
* Return an object containing the Teredo properties of the address
* @memberof Address6
* @instance
* @returns {Object}
*/
inspectTeredo(): TeredoProperties;
/**
* Return an object containing the 6to4 properties of the address
* @memberof Address6
* @instance
* @returns {Object}
*/
inspect6to4(): SixToFourProperties;
/**
* Return a v6 6to4 address from a v6 v4inv6 address
* @memberof Address6
* @instance
* @returns {Address6}
*/
to6to4(): Address6 | null;
/**
* Return a byte array
* @memberof Address6
* @instance
* @returns {Array}
*/
toByteArray(): number[];
/**
* Return an unsigned byte array
* @memberof Address6
* @instance
* @returns {Array}
*/
toUnsignedByteArray(): number[];
/**
* Convert a byte array to an Address6 object
* @memberof Address6
* @static
* @returns {Address6}
*/
static fromByteArray(bytes: Array<any>): Address6;
/**
* Convert an unsigned byte array to an Address6 object
* @memberof Address6
* @static
* @returns {Address6}
*/
static fromUnsignedByteArray(bytes: Array<any>): Address6;
/**
* Returns true if the given address is in the subnet of the current address
* @memberof Address6
* @instance
* @returns {boolean}
*/
isInSubnet: typeof common.isInSubnet;
/**
* Returns true if the address is correct, false otherwise
* @memberof Address6
* @instance
* @returns {boolean}
*/
isCorrect: (this: Address4 | Address6) => boolean;
/**
* Returns true if the address is in the canonical form, false otherwise
* @memberof Address6
* @instance
* @returns {boolean}
*/
isCanonical(): boolean;
/**
* Returns true if the address is a link local address, false otherwise
* @memberof Address6
* @instance
* @returns {boolean}
*/
isLinkLocal(): boolean;
/**
* Returns true if the address is a multicast address, false otherwise
* @memberof Address6
* @instance
* @returns {boolean}
*/
isMulticast(): boolean;
/**
* Returns true if the address is a v4-in-v6 address, false otherwise
* @memberof Address6
* @instance
* @returns {boolean}
*/
is4(): boolean;
/**
* Returns true if the address is a Teredo address, false otherwise
* @memberof Address6
* @instance
* @returns {boolean}
*/
isTeredo(): boolean;
/**
* Returns true if the address is a 6to4 address, false otherwise
* @memberof Address6
* @instance
* @returns {boolean}
*/
is6to4(): boolean;
/**
* Returns true if the address is a loopback address, false otherwise
* @memberof Address6
* @instance
* @returns {boolean}
*/
isLoopback(): boolean;
/**
* @returns {String} the address in link form with a default port of 80
*/
href(optionalPort?: number | string): string;
/**
* @returns {String} a link suitable for conveying the address via a URL hash
*/
link(options?: {
className?: string;
prefix?: string;
v4?: boolean;
}): string;
/**
* Groups an address
* @returns {String}
*/
group(): string;
/**
* Generate a regular expression string that can be used to find or validate
* all variations of this address
* @memberof Address6
* @instance
* @param {boolean} substringSearch
* @returns {string}
*/
regularExpressionString(this: Address6, substringSearch?: boolean): string;
/**
* Generate a regular expression that can be used to find or validate all
* variations of this address.
* @memberof Address6
* @instance
* @param {boolean} substringSearch
* @returns {RegExp}
*/
regularExpression(this: Address6, substringSearch?: boolean): RegExp;
}
export {};
//# sourceMappingURL=ipv6.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"ipv6.d.ts","sourceRoot":"","sources":["../src/ipv6.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,MAAM,UAAU,CAAC;AAInC,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AA4DlC,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,gBAAgB;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE;QACT,QAAQ,EAAE,OAAO,CAAC;QAClB,cAAc,EAAE,OAAO,CAAC;QACxB,eAAe,EAAE,OAAO,CAAC;QACzB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,qBAAa,QAAQ;IACnB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAM;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,CAAM;IAC1B,MAAM,EAAE,MAAM,CAAU;IACxB,UAAU,EAAE,MAAM,CAAO;IACzB,EAAE,EAAE,OAAO,CAAS;IACpB,IAAI,EAAE,MAAM,CAAM;gBAEN,OAAO,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM;IA0CpD,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAWxC;;;;;;;;;;OAUG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,QAAQ;IAY3C;;;;;;;;;OASG;IACH,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,MAAM;;;;;;;;;IA4D1B;;;;;;;;;;OAUG;IACH,MAAM,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ;IAQ9C;;;;;;;;;OASG;IACH,MAAM,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,QAAQ;IAsBlD;;;;;OAKG;IACH,sBAAsB,IAAI,MAAM;IAIhC;;;;;;OAMG;IACH,IAAI,CAAC,IAAI,GAAE,MAAwB,GAAG,MAAM;IAI5C;;;;;;OAMG;IAEH,eAAe,CAAC,UAAU,GAAE,MAAY,GAAG,MAAM;IAYjD;;;;;OAKG;IACH,aAAa,IAAI,MAAM;IAIvB;;;;;;OAMG;IACH,YAAY,IAAI,QAAQ;IAIxB;;;;;;OAMG;IACH,qBAAqB,IAAI,QAAQ;IAKjC;;;;;OAKG;IACH,WAAW,IAAI,MAAM;IAIrB;;;;;;OAMG;IACH,UAAU,IAAI,QAAQ;IAItB;;;;;;OAMG;IACH,mBAAmB,IAAI,QAAQ;IAK/B;;;;;OAKG;IACH,QAAQ,IAAI,MAAM;IAUlB;;;;;OAKG;IACH,OAAO,IAAI,MAAM;IAUjB;;;;;OAKG;IACH,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAI3C;;;;;OAKG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAIhD;;;;;OAKG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM;IAYjD;;;;;OAKG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;;;;;;OAOG;IACH,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,kBAAkB,GAAG,MAAM;IA6BxD;;;;;OAKG;IACH,WAAW,IAAI,MAAM;IAqDrB;;;;;;;;;;OAUG;IACH,aAAa,IAAI,MAAM;IAKvB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM;IAiClC,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE;IA0EhC;;;;;OAKG;IACH,aAAa,IAAI,MAAM;IAIvB;;;;;OAKG;IACH,OAAO,IAAI,MAAM;IAIjB;;;;;OAKG;IACH,MAAM,IAAI,MAAM;IAIhB;;;;;;;;OAQG;IACH,GAAG,IAAI,QAAQ;IAMf;;;;;OAKG;IACH,MAAM,IAAI,MAAM;IAehB;;;;;OAKG;IACH,aAAa,IAAI,gBAAgB;IA0DjC;;;;;OAKG;IACH,WAAW,IAAI,mBAAmB;IAgBlC;;;;;OAKG;IACH,MAAM,IAAI,QAAQ,GAAG,IAAI;IAgBzB;;;;;OAKG;IACH,WAAW,IAAI,MAAM,EAAE;IAcvB;;;;;OAKG;IACH,mBAAmB,IAAI,MAAM,EAAE;IAI/B;;;;;OAKG;IACH,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,QAAQ;IAIjD;;;;;OAKG;IACH,MAAM,CAAC,qBAAqB,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,GAAG,QAAQ;IAezD;;;;;OAKG;IACH,UAAU,2BAAqB;IAE/B;;;;;OAKG;IACH,SAAS,yCAAqC;IAE9C;;;;;OAKG;IACH,WAAW,IAAI,OAAO;IAItB;;;;;OAKG;IACH,WAAW,IAAI,OAAO;IAYtB;;;;;OAKG;IACH,WAAW,IAAI,OAAO;IAItB;;;;;OAKG;IACH,GAAG,IAAI,OAAO;IAId;;;;;OAKG;IACH,QAAQ,IAAI,OAAO;IAInB;;;;;OAKG;IACH,MAAM,IAAI,OAAO;IAIjB;;;;;OAKG;IACH,UAAU,IAAI,OAAO;IAMrB;;OAEG;IACH,IAAI,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM;IAU5C;;OAEG;IACH,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,EAAE,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,MAAM;IAgC7E;;;OAGG;IACH,KAAK,IAAI,MAAM;IA8Cf;;;;;;;OAOG;IACH,uBAAuB,CAAC,IAAI,EAAE,QAAQ,EAAE,eAAe,GAAE,OAAe,GAAG,MAAM;IAgDjF;;;;;;;OAOG;IACH,iBAAiB,CAAC,IAAI,EAAE,QAAQ,EAAE,eAAe,GAAE,OAAe,GAAG,MAAM;CAI5E"}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,5 @@
export declare const BITS = 32;
export declare const GROUPS = 4;
export declare const RE_ADDRESS: RegExp;
export declare const RE_SUBNET_STRING: RegExp;
//# sourceMappingURL=constants.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/v4/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,KAAK,CAAC;AACvB,eAAO,MAAM,MAAM,IAAI,CAAC;AAExB,eAAO,MAAM,UAAU,QAC8I,CAAC;AAEtK,eAAO,MAAM,gBAAgB,QAAe,CAAC"}

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RE_SUBNET_STRING = exports.RE_ADDRESS = exports.GROUPS = exports.BITS = void 0;
exports.BITS = 32;
exports.GROUPS = 4;
exports.RE_ADDRESS = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/g;
exports.RE_SUBNET_STRING = /\/\d{1,2}$/;
//# sourceMappingURL=constants.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/v4/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,IAAI,GAAG,EAAE,CAAC;AACV,QAAA,MAAM,GAAG,CAAC,CAAC;AAEX,QAAA,UAAU,GACrB,mKAAmK,CAAC;AAEzJ,QAAA,gBAAgB,GAAG,YAAY,CAAC"}

View File

@@ -0,0 +1,45 @@
export declare const BITS = 128;
export declare const GROUPS = 8;
/**
* Represents IPv6 address scopes
* @memberof Address6
* @static
*/
export declare const SCOPES: {
[key: number]: string | undefined;
};
/**
* Represents IPv6 address types
* @memberof Address6
* @static
*/
export declare const TYPES: {
[key: string]: string | undefined;
};
/**
* A regular expression that matches bad characters in an IPv6 address
* @memberof Address6
* @static
*/
export declare const RE_BAD_CHARACTERS: RegExp;
/**
* A regular expression that matches an incorrect IPv6 address
* @memberof Address6
* @static
*/
export declare const RE_BAD_ADDRESS: RegExp;
/**
* A regular expression that matches an IPv6 subnet
* @memberof Address6
* @static
*/
export declare const RE_SUBNET_STRING: RegExp;
/**
* A regular expression that matches an IPv6 zone
* @memberof Address6
* @static
*/
export declare const RE_ZONE_STRING: RegExp;
export declare const RE_URL: RegExp;
export declare const RE_URL_WITH_PORT: RegExp;
//# sourceMappingURL=constants.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/v6/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,MAAM,CAAC;AACxB,eAAO,MAAM,MAAM,IAAI,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,MAAM,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAS9C,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,KAAK,EAAE;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;CAuB7C,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,QAAqB,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAA6C,CAAC;AAEzE;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAqB,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,cAAc,QAAS,CAAC;AAErC,eAAO,MAAM,MAAM,QAAgC,CAAC;AACpD,eAAO,MAAM,gBAAgB,QAAkC,CAAC"}

View File

@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RE_URL_WITH_PORT = exports.RE_URL = exports.RE_ZONE_STRING = exports.RE_SUBNET_STRING = exports.RE_BAD_ADDRESS = exports.RE_BAD_CHARACTERS = exports.TYPES = exports.SCOPES = exports.GROUPS = exports.BITS = void 0;
exports.BITS = 128;
exports.GROUPS = 8;
/**
* Represents IPv6 address scopes
* @memberof Address6
* @static
*/
exports.SCOPES = {
0: 'Reserved',
1: 'Interface local',
2: 'Link local',
4: 'Admin local',
5: 'Site local',
8: 'Organization local',
14: 'Global',
15: 'Reserved',
};
/**
* Represents IPv6 address types
* @memberof Address6
* @static
*/
exports.TYPES = {
'ff01::1/128': 'Multicast (All nodes on this interface)',
'ff01::2/128': 'Multicast (All routers on this interface)',
'ff02::1/128': 'Multicast (All nodes on this link)',
'ff02::2/128': 'Multicast (All routers on this link)',
'ff05::2/128': 'Multicast (All routers in this site)',
'ff02::5/128': 'Multicast (OSPFv3 AllSPF routers)',
'ff02::6/128': 'Multicast (OSPFv3 AllDR routers)',
'ff02::9/128': 'Multicast (RIP routers)',
'ff02::a/128': 'Multicast (EIGRP routers)',
'ff02::d/128': 'Multicast (PIM routers)',
'ff02::16/128': 'Multicast (MLDv2 reports)',
'ff01::fb/128': 'Multicast (mDNSv6)',
'ff02::fb/128': 'Multicast (mDNSv6)',
'ff05::fb/128': 'Multicast (mDNSv6)',
'ff02::1:2/128': 'Multicast (All DHCP servers and relay agents on this link)',
'ff05::1:2/128': 'Multicast (All DHCP servers and relay agents in this site)',
'ff02::1:3/128': 'Multicast (All DHCP servers on this link)',
'ff05::1:3/128': 'Multicast (All DHCP servers in this site)',
'::/128': 'Unspecified',
'::1/128': 'Loopback',
'ff00::/8': 'Multicast',
'fe80::/10': 'Link-local unicast',
};
/**
* A regular expression that matches bad characters in an IPv6 address
* @memberof Address6
* @static
*/
exports.RE_BAD_CHARACTERS = /([^0-9a-f:/%])/gi;
/**
* A regular expression that matches an incorrect IPv6 address
* @memberof Address6
* @static
*/
exports.RE_BAD_ADDRESS = /([0-9a-f]{5,}|:{3,}|[^:]:$|^:[^:]|\/$)/gi;
/**
* A regular expression that matches an IPv6 subnet
* @memberof Address6
* @static
*/
exports.RE_SUBNET_STRING = /\/\d{1,3}(?=%|$)/;
/**
* A regular expression that matches an IPv6 zone
* @memberof Address6
* @static
*/
exports.RE_ZONE_STRING = /%.*$/;
exports.RE_URL = /^\[{0,1}([0-9a-f:]+)\]{0,1}/;
exports.RE_URL_WITH_PORT = /\[([0-9a-f:]+)\]:([0-9]{1,5})/;
//# sourceMappingURL=constants.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/v6/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,IAAI,GAAG,GAAG,CAAC;AACX,QAAA,MAAM,GAAG,CAAC,CAAC;AAExB;;;;GAIG;AACU,QAAA,MAAM,GAA0C;IAC3D,CAAC,EAAE,UAAU;IACb,CAAC,EAAE,iBAAiB;IACpB,CAAC,EAAE,YAAY;IACf,CAAC,EAAE,aAAa;IAChB,CAAC,EAAE,YAAY;IACf,CAAC,EAAE,oBAAoB;IACvB,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,UAAU;CACN,CAAC;AAEX;;;;GAIG;AACU,QAAA,KAAK,GAA0C;IAC1D,aAAa,EAAE,yCAAyC;IACxD,aAAa,EAAE,2CAA2C;IAC1D,aAAa,EAAE,oCAAoC;IACnD,aAAa,EAAE,sCAAsC;IACrD,aAAa,EAAE,sCAAsC;IACrD,aAAa,EAAE,mCAAmC;IAClD,aAAa,EAAE,kCAAkC;IACjD,aAAa,EAAE,yBAAyB;IACxC,aAAa,EAAE,2BAA2B;IAC1C,aAAa,EAAE,yBAAyB;IACxC,cAAc,EAAE,2BAA2B;IAC3C,cAAc,EAAE,oBAAoB;IACpC,cAAc,EAAE,oBAAoB;IACpC,cAAc,EAAE,oBAAoB;IACpC,eAAe,EAAE,4DAA4D;IAC7E,eAAe,EAAE,4DAA4D;IAC7E,eAAe,EAAE,2CAA2C;IAC5D,eAAe,EAAE,2CAA2C;IAC5D,QAAQ,EAAE,aAAa;IACvB,SAAS,EAAE,UAAU;IACrB,UAAU,EAAE,WAAW;IACvB,WAAW,EAAE,oBAAoB;CACzB,CAAC;AAEX;;;;GAIG;AACU,QAAA,iBAAiB,GAAG,kBAAkB,CAAC;AAEpD;;;;GAIG;AACU,QAAA,cAAc,GAAG,0CAA0C,CAAC;AAEzE;;;;GAIG;AACU,QAAA,gBAAgB,GAAG,kBAAkB,CAAC;AAEnD;;;;GAIG;AACU,QAAA,cAAc,GAAG,MAAM,CAAC;AAExB,QAAA,MAAM,GAAG,6BAA6B,CAAC;AACvC,QAAA,gBAAgB,GAAG,+BAA+B,CAAC"}

View File

@@ -0,0 +1,18 @@
/**
* @returns {String} the string with all zeroes contained in a <span>
*/
export declare function spanAllZeroes(s: string): string;
/**
* @returns {String} the string with each character contained in a <span>
*/
export declare function spanAll(s: string, offset?: number): string;
/**
* @returns {String} the string with leading zeroes contained in a <span>
*/
export declare function spanLeadingZeroes(address: string): string;
/**
* Groups an address
* @returns {String} a grouped address
*/
export declare function simpleGroup(addressString: string, offset?: number): string[];
//# sourceMappingURL=helpers.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/v6/helpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,GAAG,MAAM,CAQ7D;AAMD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAIzD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,GAAG,MAAM,EAAE,CAU/E"}

View File

@@ -0,0 +1,45 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.spanAllZeroes = spanAllZeroes;
exports.spanAll = spanAll;
exports.spanLeadingZeroes = spanLeadingZeroes;
exports.simpleGroup = simpleGroup;
/**
* @returns {String} the string with all zeroes contained in a <span>
*/
function spanAllZeroes(s) {
return s.replace(/(0+)/g, '<span class="zero">$1</span>');
}
/**
* @returns {String} the string with each character contained in a <span>
*/
function spanAll(s, offset = 0) {
const letters = s.split('');
return letters
.map((n, i) => `<span class="digit value-${n} position-${i + offset}">${spanAllZeroes(n)}</span>`)
.join('');
}
function spanLeadingZeroesSimple(group) {
return group.replace(/^(0+)/, '<span class="zero">$1</span>');
}
/**
* @returns {String} the string with leading zeroes contained in a <span>
*/
function spanLeadingZeroes(address) {
const groups = address.split(':');
return groups.map((g) => spanLeadingZeroesSimple(g)).join(':');
}
/**
* Groups an address
* @returns {String} a grouped address
*/
function simpleGroup(addressString, offset = 0) {
const groups = addressString.split(':');
return groups.map((g, i) => {
if (/group-v4/.test(g)) {
return g;
}
return `<span class="hover-group group-${i + offset}">${spanLeadingZeroesSimple(g)}</span>`;
});
}
//# sourceMappingURL=helpers.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/v6/helpers.ts"],"names":[],"mappings":";;AAGA,sCAEC;AAKD,0BAQC;AASD,8CAIC;AAMD,kCAUC;AA/CD;;GAEG;AACH,SAAgB,aAAa,CAAC,CAAS;IACrC,OAAO,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,SAAgB,OAAO,CAAC,CAAS,EAAE,SAAiB,CAAC;IACnD,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAE5B,OAAO,OAAO;SACX,GAAG,CACF,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,4BAA4B,CAAC,aAAa,CAAC,GAAG,MAAM,KAAK,aAAa,CAAC,CAAC,CAAC,SAAS,CAC7F;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;AACd,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAa;IAC5C,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,8BAA8B,CAAC,CAAC;AAChE,CAAC;AAED;;GAEG;AACH,SAAgB,iBAAiB,CAAC,OAAe;IAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAElC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,uBAAuB,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACjE,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,aAAqB,EAAE,SAAiB,CAAC;IACnE,MAAM,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAExC,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACzB,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACvB,OAAO,CAAC,CAAC;QACX,CAAC;QAED,OAAO,kCAAkC,CAAC,GAAG,MAAM,KAAK,uBAAuB,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9F,CAAC,CAAC,CAAC;AACL,CAAC"}

View File

@@ -0,0 +1,6 @@
export declare function groupPossibilities(possibilities: string[]): string;
export declare function padGroup(group: string): string;
export declare const ADDRESS_BOUNDARY = "[^A-Fa-f0-9:]";
export declare function simpleRegularExpression(groups: string[]): string;
export declare function possibleElisions(elidedGroups: number, moreLeft?: boolean, moreRight?: boolean): string;
//# sourceMappingURL=regular-expressions.d.ts.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"regular-expressions.d.ts","sourceRoot":"","sources":["../../src/v6/regular-expressions.ts"],"names":[],"mappings":"AAEA,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,EAAE,GAAG,MAAM,CAElE;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM9C;AAED,eAAO,MAAM,gBAAgB,kBAAkB,CAAC;AAEhD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,UA+BvD;AAED,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,QAAQ,CAAC,EAAE,OAAO,EAClB,SAAS,CAAC,EAAE,OAAO,GAClB,MAAM,CAwCR"}

View File

@@ -0,0 +1,95 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ADDRESS_BOUNDARY = void 0;
exports.groupPossibilities = groupPossibilities;
exports.padGroup = padGroup;
exports.simpleRegularExpression = simpleRegularExpression;
exports.possibleElisions = possibleElisions;
const v6 = __importStar(require("./constants"));
function groupPossibilities(possibilities) {
return `(${possibilities.join('|')})`;
}
function padGroup(group) {
if (group.length < 4) {
return `0{0,${4 - group.length}}${group}`;
}
return group;
}
exports.ADDRESS_BOUNDARY = '[^A-Fa-f0-9:]';
function simpleRegularExpression(groups) {
const zeroIndexes = [];
groups.forEach((group, i) => {
const groupInteger = parseInt(group, 16);
if (groupInteger === 0) {
zeroIndexes.push(i);
}
});
// You can technically elide a single 0, this creates the regular expressions
// to match that eventuality
const possibilities = zeroIndexes.map((zeroIndex) => groups
.map((group, i) => {
if (i === zeroIndex) {
const elision = i === 0 || i === v6.GROUPS - 1 ? ':' : '';
return groupPossibilities([padGroup(group), elision]);
}
return padGroup(group);
})
.join(':'));
// The simplest case
possibilities.push(groups.map(padGroup).join(':'));
return groupPossibilities(possibilities);
}
function possibleElisions(elidedGroups, moreLeft, moreRight) {
const left = moreLeft ? '' : ':';
const right = moreRight ? '' : ':';
const possibilities = [];
// 1. elision of everything (::)
if (!moreLeft && !moreRight) {
possibilities.push('::');
}
// 2. complete elision of the middle
if (moreLeft && moreRight) {
possibilities.push('');
}
if ((moreRight && !moreLeft) || (!moreRight && moreLeft)) {
// 3. complete elision of one side
possibilities.push(':');
}
// 4. elision from the left side
possibilities.push(`${left}(:0{1,4}){1,${elidedGroups - 1}}`);
// 5. elision from the right side
possibilities.push(`(0{1,4}:){1,${elidedGroups - 1}}${right}`);
// 6. no elision
possibilities.push(`(0{1,4}:){${elidedGroups - 1}}0{1,4}`);
// 7. elision (including sloppy elision) from the middle
for (let groups = 1; groups < elidedGroups - 1; groups++) {
for (let position = 1; position < elidedGroups - groups; position++) {
possibilities.push(`(0{1,4}:){${position}}:(0{1,4}:){${elidedGroups - position - groups - 1}}0{1,4}`);
}
}
return groupPossibilities(possibilities);
}
//# sourceMappingURL=regular-expressions.js.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"regular-expressions.js","sourceRoot":"","sources":["../../src/v6/regular-expressions.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,gDAEC;AAED,4BAMC;AAID,0DA+BC;AAED,4CA4CC;AA7FD,gDAAkC;AAElC,SAAgB,kBAAkB,CAAC,aAAuB;IACxD,OAAO,IAAI,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;AACxC,CAAC;AAED,SAAgB,QAAQ,CAAC,KAAa;IACpC,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,OAAO,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;IAC5C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAEY,QAAA,gBAAgB,GAAG,eAAe,CAAC;AAEhD,SAAgB,uBAAuB,CAAC,MAAgB;IACtD,MAAM,WAAW,GAAa,EAAE,CAAC;IAEjC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAC1B,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAEzC,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;YACvB,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,6EAA6E;IAC7E,4BAA4B;IAC5B,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAClD,MAAM;SACH,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE;QAChB,IAAI,CAAC,KAAK,SAAS,EAAE,CAAC;YACpB,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;YAE1D,OAAO,kBAAkB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;QACxD,CAAC;QAED,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC,CAAC;SACD,IAAI,CAAC,GAAG,CAAC,CACb,CAAC;IAEF,oBAAoB;IACpB,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAEnD,OAAO,kBAAkB,CAAC,aAAa,CAAC,CAAC;AAC3C,CAAC;AAED,SAAgB,gBAAgB,CAC9B,YAAoB,EACpB,QAAkB,EAClB,SAAmB;IAEnB,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IACjC,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;IAEnC,MAAM,aAAa,GAAG,EAAE,CAAC;IAEzB,gCAAgC;IAChC,IAAI,CAAC,QAAQ,IAAI,CAAC,SAAS,EAAE,CAAC;QAC5B,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,oCAAoC;IACpC,IAAI,QAAQ,IAAI,SAAS,EAAE,CAAC;QAC1B,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IAED,IAAI,CAAC,SAAS,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,QAAQ,CAAC,EAAE,CAAC;QACzD,kCAAkC;QAClC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1B,CAAC;IAED,gCAAgC;IAChC,aAAa,CAAC,IAAI,CAAC,GAAG,IAAI,eAAe,YAAY,GAAG,CAAC,GAAG,CAAC,CAAC;IAE9D,iCAAiC;IACjC,aAAa,CAAC,IAAI,CAAC,eAAe,YAAY,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IAE/D,gBAAgB;IAChB,aAAa,CAAC,IAAI,CAAC,aAAa,YAAY,GAAG,CAAC,SAAS,CAAC,CAAC;IAE3D,wDAAwD;IACxD,KAAK,IAAI,MAAM,GAAG,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;QACzD,KAAK,IAAI,QAAQ,GAAG,CAAC,EAAE,QAAQ,GAAG,YAAY,GAAG,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC;YACpE,aAAa,CAAC,IAAI,CAChB,aAAa,QAAQ,eAAe,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,CAAC,SAAS,CAClF,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,kBAAkB,CAAC,aAAa,CAAC,CAAC;AAC3C,CAAC"}

View File

@@ -0,0 +1,78 @@
{
"name": "ip-address",
"description": "A library for parsing IPv4 and IPv6 IP addresses in node and the browser.",
"keywords": [
"ipv6",
"ipv4",
"browser",
"validation"
],
"version": "10.0.1",
"author": "Beau Gunderson <beau@beaugunderson.com> (https://beaugunderson.com/)",
"license": "MIT",
"main": "dist/ip-address.js",
"types": "dist/ip-address.d.ts",
"scripts": {
"docs": "documentation build --github --output docs --format html ./ip-address.js",
"build": "rm -rf dist; mkdir dist; tsc",
"prepack": "npm run build",
"release": "release-it",
"test-ci": "nyc mocha",
"test": "mocha",
"watch": "mocha --watch"
},
"nyc": {
"extension": [
".ts"
],
"exclude": [
"**/*.d.ts",
".eslintrc.js",
"coverage/",
"dist/",
"test/",
"tmp/"
],
"reporter": [
"html",
"lcov",
"text"
],
"all": true
},
"engines": {
"node": ">= 12"
},
"files": [
"src",
"dist"
],
"repository": {
"type": "git",
"url": "git://github.com/beaugunderson/ip-address.git"
},
"devDependencies": {
"@types/chai": "^5.0.0",
"@types/mocha": "^10.0.8",
"@typescript-eslint/eslint-plugin": "^8.8.0",
"@typescript-eslint/parser": "^8.8.0",
"chai": "^5.1.1",
"documentation": "^14.0.3",
"eslint": "^8.50.0",
"eslint_d": "^14.0.4",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-import": "^2.30.0",
"eslint-plugin-jsx-a11y": "^6.10.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-sort-imports-es6-autofix": "^0.6.0",
"mocha": "^10.7.3",
"nyc": "^17.1.0",
"prettier": "^3.3.3",
"release-it": "^17.6.0",
"source-map-support": "^0.5.21",
"tsx": "^4.19.1",
"typescript": "<5.6.0"
}
}

View File

@@ -0,0 +1,11 @@
export class AddressError extends Error {
parseMessage?: string;
constructor(message: string, parseMessage?: string) {
super(message);
this.name = 'AddressError';
this.parseMessage = parseMessage;
}
}

View File

@@ -0,0 +1,55 @@
import { Address4 } from './ipv4';
import { Address6 } from './ipv6';
export interface ReverseFormOptions {
omitSuffix?: boolean;
}
export function isInSubnet(this: Address4 | Address6, address: Address4 | Address6) {
if (this.subnetMask < address.subnetMask) {
return false;
}
if (this.mask(address.subnetMask) === address.mask()) {
return true;
}
return false;
}
export function isCorrect(defaultBits: number) {
return function (this: Address4 | Address6) {
if (this.addressMinusSuffix !== this.correctForm()) {
return false;
}
if (this.subnetMask === defaultBits && !this.parsedSubnet) {
return true;
}
return this.parsedSubnet === String(this.subnetMask);
};
}
export function numberToPaddedHex(number: number) {
return number.toString(16).padStart(2, '0');
}
export function stringToPaddedHex(numberString: string) {
return numberToPaddedHex(parseInt(numberString, 10));
}
/**
* @param binaryValue Binary representation of a value (e.g. `10`)
* @param position Byte position, where 0 is the least significant bit
*/
export function testBit(binaryValue: string, position: number): boolean {
const { length } = binaryValue;
if (position > length) {
return false;
}
const positionInString = length - position;
return binaryValue.substring(positionInString, positionInString + 1) === '1';
}

View File

@@ -0,0 +1,7 @@
export { Address4 } from './ipv4';
export { Address6 } from './ipv6';
export { AddressError } from './address-error';
import * as helpers from './v6/helpers';
export const v6 = { helpers };

View File

@@ -0,0 +1,356 @@
/* eslint-disable no-param-reassign */
import * as common from './common';
import * as constants from './v4/constants';
import { AddressError } from './address-error';
/**
* Represents an IPv4 address
* @class Address4
* @param {string} address - An IPv4 address string
*/
export class Address4 {
address: string;
addressMinusSuffix?: string;
groups: number = constants.GROUPS;
parsedAddress: string[] = [];
parsedSubnet: string = '';
subnet: string = '/32';
subnetMask: number = 32;
v4: boolean = true;
constructor(address: string) {
this.address = address;
const subnet = constants.RE_SUBNET_STRING.exec(address);
if (subnet) {
this.parsedSubnet = subnet[0].replace('/', '');
this.subnetMask = parseInt(this.parsedSubnet, 10);
this.subnet = `/${this.subnetMask}`;
if (this.subnetMask < 0 || this.subnetMask > constants.BITS) {
throw new AddressError('Invalid subnet mask.');
}
address = address.replace(constants.RE_SUBNET_STRING, '');
}
this.addressMinusSuffix = address;
this.parsedAddress = this.parse(address);
}
static isValid(address: string): boolean {
try {
// eslint-disable-next-line no-new
new Address4(address);
return true;
} catch (e) {
return false;
}
}
/*
* Parses a v4 address
*/
parse(address: string) {
const groups = address.split('.');
if (!address.match(constants.RE_ADDRESS)) {
throw new AddressError('Invalid IPv4 address.');
}
return groups;
}
/**
* Returns the correct form of an address
* @memberof Address4
* @instance
* @returns {String}
*/
correctForm(): string {
return this.parsedAddress.map((part) => parseInt(part, 10)).join('.');
}
/**
* Returns true if the address is correct, false otherwise
* @memberof Address4
* @instance
* @returns {Boolean}
*/
isCorrect = common.isCorrect(constants.BITS);
/**
* Converts a hex string to an IPv4 address object
* @memberof Address4
* @static
* @param {string} hex - a hex string to convert
* @returns {Address4}
*/
static fromHex(hex: string): Address4 {
const padded = hex.replace(/:/g, '').padStart(8, '0');
const groups = [];
let i;
for (i = 0; i < 8; i += 2) {
const h = padded.slice(i, i + 2);
groups.push(parseInt(h, 16));
}
return new Address4(groups.join('.'));
}
/**
* Converts an integer into a IPv4 address object
* @memberof Address4
* @static
* @param {integer} integer - a number to convert
* @returns {Address4}
*/
static fromInteger(integer: number): Address4 {
return Address4.fromHex(integer.toString(16));
}
/**
* Return an address from in-addr.arpa form
* @memberof Address4
* @static
* @param {string} arpaFormAddress - an 'in-addr.arpa' form ipv4 address
* @returns {Adress4}
* @example
* var address = Address4.fromArpa(42.2.0.192.in-addr.arpa.)
* address.correctForm(); // '192.0.2.42'
*/
static fromArpa(arpaFormAddress: string): Address4 {
// remove ending ".in-addr.arpa." or just "."
const leader = arpaFormAddress.replace(/(\.in-addr\.arpa)?\.$/, '');
const address = leader.split('.').reverse().join('.');
return new Address4(address);
}
/**
* Converts an IPv4 address object to a hex string
* @memberof Address4
* @instance
* @returns {String}
*/
toHex(): string {
return this.parsedAddress.map((part) => common.stringToPaddedHex(part)).join(':');
}
/**
* Converts an IPv4 address object to an array of bytes
* @memberof Address4
* @instance
* @returns {Array}
*/
toArray(): number[] {
return this.parsedAddress.map((part) => parseInt(part, 10));
}
/**
* Converts an IPv4 address object to an IPv6 address group
* @memberof Address4
* @instance
* @returns {String}
*/
toGroup6(): string {
const output = [];
let i;
for (i = 0; i < constants.GROUPS; i += 2) {
output.push(
`${common.stringToPaddedHex(this.parsedAddress[i])}${common.stringToPaddedHex(
this.parsedAddress[i + 1],
)}`,
);
}
return output.join(':');
}
/**
* Returns the address as a `bigint`
* @memberof Address4
* @instance
* @returns {bigint}
*/
bigInt(): bigint {
return BigInt(`0x${this.parsedAddress.map((n) => common.stringToPaddedHex(n)).join('')}`);
}
/**
* Helper function getting start address.
* @memberof Address4
* @instance
* @returns {bigint}
*/
_startAddress(): bigint {
return BigInt(`0b${this.mask() + '0'.repeat(constants.BITS - this.subnetMask)}`);
}
/**
* The first address in the range given by this address' subnet.
* Often referred to as the Network Address.
* @memberof Address4
* @instance
* @returns {Address4}
*/
startAddress(): Address4 {
return Address4.fromBigInt(this._startAddress());
}
/**
* The first host address in the range given by this address's subnet ie
* the first address after the Network Address
* @memberof Address4
* @instance
* @returns {Address4}
*/
startAddressExclusive(): Address4 {
const adjust = BigInt('1');
return Address4.fromBigInt(this._startAddress() + adjust);
}
/**
* Helper function getting end address.
* @memberof Address4
* @instance
* @returns {bigint}
*/
_endAddress(): bigint {
return BigInt(`0b${this.mask() + '1'.repeat(constants.BITS - this.subnetMask)}`);
}
/**
* The last address in the range given by this address' subnet
* Often referred to as the Broadcast
* @memberof Address4
* @instance
* @returns {Address4}
*/
endAddress(): Address4 {
return Address4.fromBigInt(this._endAddress());
}
/**
* The last host address in the range given by this address's subnet ie
* the last address prior to the Broadcast Address
* @memberof Address4
* @instance
* @returns {Address4}
*/
endAddressExclusive(): Address4 {
const adjust = BigInt('1');
return Address4.fromBigInt(this._endAddress() - adjust);
}
/**
* Converts a BigInt to a v4 address object
* @memberof Address4
* @static
* @param {bigint} bigInt - a BigInt to convert
* @returns {Address4}
*/
static fromBigInt(bigInt: bigint): Address4 {
return Address4.fromHex(bigInt.toString(16));
}
/**
* Returns the first n bits of the address, defaulting to the
* subnet mask
* @memberof Address4
* @instance
* @returns {String}
*/
mask(mask?: number): string {
if (mask === undefined) {
mask = this.subnetMask;
}
return this.getBitsBase2(0, mask);
}
/**
* Returns the bits in the given range as a base-2 string
* @memberof Address4
* @instance
* @returns {string}
*/
getBitsBase2(start: number, end: number): string {
return this.binaryZeroPad().slice(start, end);
}
/**
* Return the reversed ip6.arpa form of the address
* @memberof Address4
* @param {Object} options
* @param {boolean} options.omitSuffix - omit the "in-addr.arpa" suffix
* @instance
* @returns {String}
*/
reverseForm(options?: common.ReverseFormOptions): string {
if (!options) {
options = {};
}
const reversed = this.correctForm().split('.').reverse().join('.');
if (options.omitSuffix) {
return reversed;
}
return `${reversed}.in-addr.arpa.`;
}
/**
* Returns true if the given address is in the subnet of the current address
* @memberof Address4
* @instance
* @returns {boolean}
*/
isInSubnet = common.isInSubnet;
/**
* Returns true if the given address is a multicast address
* @memberof Address4
* @instance
* @returns {boolean}
*/
isMulticast(): boolean {
return this.isInSubnet(new Address4('224.0.0.0/4'));
}
/**
* Returns a zero-padded base-2 string representation of the address
* @memberof Address4
* @instance
* @returns {string}
*/
binaryZeroPad(): string {
return this.bigInt().toString(2).padStart(constants.BITS, '0');
}
/**
* Groups an IPv4 address for inclusion at the end of an IPv6 address
* @returns {String}
*/
groupForV6(): string {
const segments = this.parsedAddress;
return this.address.replace(
constants.RE_ADDRESS,
`<span class="hover-group group-v4 group-6">${segments
.slice(0, 2)
.join('.')}</span>.<span class="hover-group group-v4 group-7">${segments
.slice(2, 4)
.join('.')}</span>`,
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
export const BITS = 32;
export const GROUPS = 4;
export const RE_ADDRESS =
/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/g;
export const RE_SUBNET_STRING = /\/\d{1,2}$/;

View File

@@ -0,0 +1,79 @@
export const BITS = 128;
export const GROUPS = 8;
/**
* Represents IPv6 address scopes
* @memberof Address6
* @static
*/
export const SCOPES: { [key: number]: string | undefined } = {
0: 'Reserved',
1: 'Interface local',
2: 'Link local',
4: 'Admin local',
5: 'Site local',
8: 'Organization local',
14: 'Global',
15: 'Reserved',
} as const;
/**
* Represents IPv6 address types
* @memberof Address6
* @static
*/
export const TYPES: { [key: string]: string | undefined } = {
'ff01::1/128': 'Multicast (All nodes on this interface)',
'ff01::2/128': 'Multicast (All routers on this interface)',
'ff02::1/128': 'Multicast (All nodes on this link)',
'ff02::2/128': 'Multicast (All routers on this link)',
'ff05::2/128': 'Multicast (All routers in this site)',
'ff02::5/128': 'Multicast (OSPFv3 AllSPF routers)',
'ff02::6/128': 'Multicast (OSPFv3 AllDR routers)',
'ff02::9/128': 'Multicast (RIP routers)',
'ff02::a/128': 'Multicast (EIGRP routers)',
'ff02::d/128': 'Multicast (PIM routers)',
'ff02::16/128': 'Multicast (MLDv2 reports)',
'ff01::fb/128': 'Multicast (mDNSv6)',
'ff02::fb/128': 'Multicast (mDNSv6)',
'ff05::fb/128': 'Multicast (mDNSv6)',
'ff02::1:2/128': 'Multicast (All DHCP servers and relay agents on this link)',
'ff05::1:2/128': 'Multicast (All DHCP servers and relay agents in this site)',
'ff02::1:3/128': 'Multicast (All DHCP servers on this link)',
'ff05::1:3/128': 'Multicast (All DHCP servers in this site)',
'::/128': 'Unspecified',
'::1/128': 'Loopback',
'ff00::/8': 'Multicast',
'fe80::/10': 'Link-local unicast',
} as const;
/**
* A regular expression that matches bad characters in an IPv6 address
* @memberof Address6
* @static
*/
export const RE_BAD_CHARACTERS = /([^0-9a-f:/%])/gi;
/**
* A regular expression that matches an incorrect IPv6 address
* @memberof Address6
* @static
*/
export const RE_BAD_ADDRESS = /([0-9a-f]{5,}|:{3,}|[^:]:$|^:[^:]|\/$)/gi;
/**
* A regular expression that matches an IPv6 subnet
* @memberof Address6
* @static
*/
export const RE_SUBNET_STRING = /\/\d{1,3}(?=%|$)/;
/**
* A regular expression that matches an IPv6 zone
* @memberof Address6
* @static
*/
export const RE_ZONE_STRING = /%.*$/;
export const RE_URL = /^\[{0,1}([0-9a-f:]+)\]{0,1}/;
export const RE_URL_WITH_PORT = /\[([0-9a-f:]+)\]:([0-9]{1,5})/;

View File

@@ -0,0 +1,48 @@
/**
* @returns {String} the string with all zeroes contained in a <span>
*/
export function spanAllZeroes(s: string): string {
return s.replace(/(0+)/g, '<span class="zero">$1</span>');
}
/**
* @returns {String} the string with each character contained in a <span>
*/
export function spanAll(s: string, offset: number = 0): string {
const letters = s.split('');
return letters
.map(
(n, i) => `<span class="digit value-${n} position-${i + offset}">${spanAllZeroes(n)}</span>`,
)
.join('');
}
function spanLeadingZeroesSimple(group: string): string {
return group.replace(/^(0+)/, '<span class="zero">$1</span>');
}
/**
* @returns {String} the string with leading zeroes contained in a <span>
*/
export function spanLeadingZeroes(address: string): string {
const groups = address.split(':');
return groups.map((g) => spanLeadingZeroesSimple(g)).join(':');
}
/**
* Groups an address
* @returns {String} a grouped address
*/
export function simpleGroup(addressString: string, offset: number = 0): string[] {
const groups = addressString.split(':');
return groups.map((g, i) => {
if (/group-v4/.test(g)) {
return g;
}
return `<span class="hover-group group-${i + offset}">${spanLeadingZeroesSimple(g)}</span>`;
});
}

View File

@@ -0,0 +1,94 @@
import * as v6 from './constants';
export function groupPossibilities(possibilities: string[]): string {
return `(${possibilities.join('|')})`;
}
export function padGroup(group: string): string {
if (group.length < 4) {
return `0{0,${4 - group.length}}${group}`;
}
return group;
}
export const ADDRESS_BOUNDARY = '[^A-Fa-f0-9:]';
export function simpleRegularExpression(groups: string[]) {
const zeroIndexes: number[] = [];
groups.forEach((group, i) => {
const groupInteger = parseInt(group, 16);
if (groupInteger === 0) {
zeroIndexes.push(i);
}
});
// You can technically elide a single 0, this creates the regular expressions
// to match that eventuality
const possibilities = zeroIndexes.map((zeroIndex) =>
groups
.map((group, i) => {
if (i === zeroIndex) {
const elision = i === 0 || i === v6.GROUPS - 1 ? ':' : '';
return groupPossibilities([padGroup(group), elision]);
}
return padGroup(group);
})
.join(':'),
);
// The simplest case
possibilities.push(groups.map(padGroup).join(':'));
return groupPossibilities(possibilities);
}
export function possibleElisions(
elidedGroups: number,
moreLeft?: boolean,
moreRight?: boolean,
): string {
const left = moreLeft ? '' : ':';
const right = moreRight ? '' : ':';
const possibilities = [];
// 1. elision of everything (::)
if (!moreLeft && !moreRight) {
possibilities.push('::');
}
// 2. complete elision of the middle
if (moreLeft && moreRight) {
possibilities.push('');
}
if ((moreRight && !moreLeft) || (!moreRight && moreLeft)) {
// 3. complete elision of one side
possibilities.push(':');
}
// 4. elision from the left side
possibilities.push(`${left}(:0{1,4}){1,${elidedGroups - 1}}`);
// 5. elision from the right side
possibilities.push(`(0{1,4}:){1,${elidedGroups - 1}}${right}`);
// 6. no elision
possibilities.push(`(0{1,4}:){${elidedGroups - 1}}0{1,4}`);
// 7. elision (including sloppy elision) from the middle
for (let groups = 1; groups < elidedGroups - 1; groups++) {
for (let position = 1; position < elidedGroups - groups; position++) {
possibilities.push(
`(0{1,4}:){${position}}:(0{1,4}:){${elidedGroups - position - groups - 1}}0{1,4}`,
);
}
}
return groupPossibilities(possibilities);
}

112
node_modules/express-rate-limit/package.json generated vendored Normal file
View File

@@ -0,0 +1,112 @@
{
"name": "express-rate-limit",
"version": "8.2.1",
"description": "Basic IP rate-limiting middleware for Express. Use to limit repeated requests to public APIs and/or endpoints such as password reset.",
"author": {
"name": "Nathan Friedly",
"url": "http://nfriedly.com/"
},
"license": "MIT",
"homepage": "https://github.com/express-rate-limit/express-rate-limit",
"repository": {
"type": "git",
"url": "git+https://github.com/express-rate-limit/express-rate-limit.git"
},
"funding": "https://github.com/sponsors/express-rate-limit",
"keywords": [
"express-rate-limit",
"express",
"rate",
"limit",
"ratelimit",
"rate-limit",
"middleware",
"ip",
"auth",
"authorization",
"security",
"brute",
"force",
"bruteforce",
"brute-force",
"attack"
],
"type": "module",
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist/",
"tsconfig.json"
],
"engines": {
"node": ">= 16"
},
"scripts": {
"clean": "del-cli dist/ coverage/ *.log *.tmp *.bak *.tgz",
"build:cjs": "esbuild --packages=external --platform=node --bundle --target=es2022 --format=cjs --outfile=dist/index.cjs --footer:js=\"module.exports = Object.assign(rateLimit, module.exports);\" source/index.ts",
"build:esm": "esbuild --packages=external --platform=node --bundle --target=es2022 --format=esm --outfile=dist/index.mjs source/index.ts",
"build:types": "dts-bundle-generator --out-file=dist/index.d.ts source/index.ts && cp dist/index.d.ts dist/index.d.cts && cp dist/index.d.ts dist/index.d.mts",
"compile": "run-s clean build:*",
"docs": "cd docs && mintlify dev",
"lint:code": "biome check",
"lint:docs": "prettier --check docs/ *.md",
"lint": "run-s lint:*",
"format:code": "biome check --write",
"format:docs": "prettier --write docs/ *.md",
"format": "run-s format:*",
"test:lib": "jest",
"test:ext": "cd test/external/ && bash run-all-tests",
"test": "run-s lint test:lib",
"pre-commit": "lint-staged",
"prepare": "run-s compile && husky"
},
"peerDependencies": {
"express": ">= 4.11"
},
"devDependencies": {
"@biomejs/biome": "2.3.1",
"@express-rate-limit/prettier": "1.1.1",
"@express-rate-limit/tsconfig": "1.0.2",
"@jest/globals": "30.2.0",
"@types/express": "5.0.4",
"@types/jest": "30.0.0",
"@types/node": "24.9.1",
"@types/supertest": "6.0.3",
"del-cli": "6.0.0",
"dts-bundle-generator": "8.1.2",
"esbuild": "0.25.11",
"express": "5.1.0",
"husky": "9.1.7",
"jest": "30.2.0",
"lint-staged": "16.2.6",
"mintlify": "4.2.179",
"npm-run-all": "4.1.5",
"prettier": "3.6.2",
"ratelimit-header-parser": "0.1.0",
"supertest": "7.1.4",
"ts-jest": "29.4.5",
"ts-node": "10.9.2",
"typescript": "5.9.3"
},
"prettier": "@express-rate-limit/prettier",
"lint-staged": {
"*.{js,ts,json}": "biome check --write",
"*.{md,yaml}": "prettier --write"
},
"dependencies": {
"ip-address": "10.0.1"
}
}

151
node_modules/express-rate-limit/readme.md generated vendored Normal file
View File

@@ -0,0 +1,151 @@
<h1 align="center"> <code>express-rate-limit</code> </h1>
<div align="center">
[![tests](https://img.shields.io/github/actions/workflow/status/express-rate-limit/express-rate-limit/ci.yaml)](https://github.com/express-rate-limit/express-rate-limit/actions/workflows/ci.yaml)
[![npm version](https://img.shields.io/npm/v/express-rate-limit.svg)](https://npmjs.org/package/express-rate-limit 'View this project on NPM')
[![npm downloads](https://img.shields.io/npm/dm/express-rate-limit)](https://www.npmjs.com/package/express-rate-limit)
[![license](https://img.shields.io/npm/l/express-rate-limit)](license.md)
</div>
Basic rate-limiting middleware for [Express](http://expressjs.com/). Use to
limit repeated requests to public APIs and/or endpoints such as password reset.
Plays nice with
[express-slow-down](https://www.npmjs.com/package/express-slow-down) and
[ratelimit-header-parser](https://www.npmjs.com/package/ratelimit-header-parser).
## Usage
The [full documentation](https://express-rate-limit.mintlify.app/overview) is
available on-line.
```ts
import { rateLimit } from 'express-rate-limit'
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
limit: 100, // Limit each IP to 100 requests per `window` (here, per 15 minutes).
standardHeaders: 'draft-8', // draft-6: `RateLimit-*` headers; draft-7 & draft-8: combined `RateLimit` header
legacyHeaders: false, // Disable the `X-RateLimit-*` headers.
ipv6Subnet: 56, // Set to 60 or 64 to be less aggressive, or 52 or 48 to be more aggressive
// store: ... , // Redis, Memcached, etc. See below.
})
// Apply the rate limiting middleware to all requests.
app.use(limiter)
```
### Data Stores
The rate limiter comes with a built-in memory store, and supports a variety of
[external data stores](https://express-rate-limit.mintlify.app/reference/stores).
### Configuration
All function options may be async. Click the name for additional info and
default values.
| Option | Type | Remarks |
| -------------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------------------- |
| [`windowMs`] | `number` | How long to remember requests for, in milliseconds. |
| [`limit`] | `number` \| `function` | How many requests to allow. |
| [`message`] | `string` \| `json` \| `function` | Response to return after limit is reached. |
| [`statusCode`] | `number` | HTTP status code after limit is reached (default is 429). |
| [`handler`] | `function` | Function to run after limit is reached (overrides `message` and `statusCode` settings, if set). |
| [`legacyHeaders`] | `boolean` | Enable the `X-Rate-Limit` header. |
| [`standardHeaders`] | `'draft-6'` \| `'draft-7'` \| `'draft-8'` | Enable the `Ratelimit` header. |
| [`identifier`] | `string` \| `function` | Name associated with the quota policy enforced by this rate limiter. |
| [`store`] | `Store` | Use a custom store to share hit counts across multiple nodes. |
| [`passOnStoreError`] | `boolean` | Allow (`true`) or block (`false`, default) traffic if the store becomes unavailable. |
| [`keyGenerator`] | `function` | Identify users (defaults to IP address). |
| [`ipv6Subnet`] | `number` (32-64) \| `function` \| `false` | How many bits of IPv6 addresses to use in default `keyGenerator` |
| [`requestPropertyName`] | `string` | Add rate limit info to the `req` object. |
| [`skip`] | `function` | Return `true` to bypass the limiter for the given request. |
| [`skipSuccessfulRequests`] | `boolean` | Uncount 1xx/2xx/3xx responses. |
| [`skipFailedRequests`] | `boolean` | Uncount 4xx/5xx responses. |
| [`requestWasSuccessful`] | `function` | Used by `skipSuccessfulRequests` and `skipFailedRequests`. |
| [`validate`] | `boolean` \| `object` | Enable or disable built-in validation checks. |
## Thank You
Sponsored by [Zuplo](https://zuplo.link/express-rate-limit) a fully-managed API
Gateway for developers. Add
[dynamic rate-limiting](https://zuplo.link/dynamic-rate-limiting),
authentication and more to any API in minutes. Learn more at
[zuplo.com](https://zuplo.link/express-rate-limit)
<p align="center">
<a href="https://zuplo.link/express-rate-limit">
<picture width="322">
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/express-rate-limit/express-rate-limit/assets/114976/cd2f6fa7-eae1-4fbb-be7d-b17df4c6f383">
<img alt="zuplo-logo" src="https://github.com/express-rate-limit/express-rate-limit/assets/114976/66fd75fa-b39e-4a8c-8d7a-52369bf244dc" width="322">
</picture>
</a>
</p>
---
Thanks to Mintlify for hosting the documentation at
[express-rate-limit.mintlify.app](https://express-rate-limit.mintlify.app)
<p align="center">
<a href="https://mintlify.com/?utm_campaign=devmark&utm_medium=readme&utm_source=express-rate-limit">
<img height="75" src="https://devmark-public-assets.s3.us-west-2.amazonaws.com/sponsorships/mintlify.svg" alt="Create your docs today">
</a>
</p>
---
Finally, thank you to everyone who's contributed to this project in any way! 🫶
## Issues and Contributing
If you encounter a bug or want to see something added/changed, please go ahead
and
[open an issue](https://github.com/express-rate-limit/express-rate-limit/issues/new)!
If you need help with something, feel free to
[start a discussion](https://github.com/express-rate-limit/express-rate-limit/discussions/new)!
If you wish to contribute to the library, thanks! First, please read
[the contributing guide](https://express-rate-limit.mintlify.app/docs/guides/contributing.mdx).
Then you can pick up any issue and fix/implement it!
## License
MIT © [Nathan Friedly](http://nfriedly.com/),
[Vedant K](https://github.com/gamemaker1)
[`windowMs`]:
https://express-rate-limit.mintlify.app/reference/configuration#windowms
[`limit`]: https://express-rate-limit.mintlify.app/reference/configuration#limit
[`message`]:
https://express-rate-limit.mintlify.app/reference/configuration#message
[`statusCode`]:
https://express-rate-limit.mintlify.app/reference/configuration#statuscode
[`handler`]:
https://express-rate-limit.mintlify.app/reference/configuration#handler
[`legacyHeaders`]:
https://express-rate-limit.mintlify.app/reference/configuration#legacyheaders
[`standardHeaders`]:
https://express-rate-limit.mintlify.app/reference/configuration#standardheaders
[`identifier`]:
https://express-rate-limit.mintlify.app/reference/configuration#identifier
[`store`]: https://express-rate-limit.mintlify.app/reference/configuration#store
[`passOnStoreError`]:
https://express-rate-limit.mintlify.app/reference/configuration#passonstoreerror
[`keyGenerator`]:
https://express-rate-limit.mintlify.app/reference/configuration#keygenerator
[`ipv6Subnet`]:
https://express-rate-limit.mintlify.app/reference/configuration#ipv6subnet
[`requestPropertyName`]:
https://express-rate-limit.mintlify.app/reference/configuration#requestpropertyname
[`skip`]: https://express-rate-limit.mintlify.app/reference/configuration#skip
[`skipSuccessfulRequests`]:
https://express-rate-limit.mintlify.app/reference/configuration#skipsuccessfulrequests
[`skipFailedRequests`]:
https://express-rate-limit.mintlify.app/reference/configuration#skipfailedrequests
[`requestWasSuccessful`]:
https://express-rate-limit.mintlify.app/reference/configuration#requestwassuccessful
[`validate`]:
https://express-rate-limit.mintlify.app/reference/configuration#validate

8
node_modules/express-rate-limit/tsconfig.json generated vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"include": ["source/"],
"exclude": ["node_modules/"],
"extends": "@express-rate-limit/tsconfig",
"compilerOptions": {
"target": "ES2020"
}
}