@lunora/ratelimit 1.0.0-alpha.17 → 1.0.0-alpha.18
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/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.mjs +1 -1
- package/dist/packem_shared/RateLimiter-CitqoGTI.mjs +1 -0
- package/dist/packem_shared/availableAt-BMlAt2U2.mjs +1 -0
- package/dist/packem_shared/{dbRateLimit-CrjZatIs.mjs → dbRateLimit-e95-suu-.mjs} +1 -1
- package/package.json +2 -2
- package/dist/packem_shared/RateLimiter-uPXzD0St.mjs +0 -1
- package/dist/packem_shared/availableAt-DITZ7jdT.mjs +0 -1
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{availableAt as o,evaluate as a}from"./packem_shared/availableAt-
|
|
1
|
+
import{availableAt as o,evaluate as a}from"./packem_shared/availableAt-BMlAt2U2.mjs";import{default as i}from"./packem_shared/dbRateLimit-e95-suu-.mjs";import{default as l}from"./packem_shared/RateLimitError-xJ2Iv-i7.mjs";import{rateLimit as x}from"./packem_shared/rateLimit-DLcwK-84.mjs";import{ratelimitPlugin as c}from"./packem_shared/ratelimitPlugin-D1QHZF9b.mjs";import{RateLimiter as b}from"./packem_shared/RateLimiter-CitqoGTI.mjs";import{createDbStore as u,createMemoryStore as L,createReadOnlyDbStore as n,createSqlStore as s}from"./packem_shared/createDbStore-CwJBMxDQ.mjs";const e="0.0.0";export{l as RateLimitError,b as RateLimiter,e as VERSION,o as availableAt,u as createDbStore,L as createMemoryStore,n as createReadOnlyDbStore,s as createSqlStore,i as dbRateLimit,a as evaluate,x as rateLimit,c as ratelimitPlugin};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as n}from"@lunora/errors";import{availableAt as p,evaluate as N}from"./availableAt-BMlAt2U2.mjs";import w from"./RateLimitError-xJ2Iv-i7.mjs";import{createMemoryStore as g}from"./createDbStore-CwJBMxDQ.mjs";const y=(r,t)=>t===void 0?encodeURIComponent(r):`${encodeURIComponent(r)}:${encodeURIComponent(t)}`,b=(r,t)=>{let e=0;for(let i=0;i<r.length;i+=1)e=e*31+r.charCodeAt(i)|0;return Math.abs(e)%t},d=(r,t)=>t>1?{...r,capacity:(r.capacity??r.rate)/t,rate:r.rate/t}:r,I=(r,t,e)=>{const i=y(r,t);return e>1?Array.from({length:e},(o,s)=>`${i}#${String(s)}`):[i]},f=(r,t,e)=>{const i=y(r,t);return e>1?`${i}#${String(b(i,e))}`:i};class z{config;denyList;normalize;now;store;constructor(t){this.config=t.config,this.denyList=new Set(t.denyList),this.normalize=t.normalize??(e=>e),this.now=t.now??Date.now,this.store=t.store??g();for(const[e,i]of Object.entries(this.config)){if(i.shards!==void 0&&(!Number.isInteger(i.shards)||i.shards<1))throw new n("INTERNAL",`rate limit "${e}": shards must be a positive integer`);if(!Number.isFinite(i.period)||i.period<=0)throw new n("INTERNAL",`rate limit "${e}": period must be a positive number`);if(!Number.isFinite(i.rate)||i.rate<=0)throw new n("INTERNAL",`rate limit "${e}": rate must be a positive number`);if(i.capacity!==void 0&&(!Number.isFinite(i.capacity)||i.capacity<0))throw new n("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 i=this.resolve(t),o=i.shards??1,s=this.now(),a=this.normalizeKey(e.key),m=f(t,a,o),c=p(d(i,o),await this.store.get(m),s);return{config:i,ts:c.ts,value:c.value}}async limit(t,e={}){return this.run(t,e,!0)}async reset(t,e={}){const i=this.resolve(t).shards??1,o=this.normalizeKey(e.key);await Promise.all(I(t,o,i).map(s=>Promise.resolve(this.store.delete(s))))}normalizeKey(t){return t===void 0?void 0:this.normalize(t)}resolve(t){const e=this.config[t];if(!e)throw new n("INTERNAL",`rate limit "${t}" is not configured`);return e}async run(t,e,i){const o=this.resolve(t),s=this.normalizeKey(e.key);if(s!==void 0&&(this.denyList.has(s)||this.denyList.has(e.key))){const l={ok:!1,reason:"deny",retryAfter:Number.POSITIVE_INFINITY};if(e.throws)throw new w(l);return l}const a=e.count??1;if(!Number.isInteger(a)||a<=0)throw new n("INTERNAL",`rate limit "${t}": count must be a positive integer`);const m=o.shards??1,c=f(t,s,m),v=await this.store.get(c),{status:h,value:u}=N(d(o,m),v,{consume:i,count:a,now:this.now(),reserve:e.reserve??!1});if(u!==void 0&&await this.store.set(c,u),!h.ok&&e.throws)throw new w(h);return h}}export{z as RateLimiter};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{LunoraError as M}from"@lunora/errors";const l=r=>r.capacity??r.rate,v=(r,e)=>{throw new M("INTERNAL",`@lunora/ratelimit: requested count ${String(r)} exceeds the limiter capacity ${String(e)}`)},f=(r,e,t)=>{const o=l(r),u=r.rate/r.period,n=e??{ts:t,value:o},a=Math.max(0,t-n.ts);return{available:Math.min(o,n.value+a*u),capacity:o,ratePerMs:u}},w=(r,e,t)=>{const o=r.start??0,u=o+Math.floor((t-o)/r.period)*r.period;if(!e||e.ts<u){let n=0;return e&&(e.value<0||r.capacity!==void 0)&&(n=e.value),{ts:u,value:Math.min(l(r),n+r.rate)}}return{ts:e.ts,value:e.value}},k=(r,e,t)=>{const o=r.start??0,u=o+Math.floor((t-o)/r.period)*r.period,n=t-u,a=(r.period-n)/r.period;let s=0,i=0;return e?.ts===u?(s=e.prev??0,i=e.value):e?.ts===u-r.period&&(s=e.value),{currentCount:i,elapsed:n,previousCount:s,weight:a,windowStart:u}},A=(r,e,t)=>{const{available:o,capacity:u,ratePerMs:n}=f(r,e,t.now);if(o>=t.count){const i={ts:t.now,value:o-t.count};return{status:{ok:!0,retryAfter:0},value:t.consume?i:void 0}}const a=t.count-o,s=Math.ceil(a/n);return t.consume&&t.reserve&&t.count<=u?{status:{ok:!0,retryAfter:s},value:{ts:t.now,value:o-t.count}}:(t.count>u&&v(t.count,u),{status:{ok:!1,reason:"rate",retryAfter:s},value:void 0})},b=(r,e,t)=>{const o=l(r),u=w(r,e,t.now);if(u.value>=t.count){const a={ts:u.ts,value:u.value-t.count};return{status:{ok:!0,retryAfter:0},value:t.consume?a:void 0}}const n=u.ts+r.period-t.now;return t.consume&&t.reserve&&t.count<=o?{status:{ok:!0,retryAfter:n},value:{ts:u.ts,value:u.value-t.count}}:(t.count>o&&v(t.count,o),{status:{ok:!1,reason:"rate",retryAfter:n},value:void 0})},g=(r,e,t)=>{const o=r.rate,{period:u}=r,{currentCount:n,elapsed:a,previousCount:s,weight:i,windowStart:h}=k(r,e,t.now),d=s*i+n+t.count<=o,p=()=>{const c=o-n-t.count;if(s>0&&c>=0)return Math.ceil(u-a-c*u/s);const m=o-t.count,y=n>0?Math.max(0,u-m*u/n):0;return Math.ceil(u-a+y)};if(d||t.consume&&t.reserve&&t.count<=o){const c={prev:s,ts:h,value:n+t.count};return{status:{ok:!0,retryAfter:d?0:p()},value:t.consume?c:void 0}}return t.count>o&&v(t.count,o),{status:{ok:!1,reason:"rate",retryAfter:p()},value:void 0}},x=(r,e,t)=>{if(r.kind==="token bucket")return{ts:t,value:f(r,e,t).available};if(r.kind==="sliding window"){const{currentCount:o,previousCount:u,weight:n,windowStart:a}=k(r,e,t);return{ts:a,value:Math.max(0,r.rate-(u*n+o))}}return w(r,e,t)},S=(r,e,t)=>r.kind==="token bucket"?A(r,e,t):r.kind==="sliding window"?g(r,e,t):b(r,e,t);export{x as availableAt,S as evaluate};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{rateLimit as a}from"./rateLimit-DLcwK-84.mjs";import{RateLimiter as i}from"./RateLimiter-
|
|
1
|
+
import{rateLimit as a}from"./rateLimit-DLcwK-84.mjs";import{RateLimiter as i}from"./RateLimiter-CitqoGTI.mjs";import{createDbStore as m}from"./createDbStore-CwJBMxDQ.mjs";const p=(t,o,r={})=>a(e=>new i({config:t,store:m({db:e.db,...r.store})}),o,r);export{p 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.18",
|
|
4
4
|
"description": "Rate limiting: token-bucket / fixed-window / sliding-window algorithms, deny list, sharding, 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.15"
|
|
50
50
|
},
|
|
51
51
|
"engines": {
|
|
52
52
|
"node": "^22.15.0 || >=24.11.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as n}from"@lunora/errors";import{availableAt as p,evaluate as N}from"./availableAt-DITZ7jdT.mjs";import d from"./RateLimitError-xJ2Iv-i7.mjs";import{createMemoryStore as g}from"./createDbStore-CwJBMxDQ.mjs";const v=(r,t)=>t===void 0?encodeURIComponent(r):`${encodeURIComponent(r)}:${encodeURIComponent(t)}`,b=(r,t)=>{let e=0;for(let i=0;i<r.length;i+=1)e=e*31+r.charCodeAt(i)|0;return Math.abs(e)%t},w=(r,t)=>t>1?{...r,capacity:(r.capacity??r.rate)/t,rate:r.rate/t}:r,I=(r,t,e)=>{const i=v(r,t);return e>1?Array.from({length:e},(o,s)=>`${i}#${String(s)}`):[i]},f=(r,t,e)=>{const i=v(r,t);return e>1?`${i}#${String(b(i,e))}`:i};class R{config;denyList;normalize;now;store;constructor(t){this.config=t.config,this.denyList=new Set(t.denyList),this.normalize=t.normalize??(e=>e),this.now=t.now??Date.now,this.store=t.store??g();for(const[e,i]of Object.entries(this.config)){if(i.shards!==void 0&&(!Number.isInteger(i.shards)||i.shards<1))throw new n("INTERNAL",`rate limit "${e}": shards must be a positive integer`);if(!Number.isFinite(i.period)||i.period<=0)throw new n("INTERNAL",`rate limit "${e}": period must be a positive number`);if(!Number.isFinite(i.rate)||i.rate<=0)throw new n("INTERNAL",`rate limit "${e}": rate must be a positive number`);if(i.capacity!==void 0&&(!Number.isFinite(i.capacity)||i.capacity<0))throw new n("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 i=this.resolve(t),o=i.shards??1,s=this.now(),a=e.key===void 0?void 0:this.normalize(e.key),m=f(t,a,o),c=p(w(i,o),await this.store.get(m),s);return{config:i,ts:c.ts,value:c.value}}async limit(t,e={}){return this.run(t,e,!0)}async reset(t,e={}){const i=this.resolve(t).shards??1,o=e.key===void 0?void 0:this.normalize(e.key);await Promise.all(I(t,o,i).map(s=>Promise.resolve(this.store.delete(s))))}resolve(t){const e=this.config[t];if(!e)throw new n("INTERNAL",`rate limit "${t}" is not configured`);return e}async run(t,e,i){const o=this.resolve(t),s=e.key===void 0?void 0:this.normalize(e.key);if(s!==void 0&&(this.denyList.has(s)||this.denyList.has(e.key))){const l={ok:!1,reason:"deny",retryAfter:Number.POSITIVE_INFINITY};if(e.throws)throw new d(l);return l}const a=e.count??1;if(!Number.isInteger(a)||a<=0)throw new n("INTERNAL",`rate limit "${t}": count must be a positive integer`);const m=o.shards??1,c=f(t,s,m),y=await this.store.get(c),{status:h,value:u}=N(w(o,m),y,{consume:i,count:a,now:this.now(),reserve:e.reserve??!1});if(u!==void 0&&await this.store.set(c,u),!h.ok&&e.throws)throw new d(h);return h}}export{R as RateLimiter};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{LunoraError as l}from"@lunora/errors";const v=r=>r.capacity??r.rate,p=(r,e,t)=>{const n=v(r),u=r.rate/r.period,o=e??{ts:t,value:n},a=Math.max(0,t-o.ts);return{available:Math.min(n,o.value+a*u),capacity:n,ratePerMs:u}},w=(r,e,t)=>{const n=r.start??0,u=n+Math.floor((t-n)/r.period)*r.period;if(!e||e.ts<u){let o=0;return e&&(e.value<0||r.capacity!==void 0)&&(o=e.value),{ts:u,value:Math.min(v(r),o+r.rate)}}return{ts:e.ts,value:e.value}},h=(r,e,t)=>{const n=r.start??0,u=n+Math.floor((t-n)/r.period)*r.period,o=t-u,a=(r.period-o)/r.period;let i=0,s=0;return e?.ts===u?(i=e.prev??0,s=e.value):e?.ts===u-r.period&&(i=e.value),{currentCount:s,elapsed:o,previousCount:i,weight:a,windowStart:u}},A=(r,e,t)=>{const{available:n,capacity:u,ratePerMs:o}=p(r,e,t.now);if(n>=t.count){const s={ts:t.now,value:n-t.count};return{status:{ok:!0,retryAfter:0},value:t.consume?s:void 0}}const a=t.count-n,i=Math.ceil(a/o);if(t.consume&&t.reserve&&t.count<=u)return{status:{ok:!0,retryAfter:i},value:{ts:t.now,value:n-t.count}};if(t.count>u)throw new l("INTERNAL",`@lunora/ratelimit: requested count ${String(t.count)} exceeds the limiter capacity ${String(u)}`);return{status:{ok:!1,reason:"rate",retryAfter:i},value:void 0}},M=(r,e,t)=>{const n=v(r),u=w(r,e,t.now);if(u.value>=t.count){const a={ts:u.ts,value:u.value-t.count};return{status:{ok:!0,retryAfter:0},value:t.consume?a:void 0}}const o=u.ts+r.period-t.now;if(t.consume&&t.reserve&&t.count<=n)return{status:{ok:!0,retryAfter:o},value:{ts:u.ts,value:u.value-t.count}};if(t.count>n)throw new l("INTERNAL",`@lunora/ratelimit: requested count ${String(t.count)} exceeds the limiter capacity ${String(n)}`);return{status:{ok:!1,reason:"rate",retryAfter:o},value:void 0}},g=(r,e,t)=>{const n=r.rate,{period:u}=r,{currentCount:o,elapsed:a,previousCount:i,weight:s,windowStart:m}=h(r,e,t.now),d=i*s+o+t.count<=n,f=()=>{const c=n-o-t.count;if(i>0&&c>=0)return Math.ceil(u-a-c*u/i);const k=n-t.count,y=o>0?Math.max(0,u-k*u/o):0;return Math.ceil(u-a+y)};if(d||t.consume&&t.reserve&&t.count<=n){const c={prev:i,ts:m,value:o+t.count};return{status:{ok:!0,retryAfter:d?0:f()},value:t.consume?c:void 0}}if(t.count>n)throw new l("INTERNAL",`@lunora/ratelimit: requested count ${String(t.count)} exceeds the limiter capacity ${String(n)}`);return{status:{ok:!1,reason:"rate",retryAfter:f()},value:void 0}},b=(r,e,t)=>{if(r.kind==="token bucket")return{ts:t,value:p(r,e,t).available};if(r.kind==="sliding window"){const{currentCount:n,previousCount:u,weight:o,windowStart:a}=h(r,e,t);return{ts:a,value:Math.max(0,r.rate-(u*o+n))}}return w(r,e,t)},x=(r,e,t)=>r.kind==="token bucket"?A(r,e,t):r.kind==="sliding window"?g(r,e,t):M(r,e,t);export{b as availableAt,x as evaluate};
|