@lunora/ratelimit 1.0.0-alpha.80 → 1.0.0-alpha.82

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 CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  </a>
8
8
 
9
- <h3 align="center">Rate limiting: token-bucket / fixed-window / sliding-window algorithms, deny list, sharding, pluggable stores, and procedure middleware</h3>
9
+ <h3 align="center">Rate limiting: token-bucket / fixed-window / sliding-window algorithms, deny list, pluggable stores, and procedure middleware</h3>
10
10
 
11
11
  <!-- END_PACKAGE_OG_IMAGE_PLACEHOLDER -->
12
12
 
@@ -34,7 +34,7 @@
34
34
 
35
35
  ---
36
36
 
37
- Rate limiting for Lunora: token-bucket, fixed-window, and sliding-window algorithms, a deny list, optional sharding for hot limits, pluggable stores (in-memory, SQL, or the Lunora ORM), and procedure middleware that rides the `.use()` chain.
37
+ Rate limiting for Lunora: token-bucket, fixed-window, and sliding-window algorithms, a deny list, pluggable stores (in-memory, SQL, or the Lunora ORM), and procedure middleware that rides the `.use()` chain.
38
38
 
39
39
  Part of the [Lunora](https://github.com/anolilab/lunora) framework — a type-safe, real-time backend on Cloudflare Workers + Durable Objects with a Vite-first DX.
40
40
 
package/dist/index.d.mts CHANGED
@@ -19,18 +19,6 @@ interface RateLimitConfig {
19
19
  period: number;
20
20
  /** Tokens granted per `period`. */
21
21
  rate: number;
22
- /**
23
- * Split a hot limit across N independent sub-buckets to avoid a single
24
- * contended key/Durable Object. Each shard enforces `rate / shards` (and
25
- * `capacity / shards`); a request is routed to a shard via a deterministic
26
- * hash of `(name, key)`, so the same key always lands on the same shard and
27
- * a single key's effective throughput is exactly `rate / shards`. Aggregate
28
- * throughput across many distinct keys approaches `rate` as keys spread
29
- * uniformly across shards. Reserve it for high-volume limits where
30
- * contention bites; leave unset (one bucket) otherwise. Must be a positive
31
- * integer — `1` is equivalent to unset.
32
- */
33
- shards?: number;
34
22
  /**
35
23
  * Phase offset in epoch milliseconds for windowed algorithms — windows
36
24
  * align to `start + n * period`. Ignored by token buckets. Defaults to `0`.
@@ -135,8 +123,7 @@ interface RateLimiterOptions<Names extends string> {
135
123
  denyList?: Iterable<string>;
136
124
  /**
137
125
  * Optional key normalizer applied to every incoming `args.key` (the
138
- * deny-list check, the storage key, and downstream shard selection all see
139
- * the normalized form). Use for case-folding, trimming, or canonicalizing
126
+ * deny-list check and the storage key both see the normalized form). Use for case-folding, trimming, or canonicalizing
140
127
  * IPs/emails so equivalent inputs share a single bucket. The deny-list
141
128
  * itself is consulted as-is; normalize the deny-list entries up front to
142
129
  * match.
@@ -166,9 +153,7 @@ declare class RateLimiter<Names extends string = string> {
166
153
  * Read the current config and the units admittable right now for a
167
154
  * `(name, key)` pair. The value is projected forward to the current clock
168
155
  * (token-bucket refill, fixed-window rollover, sliding-window decay), not
169
- * the last persisted figure. For a sharded limit it reads only the single
170
- * shard `limit()`/`run()` would route this key to — the sibling shards are
171
- * never touched by this key, so summing them would over-report.
156
+ * the last persisted figure.
172
157
  */
173
158
  getValue(name: Names, args?: {
174
159
  key?: string;
package/dist/index.d.ts CHANGED
@@ -19,18 +19,6 @@ interface RateLimitConfig {
19
19
  period: number;
20
20
  /** Tokens granted per `period`. */
21
21
  rate: number;
22
- /**
23
- * Split a hot limit across N independent sub-buckets to avoid a single
24
- * contended key/Durable Object. Each shard enforces `rate / shards` (and
25
- * `capacity / shards`); a request is routed to a shard via a deterministic
26
- * hash of `(name, key)`, so the same key always lands on the same shard and
27
- * a single key's effective throughput is exactly `rate / shards`. Aggregate
28
- * throughput across many distinct keys approaches `rate` as keys spread
29
- * uniformly across shards. Reserve it for high-volume limits where
30
- * contention bites; leave unset (one bucket) otherwise. Must be a positive
31
- * integer — `1` is equivalent to unset.
32
- */
33
- shards?: number;
34
22
  /**
35
23
  * Phase offset in epoch milliseconds for windowed algorithms — windows
36
24
  * align to `start + n * period`. Ignored by token buckets. Defaults to `0`.
@@ -135,8 +123,7 @@ interface RateLimiterOptions<Names extends string> {
135
123
  denyList?: Iterable<string>;
136
124
  /**
137
125
  * Optional key normalizer applied to every incoming `args.key` (the
138
- * deny-list check, the storage key, and downstream shard selection all see
139
- * the normalized form). Use for case-folding, trimming, or canonicalizing
126
+ * deny-list check and the storage key both see the normalized form). Use for case-folding, trimming, or canonicalizing
140
127
  * IPs/emails so equivalent inputs share a single bucket. The deny-list
141
128
  * itself is consulted as-is; normalize the deny-list entries up front to
142
129
  * match.
@@ -166,9 +153,7 @@ declare class RateLimiter<Names extends string = string> {
166
153
  * Read the current config and the units admittable right now for a
167
154
  * `(name, key)` pair. The value is projected forward to the current clock
168
155
  * (token-bucket refill, fixed-window rollover, sliding-window decay), not
169
- * the last persisted figure. For a sharded limit it reads only the single
170
- * shard `limit()`/`run()` would route this key to — the sibling shards are
171
- * never touched by this key, so summing them would over-report.
156
+ * the last persisted figure.
172
157
  */
173
158
  getValue(name: Names, args?: {
174
159
  key?: string;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{availableAt as o,evaluate as a}from"./packem_shared/availableAt-BzjRBFIT.mjs";import{default as i}from"./packem_shared/dbRateLimit-Cjcp6OlW.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-Bj5bkehV.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-R_GR6ipU.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};
1
+ import{availableAt as o,evaluate as a}from"./packem_shared/availableAt-BSKYrouo.mjs";import{default as i}from"./packem_shared/dbRateLimit-rlAL4c2M.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-DKXPLL9z.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 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};
@@ -0,0 +1 @@
1
+ import{LunoraError as b}from"@lunora/errors";const o=e=>e.capacity??e.rate,E=e=>e.kind==="sliding window"?e.rate:o(e),i=(e,r)=>{throw new b("INTERNAL",`@lunora/ratelimit: requested count ${String(e)} exceeds the limiter capacity ${String(r)}`)},M=(e,r,t)=>{const u=o(e),a=e.rate/e.period,s=r??{ts:t,value:u},c=Math.max(0,t-s.ts);return{available:Math.min(u,s.value+c*a),capacity:u,ratePerMs:a}},w=(e,r,t)=>{const u=e.start??0,a=u+Math.floor((t-u)/e.period)*e.period;if(!r)return{ts:a,value:o(e)};if(r.ts<a){let s=0,c=1;return(r.value<0||e.capacity!==void 0)&&(s=r.value,c=Math.max(1,Math.ceil((a-r.ts)/e.period))),{ts:a,value:Math.min(o(e),s+c*e.rate)}}return{ts:r.ts,value:r.value}},x=(e,r,t)=>{const u=e.start??0,a=u+Math.floor((t-u)/e.period)*e.period,s=t-a,c=(e.period-s)/e.period;let n=0,l=0;return r?.ts===a?(n=r.prev??0,l=r.value):r?.ts===a-e.period&&(n=r.value),{currentCount:l,elapsed:s,previousCount:n,weight:c,windowStart:a}},S=(e,r,t)=>{const{available:u,capacity:a,ratePerMs:s}=M(e,r,t.now);if(u>=t.count){const l={ts:t.now,value:u-t.count};return{status:{ok:!0,retryAfter:0},value:t.consume?l:void 0}}const c=t.count-u,n=Math.ceil(c/s);return t.consume&&t.reserve&&t.count<=a?{status:{ok:!0,retryAfter:n},value:{ts:t.now,value:u-t.count}}:(t.count>a&&i(t.count,a),{status:{ok:!1,reason:"rate",retryAfter:n},value:void 0})},y=(e,r,t,u,a,s)=>{const c=d=>w(e,r,t+d*e.period).value>=a;let l=u<0||e.capacity!==void 0?Math.max(1,Math.ceil((a-u)/e.rate)):1;for(let d=0;d<4&&l>1&&c(l-1);d+=1)l-=1;for(let d=0;d<4&&!c(l);d+=1)l+=1;return t+l*e.period-s},W=(e,r,t)=>{const u=o(e),a=w(e,r,t.now);if(a.value>=t.count){const s={ts:a.ts,value:a.value-t.count};return{status:{ok:!0,retryAfter:0},value:t.consume?s:void 0}}if(t.consume&&t.reserve&&t.count<=u){const s=a.value-t.count;return{status:{ok:!0,retryAfter:y(e,{ts:a.ts,value:s},a.ts,s,0,t.now)},value:{ts:a.ts,value:s}}}return t.count>u&&i(t.count,u),{status:{ok:!1,reason:"rate",retryAfter:y(e,r,a.ts,a.value,t.count,t.now)},value:void 0}},N=(e,r,t)=>{const u=e.rate,{period:a}=e,{currentCount:s,elapsed:c,previousCount:n,weight:l,windowStart:d}=x(e,r,t.now),m=n*l+s+t.count<=u,h=v=>{const k=u-v-t.count;if(n>0&&k>=0)return Math.ceil(a-c-k*a/n);const A=u-t.count,C=v>0?Math.max(0,a-A*a/v):0;return Math.ceil(a-c+C)};if(m||t.consume&&t.reserve&&t.count<=u){const v={prev:n,ts:d,value:s+t.count};return{status:{ok:!0,retryAfter:m?0:h(v.value)},value:t.consume?v:void 0}}return t.count>u&&i(t.count,u),{status:{ok:!1,reason:"rate",retryAfter:h(s)},value:void 0}},B=(e,r,t)=>{if(e.kind==="token bucket")return{ts:t,value:M(e,r,t).available};if(e.kind==="sliding window"){const{currentCount:u,previousCount:a,weight:s,windowStart:c}=x(e,r,t);return{ts:c,value:Math.max(0,e.rate-(a*s+u))}}return w(e,r,t)},L=(e,r,t)=>e.kind==="token bucket"?S(e,r,t):e.kind==="sliding window"?N(e,r,t):W(e,r,t);export{B as availableAt,E as enforcedCapacity,L as evaluate};
@@ -1 +1 @@
1
- import{rateLimit as o}from"./rateLimit-CWZur6XE.mjs";import{RateLimiter as m}from"./RateLimiter-Bj5bkehV.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};
1
+ import{rateLimit as o}from"./rateLimit-CWZur6XE.mjs";import{RateLimiter as m}from"./RateLimiter-DKXPLL9z.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};
@@ -0,0 +1 @@
1
+ import{enforcedCapacity as n}from"./availableAt-BSKYrouo.mjs";const h=(t,r)=>({check:async c=>t.check(r,{key:c}),record:async(c,a)=>{if(!Number.isFinite(a)||a<=0)return t.check(r,{key:c});const{config:e}=await t.getValue(r,{key:c}),o=Math.max(1,Math.floor(n(e)));return t.limit(r,{count:Math.min(Math.ceil(a),o),key:c,reserve:!0})}});export{h as tokenBudget};
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lunora/ratelimit",
3
- "version": "1.0.0-alpha.80",
4
- "description": "Rate limiting: token-bucket / fixed-window / sliding-window algorithms, deny list, sharding, pluggable stores, and procedure middleware",
3
+ "version": "1.0.0-alpha.82",
4
+ "description": "Rate limiting: token-bucket / fixed-window / sliding-window algorithms, deny list, pluggable stores, and procedure middleware",
5
5
  "keywords": [
6
6
  "cloudflare",
7
7
  "durable-objects",
@@ -1 +0,0 @@
1
- import{LunoraError as a}from"@lunora/errors";import{availableAt as p,evaluate as v}from"./availableAt-BzjRBFIT.mjs";import u from"./RateLimitError-9Y5OLxpl.mjs";import{createMemoryStore as N}from"./createDbStore-ve7GsO2i.mjs";const f=(o,e)=>e===void 0?encodeURIComponent(o):`${encodeURIComponent(o)}:${encodeURIComponent(e)}`,g=(o,e)=>{let t=0;for(let r=0;r<o.length;r+=1)t=t*31+o.charCodeAt(r)|0;return Math.abs(t)%e},I="@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.",y=(o,e)=>e>1?{...o,capacity:(o.capacity??o.rate)/e,rate:o.rate/e}:o,L=(o,e,t)=>{const r=f(o,e);return t>1?Array.from({length:t},(i,s)=>`${r}#${String(s)}`):[r]},w=(o,e,t)=>{const r=f(o,e);return t>1?`${r}#${String(g(r,t))}`:r};class ${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(I),this.store=e.store??N();for(const[t,r]of Object.entries(this.config)){if(r.shards!==void 0&&(!Number.isInteger(r.shards)||r.shards<1))throw new a("INTERNAL",`rate limit "${t}": shards must be a positive integer`);if(!Number.isFinite(r.period)||r.period<=0)throw new a("INTERNAL",`rate limit "${t}": period must be a positive number`);if(!Number.isFinite(r.rate)||r.rate<=0)throw new a("INTERNAL",`rate limit "${t}": rate must be a positive number`);if(r.capacity!==void 0&&(!Number.isFinite(r.capacity)||r.capacity<0))throw new a("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),i=r.shards??1,s=this.now(),n=this.normalizeKey(t.key),h=w(e,n,i),c=p(y(r,i),await this.store.get(h),s);return{config:r,ts:c.ts,value:c.value}}async limit(e,t={}){return this.run(e,t,!0)}async reset(e,t={}){const r=this.resolve(e).shards??1,i=this.normalizeKey(t.key);await Promise.all(L(e,i,r).map(s=>Promise.resolve(this.store.delete(s))))}normalizeKey(e){return e===void 0?void 0:this.normalize(e)}resolve(e){const t=this.config[e];if(!t)throw new a("INTERNAL",`rate limit "${e}" is not configured`);return t}async run(e,t,r){const i=this.resolve(e),s=this.normalizeKey(t.key);if(s!==void 0&&(this.denyList.has(s)||this.denyList.has(t.key))){const d={ok:!1,reason:"deny",retryAfter:Number.POSITIVE_INFINITY};if(t.throws)throw new u(d);return d}const n=t.count??1;if(!Number.isInteger(n)||n<=0)throw new a("INTERNAL",`rate limit "${e}": count must be a positive integer`);const h=i.shards??1,c=w(e,s,h),b=await this.store.get(c),{status:l,value:m}=v(y(i,h),b,{consume:r,count:n,now:this.now(),reserve:t.reserve??!1});if(m!==void 0&&await this.store.set(c,m),!l.ok&&t.throws)throw new u(l);return l}}export{$ as RateLimiter};
@@ -1 +0,0 @@
1
- import{LunoraError as C}from"@lunora/errors";const v=e=>e.capacity??e.rate,j=e=>e.kind==="sliding window"?e.rate:v(e),o=(e,a)=>{throw new C("INTERNAL",`@lunora/ratelimit: requested count ${String(e)} exceeds the limiter capacity ${String(a)}`)},k=(e,a,t)=>{const u=v(e),r=e.rate/e.period,s=a??{ts:t,value:u},c=Math.max(0,t-s.ts);return{available:Math.min(u,s.value+c*r),capacity:u,ratePerMs:r}},h=(e,a,t)=>{const u=e.start??0,r=u+Math.floor((t-u)/e.period)*e.period;if(!a||a.ts<r){let s=0,c=1;return a&&(a.value<0||e.capacity!==void 0)&&(s=a.value,c=Math.max(1,Math.ceil((r-a.ts)/e.period))),{ts:r,value:Math.min(v(e),s+c*e.rate)}}return{ts:a.ts,value:a.value}},y=(e,a,t)=>{const u=e.start??0,r=u+Math.floor((t-u)/e.period)*e.period,s=t-r,c=(e.period-s)/e.period;let n=0,l=0;return a?.ts===r?(n=a.prev??0,l=a.value):a?.ts===r-e.period&&(n=a.value),{currentCount:l,elapsed:s,previousCount:n,weight:c,windowStart:r}},b=(e,a,t)=>{const{available:u,capacity:r,ratePerMs:s}=k(e,a,t.now);if(u>=t.count){const l={ts:t.now,value:u-t.count};return{status:{ok:!0,retryAfter:0},value:t.consume?l:void 0}}const c=t.count-u,n=Math.ceil(c/s);return t.consume&&t.reserve&&t.count<=r?{status:{ok:!0,retryAfter:n},value:{ts:t.now,value:u-t.count}}:(t.count>r&&o(t.count,r),{status:{ok:!1,reason:"rate",retryAfter:n},value:void 0})},S=(e,a,t)=>{const u=v(e),r=h(e,a,t.now);if(r.value>=t.count){const c={ts:r.ts,value:r.value-t.count};return{status:{ok:!0,retryAfter:0},value:t.consume?c:void 0}}const s=r.ts+e.period-t.now;return t.consume&&t.reserve&&t.count<=u?{status:{ok:!0,retryAfter:s},value:{ts:r.ts,value:r.value-t.count}}:(t.count>u&&o(t.count,u),{status:{ok:!1,reason:"rate",retryAfter:s},value:void 0})},f=(e,a,t)=>{const u=e.rate,{period:r}=e,{currentCount:s,elapsed:c,previousCount:n,weight:l,windowStart:M}=y(e,a,t.now),i=n*l+s+t.count<=u,w=d=>{const m=u-d-t.count;if(n>0&&m>=0)return Math.ceil(r-c-m*r/n);const x=u-t.count,A=d>0?Math.max(0,r-x*r/d):0;return Math.ceil(r-c+A)};if(i||t.consume&&t.reserve&&t.count<=u){const d={prev:n,ts:M,value:s+t.count};return{status:{ok:!0,retryAfter:i?0:w(d.value)},value:t.consume?d:void 0}}return t.count>u&&o(t.count,u),{status:{ok:!1,reason:"rate",retryAfter:w(s)},value:void 0}},E=(e,a,t)=>{if(e.kind==="token bucket")return{ts:t,value:k(e,a,t).available};if(e.kind==="sliding window"){const{currentCount:u,previousCount:r,weight:s,windowStart:c}=y(e,a,t);return{ts:c,value:Math.max(0,e.rate-(r*s+u))}}return h(e,a,t)},B=(e,a,t)=>e.kind==="token bucket"?b(e,a,t):e.kind==="sliding window"?f(e,a,t):S(e,a,t);export{E as availableAt,j as enforcedCapacity,B as evaluate};
@@ -1 +0,0 @@
1
- import{enforcedCapacity as n}from"./availableAt-BzjRBFIT.mjs";const u=(t,r)=>({check:async c=>t.check(r,{key:c}),record:async(c,a)=>{if(!Number.isFinite(a)||a<=0)return t.check(r,{key:c});const{config:e}=await t.getValue(r,{key:c}),o=Math.max(1,Math.floor(n(e)/(e.shards??1)));return t.limit(r,{count:Math.min(Math.ceil(a),o),key:c,reserve:!0})}});export{u as tokenBudget};