@lunora/ratelimit 1.0.0-alpha.90 → 1.0.0-alpha.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/dist/index.d.mts +6 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/RateLimiter-Cbc67DNk.mjs +1 -0
- package/dist/packem_shared/{dbRateLimit-rlAL4c2M.mjs → dbRateLimit-BuvEF1x0.mjs} +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/RateLimiter-DKXPLL9z.mjs +0 -1
package/README.md
CHANGED
|
@@ -117,6 +117,11 @@ correct inside a single Durable Object — but constructing a `RateLimiter` with
|
|
|
117
117
|
no explicit `store` now warns once so that choice is visible instead of
|
|
118
118
|
silent.
|
|
119
119
|
|
|
120
|
+
A key that is an IPv6 address (for example `ctx.ip`) is bucketed by its /64
|
|
121
|
+
prefix, and an IPv4-mapped address by the IPv4 address it wraps. An IPv6 client
|
|
122
|
+
usually controls a whole /64, so a per-address bucket would let it pick a fresh
|
|
123
|
+
address for every request. A deny-list IPv6 entry bans its /64.
|
|
124
|
+
|
|
120
125
|
> This README covers the basics. For the full API, options, and guides, see the **[documentation](https://lunora.sh/docs)**.
|
|
121
126
|
|
|
122
127
|
## Related
|
package/dist/index.d.mts
CHANGED
|
@@ -124,9 +124,14 @@ interface RateLimiterOptions<Names extends string> {
|
|
|
124
124
|
/**
|
|
125
125
|
* Optional key normalizer applied to every incoming `args.key` (the
|
|
126
126
|
* deny-list check and the storage key both see the normalized form). Use for case-folding, trimming, or canonicalizing
|
|
127
|
-
*
|
|
127
|
+
* emails so equivalent inputs share a single bucket. The deny-list
|
|
128
128
|
* itself is consulted as-is; normalize the deny-list entries up front to
|
|
129
129
|
* match.
|
|
130
|
+
*
|
|
131
|
+
* After it, a key that is an IPv6 address is always collapsed to its /64
|
|
132
|
+
* (`2001:db8:1:2::/64`) and an IPv4-mapped one to its IPv4 address, so a
|
|
133
|
+
* `ctx.ip`-keyed limit cannot be dodged by rotating addresses within one
|
|
134
|
+
* delegated prefix. A deny-list IPv6 entry therefore bans its whole /64.
|
|
130
135
|
*/
|
|
131
136
|
normalize?: (key: string) => string;
|
|
132
137
|
/** Clock injection for tests. Defaults to `Date.now`. */
|
package/dist/index.d.ts
CHANGED
|
@@ -124,9 +124,14 @@ interface RateLimiterOptions<Names extends string> {
|
|
|
124
124
|
/**
|
|
125
125
|
* Optional key normalizer applied to every incoming `args.key` (the
|
|
126
126
|
* deny-list check and the storage key both see the normalized form). Use for case-folding, trimming, or canonicalizing
|
|
127
|
-
*
|
|
127
|
+
* emails so equivalent inputs share a single bucket. The deny-list
|
|
128
128
|
* itself is consulted as-is; normalize the deny-list entries up front to
|
|
129
129
|
* match.
|
|
130
|
+
*
|
|
131
|
+
* After it, a key that is an IPv6 address is always collapsed to its /64
|
|
132
|
+
* (`2001:db8:1:2::/64`) and an IPv4-mapped one to its IPv4 address, so a
|
|
133
|
+
* `ctx.ip`-keyed limit cannot be dodged by rotating addresses within one
|
|
134
|
+
* delegated prefix. A deny-list IPv6 entry therefore bans its whole /64.
|
|
130
135
|
*/
|
|
131
136
|
normalize?: (key: string) => string;
|
|
132
137
|
/** Clock injection for tests. Defaults to `Date.now`. */
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{availableAt as o,evaluate as a}from"./packem_shared/availableAt-BSKYrouo.mjs";import{default as i}from"./packem_shared/dbRateLimit-
|
|
1
|
+
import{availableAt as o,evaluate as a}from"./packem_shared/availableAt-BSKYrouo.mjs";import{default as i}from"./packem_shared/dbRateLimit-BuvEF1x0.mjs";import{default as l}from"./packem_shared/RateLimitError-9Y5OLxpl.mjs";import{rateLimit as x}from"./packem_shared/rateLimit-CWZur6XE.mjs";import{ratelimitPlugin as c}from"./packem_shared/ratelimitPlugin-CFnDs9rj.mjs";import{RateLimiter as u}from"./packem_shared/RateLimiter-Cbc67DNk.mjs";import{createDbStore as b,createMemoryStore as n,createReadOnlyDbStore as L,createSqlStore as s}from"./packem_shared/createDbStore-ve7GsO2i.mjs";import{tokenBudget as v}from"./packem_shared/tokenBudget-BRlWn1im.mjs";const e="0.0.0";export{l as RateLimitError,u as RateLimiter,e as VERSION,o as availableAt,b as createDbStore,n as createMemoryStore,L as createReadOnlyDbStore,s as createSqlStore,i as dbRateLimit,a as evaluate,x as rateLimit,c as ratelimitPlugin,v as tokenBudget};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as l}from"@lunora/errors";import{availableAt as p,evaluate as y}from"./availableAt-BSKYrouo.mjs";import f from"./RateLimitError-9Y5OLxpl.mjs";import{createMemoryStore as v}from"./createDbStore-ve7GsO2i.mjs";const b=/^[\d.:a-f]+$/iu,w=i=>{const t=i.indexOf("%"),e=t===-1?i:i.slice(0,t);if(!e.includes(":")||!b.test(e))return i;let r;try{r=new URL(`http://[${e}]/`).hostname.slice(1,-1)}catch{return i}const[a="",s]=r.split("::"),n=a===""?[]:a.split(":"),h=s===void 0||s===""?[]:s.split(":"),c=[...n,...Array.from({length:8-n.length-h.length},()=>"0"),...h].map(o=>Number.parseInt(o,16));if(c.slice(0,5).every(o=>o===0)&&c[5]===65535){const[o=0,m=0]=c.slice(6);return[o>>8,o&255,m>>8,m&255].join(".")}return`${c.slice(0,4).map(o=>o.toString(16)).join(":")}::/64`},u=(i,t)=>t===void 0?encodeURIComponent(i):`${encodeURIComponent(i)}:${encodeURIComponent(t)}`,g=i=>i?t=>w(i(t)):w,N="@lunora/ratelimit: new RateLimiter() was built with no explicit `store`, so it falls back to `createMemoryStore()` — an in-process Map. That Map is durable and correctly shared only for calls that land on the same Durable Object instance; it is NOT shared across `.shardBy(...)` shards or `.global()` replicas, and it resets when the DO instance is evicted/restarted. For a limit that must hold across any of those, pass a durable `store` (`createDbStore` or `createSqlStore`) instead. Pass `store: createMemoryStore()` explicitly once you've confirmed the in-memory default is correct here, to silence this warning.";class S{config;denyList;normalize;now;store;constructor(t){this.config=t.config,this.normalize=g(t.normalize),this.denyList=new Set([...t.denyList??[]].flatMap(e=>[e,this.normalize(e)])),this.now=t.now??Date.now,t.store===void 0&&console.warn(N),this.store=t.store??v();for(const[e,r]of Object.entries(this.config)){if(Object.hasOwn(r,"shards"))throw new l("INTERNAL",`rate limit "${e}": \`shards\` is no longer supported — remove it; the limit now enforces its full \`rate\` (lower \`rate\` if you raised it to offset the split)`);if(!Number.isFinite(r.period)||r.period<=0)throw new l("INTERNAL",`rate limit "${e}": period must be a positive number`);if(!Number.isFinite(r.rate)||r.rate<=0)throw new l("INTERNAL",`rate limit "${e}": rate must be a positive number`);if(r.capacity!==void 0&&(!Number.isFinite(r.capacity)||r.capacity<0))throw new l("INTERNAL",`rate limit "${e}": capacity must be a non-negative number`)}}async check(t,e={}){return this.run(t,e,!1)}async getValue(t,e={}){const r=this.resolve(t),a=this.now(),s=u(t,this.normalizeKey(e.key)),n=p(r,await this.store.get(s),a);return{config:r,ts:n.ts,value:n.value}}async limit(t,e={}){return this.run(t,e,!0)}async reset(t,e={}){this.resolve(t),await Promise.resolve(this.store.delete(u(t,this.normalizeKey(e.key))))}normalizeKey(t){return t===void 0?void 0:this.normalize(t)}resolve(t){const e=this.config[t];if(!e)throw new l("INTERNAL",`rate limit "${t}" is not configured`);return e}async run(t,e,r){const a=this.resolve(t),s=this.normalizeKey(e.key);if(s!==void 0&&(this.denyList.has(s)||this.denyList.has(e.key))){const d={ok:!1,reason:"deny",retryAfter:Number.POSITIVE_INFINITY};if(e.throws)throw new f(d);return d}const n=e.count??1;if(!Number.isInteger(n)||n<=0)throw new l("INTERNAL",`rate limit "${t}": count must be a positive integer`);const h=u(t,s),c=await this.store.get(h),{status:o,value:m}=y(a,c,{consume:r,count:n,now:this.now(),reserve:e.reserve??!1});if(m!==void 0&&await this.store.set(h,m),!o.ok&&e.throws)throw new f(o);return o}}export{S as RateLimiter};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{rateLimit as o}from"./rateLimit-CWZur6XE.mjs";import{RateLimiter as m}from"./RateLimiter-
|
|
1
|
+
import{rateLimit as o}from"./rateLimit-CWZur6XE.mjs";import{RateLimiter as m}from"./RateLimiter-Cbc67DNk.mjs";import{createDbStore as i}from"./createDbStore-ve7GsO2i.mjs";const f=(e,r,t={})=>o(a=>new m({config:e,store:i({db:a.db,...t.store})}),r,t);export{f as default};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/ratelimit",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.92",
|
|
4
4
|
"description": "Rate limiting: token-bucket / fixed-window / sliding-window algorithms, deny list, pluggable stores, and procedure middleware",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@lunora/errors": "1.0.0-alpha.
|
|
49
|
+
"@lunora/errors": "1.0.0-alpha.47"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"@lunora/server": ">=1.0.0-alpha.24 <2.0.0-0",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as o}from"@lunora/errors";import{availableAt as w,evaluate as y}from"./availableAt-BSKYrouo.mjs";import f from"./RateLimitError-9Y5OLxpl.mjs";import{createMemoryStore as v}from"./createDbStore-ve7GsO2i.mjs";const l=(n,e)=>e===void 0?encodeURIComponent(n):`${encodeURIComponent(n)}:${encodeURIComponent(e)}`,p="@lunora/ratelimit: new RateLimiter() was built with no explicit `store`, so it falls back to `createMemoryStore()` — an in-process Map. That Map is durable and correctly shared only for calls that land on the same Durable Object instance; it is NOT shared across `.shardBy(...)` shards or `.global()` replicas, and it resets when the DO instance is evicted/restarted. For a limit that must hold across any of those, pass a durable `store` (`createDbStore` or `createSqlStore`) instead. Pass `store: createMemoryStore()` explicitly once you've confirmed the in-memory default is correct here, to silence this warning.";class L{config;denyList;normalize;now;store;constructor(e){this.config=e.config,this.normalize=e.normalize??(t=>t),this.denyList=new Set([...e.denyList??[]].flatMap(t=>[t,this.normalize(t)])),this.now=e.now??Date.now,e.store===void 0&&console.warn(p),this.store=e.store??v();for(const[t,r]of Object.entries(this.config)){if(Object.hasOwn(r,"shards"))throw new o("INTERNAL",`rate limit "${t}": \`shards\` is no longer supported — remove it; the limit now enforces its full \`rate\` (lower \`rate\` if you raised it to offset the split)`);if(!Number.isFinite(r.period)||r.period<=0)throw new o("INTERNAL",`rate limit "${t}": period must be a positive number`);if(!Number.isFinite(r.rate)||r.rate<=0)throw new o("INTERNAL",`rate limit "${t}": rate must be a positive number`);if(r.capacity!==void 0&&(!Number.isFinite(r.capacity)||r.capacity<0))throw new o("INTERNAL",`rate limit "${t}": capacity must be a non-negative number`)}}async check(e,t={}){return this.run(e,t,!1)}async getValue(e,t={}){const r=this.resolve(e),a=this.now(),s=l(e,this.normalizeKey(t.key)),i=w(r,await this.store.get(s),a);return{config:r,ts:i.ts,value:i.value}}async limit(e,t={}){return this.run(e,t,!0)}async reset(e,t={}){this.resolve(e),await Promise.resolve(this.store.delete(l(e,this.normalizeKey(t.key))))}normalizeKey(e){return e===void 0?void 0:this.normalize(e)}resolve(e){const t=this.config[e];if(!t)throw new o("INTERNAL",`rate limit "${e}" is not configured`);return t}async run(e,t,r){const a=this.resolve(e),s=this.normalizeKey(t.key);if(s!==void 0&&(this.denyList.has(s)||this.denyList.has(t.key))){const u={ok:!1,reason:"deny",retryAfter:Number.POSITIVE_INFINITY};if(t.throws)throw new f(u);return u}const i=t.count??1;if(!Number.isInteger(i)||i<=0)throw new o("INTERNAL",`rate limit "${e}": count must be a positive integer`);const h=l(e,s),d=await this.store.get(h),{status:c,value:m}=y(a,d,{consume:r,count:i,now:this.now(),reserve:t.reserve??!1});if(m!==void 0&&await this.store.set(h,m),!c.ok&&t.throws)throw new f(c);return c}}export{L as RateLimiter};
|