@unkey/ratelimit
Version:
<div align="center"> <h1 align="center">@unkey/ratelimit</h1> <h5>@unkey/ratelimit is a library for fast global ratelimiting in serverless functions.</h5> </div>
201 lines (195 loc) • 5.5 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
NoopRatelimit: () => NoopRatelimit,
Overrides: () => Overrides,
Ratelimit: () => Ratelimit
});
module.exports = __toCommonJS(index_exports);
// src/noop.ts
var NoopRatelimit = class {
limit(_identifier, _opts) {
return Promise.resolve({
limit: 0,
remaining: 0,
reset: 0,
success: true
});
}
};
// src/ratelimit.ts
var import_api = require("@unkey/api");
var import_unkeyerror = require("@unkey/api/models/errors/unkeyerror");
// src/duration.ts
function ms(d) {
if (typeof d === "number") {
return d;
}
const match = d.match(/^(\d+)\s?(ms|s|m|h|d)$/);
if (!match) {
throw new Error(`Unable to parse window size: ${d}`);
}
const time = Number.parseInt(match[1]);
const unit = match[2];
switch (unit) {
case "ms":
return time;
case "s":
return time * 1e3;
case "m":
return time * 1e3 * 60;
case "h":
return time * 1e3 * 60 * 60;
case "d":
return time * 1e3 * 60 * 60 * 24;
default:
throw new Error(`Unable to parse window size: ${d}`);
}
}
// src/ratelimit.ts
var Ratelimit = class {
config;
unkey;
cache;
constructor(config) {
this.config = config;
this.unkey = new import_api.Unkey({
serverURL: config.baseUrl,
rootKey: config.rootKey
});
this.cache = config.cache ?? /* @__PURE__ */ new Map();
}
/**
* Limit a specific identifier, you can override a lot of things about this specific request using
* the 2nd argument.
*
* @example
* ```ts
* const identifier = getIpAddress() // or userId or anything else
* const res = await unkey.limit(identifier)
*
* if (!res.success){
* // reject request
* }
* // handle request
* ```
*/
async limit(identifier, opts) {
try {
return await this._limit(
identifier,
opts?.limit?.limit ?? this.config.limit,
ms(opts?.limit?.duration ?? this.config.duration),
opts?.cost ?? 1
);
} catch (e) {
if (typeof this.config.onError !== "function") {
throw e;
}
const err = e instanceof import_unkeyerror.UnkeyError ? new Error(e.message) : e instanceof Error ? e : new Error(String(e));
return await this.config.onError(err, identifier);
}
}
// _limit just handles the racing and caching. It must not handle errors, those are handled by limit
async _limit(identifier, limit, duration, cost) {
const cacheKey = `${this.config.namespace}:${identifier}:${limit}:${duration}`;
const naughty = this.cache.get(cacheKey);
if (naughty) {
if (naughty.reset > Date.now()) {
return naughty;
} else {
this.cache.delete(cacheKey);
}
}
const timeout = this.config.timeout === false ? null : this.config.timeout ?? {
ms: 5e3,
fallback: () => ({
success: false,
limit: 0,
remaining: 0,
reset: Date.now()
})
};
let timeoutId = null;
try {
const ps = [
this.unkey.ratelimit.limit({
namespace: this.config.namespace,
identifier,
limit,
duration,
cost
}).then(async (res2) => {
return res2.data;
})
];
if (timeout) {
ps.push(
new Promise((resolve) => {
timeoutId = setTimeout(async () => {
const resolvedValue = typeof timeout.fallback === "function" ? await timeout.fallback(identifier) : timeout.fallback;
resolve(resolvedValue);
}, ms(timeout.ms));
})
);
}
const res = await Promise.race(ps);
if (!res.success) {
this.cache.set(cacheKey, res);
}
return res;
} finally {
if (timeoutId) {
clearTimeout(timeoutId);
}
}
}
};
// src/overrides.ts
var import_api2 = require("@unkey/api");
var Overrides = class {
unkey;
constructor(config) {
this.unkey = new import_api2.Unkey({
serverURL: config.baseUrl,
rootKey: config.rootKey
});
}
getOverride(request, options) {
return this.unkey.ratelimit.getOverride(request, options);
}
setOverride(request, options) {
return this.unkey.ratelimit.setOverride(request, options);
}
deleteOverride(request, options) {
return this.unkey.ratelimit.deleteOverride(request, options);
}
listOverrides(request, options) {
return this.unkey.ratelimit.listOverrides(request, options);
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
NoopRatelimit,
Overrides,
Ratelimit
});
//# sourceMappingURL=index.js.map