New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@upstash/ratelimit

Package Overview
Dependencies
Maintainers
4
Versions
183
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@upstash/ratelimit - npm Package Compare versions

Comparing version 2.0.2 to 2.0.3

14

dist/index.d.ts

@@ -23,8 +23,2 @@ import { Aggregate } from '@upstash/core-analytics';

cache?: EphemeralCache;
scriptHashes: {
limitHash?: string;
getRemainingHash?: string;
resetHash?: string;
};
cacheScripts: boolean;
};

@@ -574,5 +568,9 @@ type MultiRegionContext = {

/**
* If enabled, lua scripts will be sent to Redis with SCRIPT LOAD durint the first request.
* In the subsequent requests, hash of the script will be used to invoke it
* @deprecated Has no affect since v2.0.3. Instead, hash values of scripts are
* hardcoded in the sdk and it attempts to run the script using EVALSHA (with the hash).
* If it fails, runs script load.
*
* Previously, if enabled, lua scripts were sent to Redis with SCRIPT LOAD durint the first request.
* In the subsequent requests, hash of the script would be used to invoke the scripts
*
* @default true

@@ -579,0 +577,0 @@ */

@@ -160,27 +160,14 @@ "use strict";

// src/hash.ts
var setHash = async (ctx, script, kind) => {
const regionContexts = "redis" in ctx ? [ctx] : ctx.regionContexts;
const hashSample = regionContexts[0].scriptHashes[kind];
if (!hashSample) {
await Promise.all(regionContexts.map(async (context) => {
context.scriptHashes[kind] = await context.redis.scriptLoad(script);
}));
}
;
};
var safeEval = async (ctx, script, kind, keys, args) => {
if (!ctx.cacheScripts) {
return await ctx.redis.eval(script, keys, args);
}
;
await setHash(ctx, script, kind);
var safeEval = async (ctx, script, keys, args) => {
try {
return await ctx.redis.evalsha(ctx.scriptHashes[kind], keys, args);
return await ctx.redis.evalsha(script.hash, keys, args);
} catch (error) {
if (`${error}`.includes("NOSCRIPT")) {
console.log("Script with the expected hash was not found in redis db. It is probably flushed. Will load another scipt before continuing.");
ctx.scriptHashes[kind] = void 0;
await setHash(ctx, script, kind);
console.log(" New script successfully loaded.");
return await ctx.redis.evalsha(ctx.scriptHashes[kind], keys, args);
const hash = await ctx.redis.scriptLoad(script.script);
if (hash !== script.hash) {
console.warn(
"Upstash Ratelimit: Expected hash and the hash received from Redis are different. Ratelimit will work as usual but performance will be reduced."
);
}
return await ctx.redis.evalsha(hash, keys, args);
}

@@ -191,4 +178,160 @@ throw error;

// src/lua-scripts/single.ts
var fixedWindowLimitScript = `
local key = KEYS[1]
local window = ARGV[1]
local incrementBy = ARGV[2] -- increment rate per request at a given value, default is 1
local r = redis.call("INCRBY", key, incrementBy)
if r == tonumber(incrementBy) then
-- The first time this key is set, the value will be equal to incrementBy.
-- So we only need the expire command once
redis.call("PEXPIRE", key, window)
end
return r
`;
var fixedWindowRemainingTokensScript = `
local key = KEYS[1]
local tokens = 0
local value = redis.call('GET', key)
if value then
tokens = value
end
return tokens
`;
var slidingWindowLimitScript = `
local currentKey = KEYS[1] -- identifier including prefixes
local previousKey = KEYS[2] -- key of the previous bucket
local tokens = tonumber(ARGV[1]) -- tokens per window
local now = ARGV[2] -- current timestamp in milliseconds
local window = ARGV[3] -- interval in milliseconds
local incrementBy = ARGV[4] -- increment rate per request at a given value, default is 1
local requestsInCurrentWindow = redis.call("GET", currentKey)
if requestsInCurrentWindow == false then
requestsInCurrentWindow = 0
end
local requestsInPreviousWindow = redis.call("GET", previousKey)
if requestsInPreviousWindow == false then
requestsInPreviousWindow = 0
end
local percentageInCurrent = ( now % window ) / window
-- weighted requests to consider from the previous window
requestsInPreviousWindow = math.floor(( 1 - percentageInCurrent ) * requestsInPreviousWindow)
if requestsInPreviousWindow + requestsInCurrentWindow >= tokens then
return -1
end
local newValue = redis.call("INCRBY", currentKey, incrementBy)
if newValue == tonumber(incrementBy) then
-- The first time this key is set, the value will be equal to incrementBy.
-- So we only need the expire command once
redis.call("PEXPIRE", currentKey, window * 2 + 1000) -- Enough time to overlap with a new window + 1 second
end
return tokens - ( newValue + requestsInPreviousWindow )
`;
var slidingWindowRemainingTokensScript = `
local currentKey = KEYS[1] -- identifier including prefixes
local previousKey = KEYS[2] -- key of the previous bucket
local now = ARGV[1] -- current timestamp in milliseconds
local window = ARGV[2] -- interval in milliseconds
local requestsInCurrentWindow = redis.call("GET", currentKey)
if requestsInCurrentWindow == false then
requestsInCurrentWindow = 0
end
local requestsInPreviousWindow = redis.call("GET", previousKey)
if requestsInPreviousWindow == false then
requestsInPreviousWindow = 0
end
local percentageInCurrent = ( now % window ) / window
-- weighted requests to consider from the previous window
requestsInPreviousWindow = math.floor(( 1 - percentageInCurrent ) * requestsInPreviousWindow)
return requestsInPreviousWindow + requestsInCurrentWindow
`;
var tokenBucketLimitScript = `
local key = KEYS[1] -- identifier including prefixes
local maxTokens = tonumber(ARGV[1]) -- maximum number of tokens
local interval = tonumber(ARGV[2]) -- size of the window in milliseconds
local refillRate = tonumber(ARGV[3]) -- how many tokens are refilled after each interval
local now = tonumber(ARGV[4]) -- current timestamp in milliseconds
local incrementBy = tonumber(ARGV[5]) -- how many tokens to consume, default is 1
local bucket = redis.call("HMGET", key, "refilledAt", "tokens")
local refilledAt
local tokens
if bucket[1] == false then
refilledAt = now
tokens = maxTokens
else
refilledAt = tonumber(bucket[1])
tokens = tonumber(bucket[2])
end
if now >= refilledAt + interval then
local numRefills = math.floor((now - refilledAt) / interval)
tokens = math.min(maxTokens, tokens + numRefills * refillRate)
refilledAt = refilledAt + numRefills * interval
end
if tokens == 0 then
return {-1, refilledAt + interval}
end
local remaining = tokens - incrementBy
local expireAt = math.ceil(((maxTokens - remaining) / refillRate)) * interval
redis.call("HSET", key, "refilledAt", refilledAt, "tokens", remaining)
redis.call("PEXPIRE", key, expireAt)
return {remaining, refilledAt + interval}
`;
var tokenBucketIdentifierNotFound = -1;
var tokenBucketRemainingTokensScript = `
local key = KEYS[1]
local maxTokens = tonumber(ARGV[1])
local bucket = redis.call("HMGET", key, "refilledAt", "tokens")
if bucket[1] == false then
return {maxTokens, ${tokenBucketIdentifierNotFound}}
end
return {tonumber(bucket[2]), tonumber(bucket[1])}
`;
var cachedFixedWindowLimitScript = `
local key = KEYS[1]
local window = ARGV[1]
local incrementBy = ARGV[2] -- increment rate per request at a given value, default is 1
local r = redis.call("INCRBY", key, incrementBy)
if r == incrementBy then
-- The first time this key is set, the value will be equal to incrementBy.
-- So we only need the expire command once
redis.call("PEXPIRE", key, window)
end
return r
`;
var cachedFixedWindowRemainingTokenScript = `
local key = KEYS[1]
local tokens = 0
local value = redis.call('GET', key)
if value then
tokens = value
end
return tokens
`;
// src/lua-scripts/multi.ts
var fixedWindowLimitScript = `
var fixedWindowLimitScript2 = `
local key = KEYS[1]

@@ -209,3 +352,3 @@ local id = ARGV[1]

`;
var fixedWindowRemainingTokensScript = `
var fixedWindowRemainingTokensScript2 = `
local key = KEYS[1]

@@ -218,3 +361,3 @@ local tokens = 0

`;
var slidingWindowLimitScript = `
var slidingWindowLimitScript2 = `
local currentKey = KEYS[1] -- identifier including prefixes

@@ -254,3 +397,3 @@ local previousKey = KEYS[2] -- key of the previous bucket

`;
var slidingWindowRemainingTokensScript = `
var slidingWindowRemainingTokensScript2 = `
local currentKey = KEYS[1] -- identifier including prefixes

@@ -304,2 +447,74 @@ local previousKey = KEYS[2] -- key of the previous bucket

// src/lua-scripts/hash.ts
var SCRIPTS = {
singleRegion: {
fixedWindow: {
limit: {
script: fixedWindowLimitScript,
hash: "b13943e359636db027ad280f1def143f02158c13"
},
getRemaining: {
script: fixedWindowRemainingTokensScript,
hash: "8c4c341934502aee132643ffbe58ead3450e5208"
}
},
slidingWindow: {
limit: {
script: slidingWindowLimitScript,
hash: "e1391e429b699c780eb0480350cd5b7280fd9213"
},
getRemaining: {
script: slidingWindowRemainingTokensScript,
hash: "65a73ac5a05bf9712903bc304b77268980c1c417"
}
},
tokenBucket: {
limit: {
script: tokenBucketLimitScript,
hash: "5bece90aeef8189a8cfd28995b479529e270b3c6"
},
getRemaining: {
script: tokenBucketRemainingTokensScript,
hash: "a15be2bb1db2a15f7c82db06146f9d08983900d0"
}
},
cachedFixedWindow: {
limit: {
script: cachedFixedWindowLimitScript,
hash: "c26b12703dd137939b9a69a3a9b18e906a2d940f"
},
getRemaining: {
script: cachedFixedWindowRemainingTokenScript,
hash: "8e8f222ccae68b595ee6e3f3bf2199629a62b91a"
}
}
},
multiRegion: {
fixedWindow: {
limit: {
script: fixedWindowLimitScript2,
hash: "a8c14f3835aa87bd70e5e2116081b81664abcf5c"
},
getRemaining: {
script: fixedWindowRemainingTokensScript2,
hash: "8ab8322d0ed5fe5ac8eb08f0c2e4557f1b4816fd"
}
},
slidingWindow: {
limit: {
script: slidingWindowLimitScript2,
hash: "cb4fdc2575056df7c6d422764df0de3a08d6753b"
},
getRemaining: {
script: slidingWindowRemainingTokensScript2,
hash: "558c9306b7ec54abb50747fe0b17e5d44bd24868"
}
}
}
};
var RESET_SCRIPT = {
script: resetScript,
hash: "54bd274ddc59fb3be0f42deee2f64322a10e2b50"
};
// src/types.ts

@@ -744,5 +959,3 @@ var DenyListExtension = "denyList";

regionContexts: config.redis.map((redis) => ({
redis,
scriptHashes: {},
cacheScripts: config.cacheScripts ?? true
redis
})),

@@ -796,4 +1009,3 @@ cache: config.ephemeralCache ? new Cache(config.ephemeralCache) : void 0

regionContext,
fixedWindowLimitScript,
"limitHash",
SCRIPTS.multiRegion.fixedWindow.limit,
[key],

@@ -873,4 +1085,3 @@ [requestId, windowDuration, incrementBy]

regionContext,
fixedWindowRemainingTokensScript,
"getRemainingHash",
SCRIPTS.multiRegion.fixedWindow.getRemaining,
[key],

@@ -901,4 +1112,3 @@ [null]

regionContext,
resetScript,
"resetHash",
RESET_SCRIPT,
[pattern],

@@ -956,4 +1166,3 @@ [null]

regionContext,
slidingWindowLimitScript,
"limitHash",
SCRIPTS.multiRegion.slidingWindow.limit,
[currentKey, previousKey],

@@ -1047,4 +1256,3 @@ [tokens, now, windowDuration, requestId, incrementBy]

regionContext,
slidingWindowRemainingTokensScript,
"getRemainingHash",
SCRIPTS.multiRegion.slidingWindow.getRemaining,
[currentKey, previousKey],

@@ -1069,4 +1277,3 @@ [now, windowSize]

regionContext,
resetScript,
"resetHash",
RESET_SCRIPT,
[pattern],

@@ -1081,158 +1288,2 @@ [null]

// src/lua-scripts/single.ts
var fixedWindowLimitScript2 = `
local key = KEYS[1]
local window = ARGV[1]
local incrementBy = ARGV[2] -- increment rate per request at a given value, default is 1
local r = redis.call("INCRBY", key, incrementBy)
if r == tonumber(incrementBy) then
-- The first time this key is set, the value will be equal to incrementBy.
-- So we only need the expire command once
redis.call("PEXPIRE", key, window)
end
return r
`;
var fixedWindowRemainingTokensScript2 = `
local key = KEYS[1]
local tokens = 0
local value = redis.call('GET', key)
if value then
tokens = value
end
return tokens
`;
var slidingWindowLimitScript2 = `
local currentKey = KEYS[1] -- identifier including prefixes
local previousKey = KEYS[2] -- key of the previous bucket
local tokens = tonumber(ARGV[1]) -- tokens per window
local now = ARGV[2] -- current timestamp in milliseconds
local window = ARGV[3] -- interval in milliseconds
local incrementBy = ARGV[4] -- increment rate per request at a given value, default is 1
local requestsInCurrentWindow = redis.call("GET", currentKey)
if requestsInCurrentWindow == false then
requestsInCurrentWindow = 0
end
local requestsInPreviousWindow = redis.call("GET", previousKey)
if requestsInPreviousWindow == false then
requestsInPreviousWindow = 0
end
local percentageInCurrent = ( now % window ) / window
-- weighted requests to consider from the previous window
requestsInPreviousWindow = math.floor(( 1 - percentageInCurrent ) * requestsInPreviousWindow)
if requestsInPreviousWindow + requestsInCurrentWindow >= tokens then
return -1
end
local newValue = redis.call("INCRBY", currentKey, incrementBy)
if newValue == tonumber(incrementBy) then
-- The first time this key is set, the value will be equal to incrementBy.
-- So we only need the expire command once
redis.call("PEXPIRE", currentKey, window * 2 + 1000) -- Enough time to overlap with a new window + 1 second
end
return tokens - ( newValue + requestsInPreviousWindow )
`;
var slidingWindowRemainingTokensScript2 = `
local currentKey = KEYS[1] -- identifier including prefixes
local previousKey = KEYS[2] -- key of the previous bucket
local now = ARGV[1] -- current timestamp in milliseconds
local window = ARGV[2] -- interval in milliseconds
local requestsInCurrentWindow = redis.call("GET", currentKey)
if requestsInCurrentWindow == false then
requestsInCurrentWindow = 0
end
local requestsInPreviousWindow = redis.call("GET", previousKey)
if requestsInPreviousWindow == false then
requestsInPreviousWindow = 0
end
local percentageInCurrent = ( now % window ) / window
-- weighted requests to consider from the previous window
requestsInPreviousWindow = math.floor(( 1 - percentageInCurrent ) * requestsInPreviousWindow)
return requestsInPreviousWindow + requestsInCurrentWindow
`;
var tokenBucketLimitScript = `
local key = KEYS[1] -- identifier including prefixes
local maxTokens = tonumber(ARGV[1]) -- maximum number of tokens
local interval = tonumber(ARGV[2]) -- size of the window in milliseconds
local refillRate = tonumber(ARGV[3]) -- how many tokens are refilled after each interval
local now = tonumber(ARGV[4]) -- current timestamp in milliseconds
local incrementBy = tonumber(ARGV[5]) -- how many tokens to consume, default is 1
local bucket = redis.call("HMGET", key, "refilledAt", "tokens")
local refilledAt
local tokens
if bucket[1] == false then
refilledAt = now
tokens = maxTokens
else
refilledAt = tonumber(bucket[1])
tokens = tonumber(bucket[2])
end
if now >= refilledAt + interval then
local numRefills = math.floor((now - refilledAt) / interval)
tokens = math.min(maxTokens, tokens + numRefills * refillRate)
refilledAt = refilledAt + numRefills * interval
end
if tokens == 0 then
return {-1, refilledAt + interval}
end
local remaining = tokens - incrementBy
local expireAt = math.ceil(((maxTokens - remaining) / refillRate)) * interval
redis.call("HSET", key, "refilledAt", refilledAt, "tokens", remaining)
redis.call("PEXPIRE", key, expireAt)
return {remaining, refilledAt + interval}
`;
var tokenBucketIdentifierNotFound = -1;
var tokenBucketRemainingTokensScript = `
local key = KEYS[1]
local maxTokens = tonumber(ARGV[1])
local bucket = redis.call("HMGET", key, "refilledAt", "tokens")
if bucket[1] == false then
return {maxTokens, ${tokenBucketIdentifierNotFound}}
end
return {tonumber(bucket[2]), tonumber(bucket[1])}
`;
var cachedFixedWindowLimitScript = `
local key = KEYS[1]
local window = ARGV[1]
local incrementBy = ARGV[2] -- increment rate per request at a given value, default is 1
local r = redis.call("INCRBY", key, incrementBy)
if r == incrementBy then
-- The first time this key is set, the value will be equal to incrementBy.
-- So we only need the expire command once
redis.call("PEXPIRE", key, window)
end
return r
`;
var cachedFixedWindowRemainingTokenScript = `
local key = KEYS[1]
local tokens = 0
local value = redis.call('GET', key)
if value then
tokens = value
end
return tokens
`;
// src/single.ts

@@ -1250,5 +1301,3 @@ var RegionRatelimit = class extends Ratelimit {

ctx: {
redis: config.redis,
scriptHashes: {},
cacheScripts: config.cacheScripts ?? true
redis: config.redis
},

@@ -1300,4 +1349,3 @@ ephemeralCache: config.ephemeralCache,

ctx,
fixedWindowLimitScript2,
"limitHash",
SCRIPTS.singleRegion.fixedWindow.limit,
[key],

@@ -1325,4 +1373,3 @@ [windowDuration, incrementBy]

ctx,
fixedWindowRemainingTokensScript2,
"getRemainingHash",
SCRIPTS.singleRegion.fixedWindow.getRemaining,
[key],

@@ -1343,4 +1390,3 @@ [null]

ctx,
resetScript,
"resetHash",
RESET_SCRIPT,
[pattern],

@@ -1393,4 +1439,3 @@ [null]

ctx,
slidingWindowLimitScript2,
"limitHash",
SCRIPTS.singleRegion.slidingWindow.limit,
[currentKey, previousKey],

@@ -1420,4 +1465,3 @@ [tokens, now, windowSize, incrementBy]

ctx,
slidingWindowRemainingTokensScript2,
"getRemainingHash",
SCRIPTS.singleRegion.slidingWindow.getRemaining,
[currentKey, previousKey],

@@ -1438,4 +1482,3 @@ [now, windowSize]

ctx,
resetScript,
"resetHash",
RESET_SCRIPT,
[pattern],

@@ -1481,4 +1524,3 @@ [null]

ctx,
tokenBucketLimitScript,
"limitHash",
SCRIPTS.singleRegion.tokenBucket.limit,
[identifier],

@@ -1502,4 +1544,3 @@ [maxTokens, intervalDuration, refillRate, now, incrementBy]

ctx,
tokenBucketRemainingTokensScript,
"getRemainingHash",
SCRIPTS.singleRegion.tokenBucket.getRemaining,
[identifier],

@@ -1522,4 +1563,3 @@ [maxTokens]

ctx,
resetScript,
"resetHash",
RESET_SCRIPT,
[pattern],

@@ -1572,4 +1612,3 @@ [null]

ctx,
cachedFixedWindowLimitScript,
"limitHash",
SCRIPTS.singleRegion.cachedFixedWindow.limit,
[key],

@@ -1588,4 +1627,3 @@ [windowDuration, incrementBy]

ctx,
cachedFixedWindowLimitScript,
"limitHash",
SCRIPTS.singleRegion.cachedFixedWindow.limit,
[key],

@@ -1620,4 +1658,3 @@ [windowDuration, incrementBy]

ctx,
cachedFixedWindowRemainingTokenScript,
"getRemainingHash",
SCRIPTS.singleRegion.cachedFixedWindow.getRemaining,
[key],

@@ -1641,4 +1678,3 @@ [null]

ctx,
resetScript,
"resetHash",
RESET_SCRIPT,
[pattern],

@@ -1645,0 +1681,0 @@ [null]

@@ -1,1 +0,1 @@

{ "name": "@upstash/ratelimit", "version": "v2.0.2", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ "dist" ], "scripts": { "build": "tsup", "test": "bun test src --coverage", "fmt": "bunx @biomejs/biome check --apply ./src" }, "devDependencies": { "@upstash/redis": "^1.31.5", "bun-types": "latest", "rome": "^11.0.0", "tsup": "^7.2.0", "turbo": "^1.10.15", "typescript": "^5.0.0" }, "license": "MIT", "dependencies": { "@upstash/core-analytics": "^0.0.10" } }
{ "name": "@upstash/ratelimit", "version": "v2.0.3", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ "dist" ], "scripts": { "build": "tsup", "test": "bun test src --coverage", "fmt": "bunx @biomejs/biome check --apply ./src" }, "devDependencies": { "@upstash/redis": "^1.31.5", "bun-types": "latest", "rome": "^11.0.0", "tsup": "^7.2.0", "turbo": "^1.10.15", "typescript": "^5.0.0" }, "license": "MIT", "dependencies": { "@upstash/core-analytics": "^0.0.10" } }

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc