limitly 2.0.0 → 3.0.0

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.
Files changed (100) hide show
  1. package/README.md +210 -40
  2. package/dist/algorithms/concurrency.d.ts +11 -0
  3. package/dist/algorithms/concurrency.d.ts.map +1 -0
  4. package/dist/algorithms/concurrency.js +19 -0
  5. package/dist/algorithms/concurrency.js.map +1 -0
  6. package/dist/algorithms/factory.d.ts.map +1 -1
  7. package/dist/algorithms/factory.js +6 -0
  8. package/dist/algorithms/factory.js.map +1 -1
  9. package/dist/algorithms/gcra.d.ts +10 -0
  10. package/dist/algorithms/gcra.d.ts.map +1 -0
  11. package/dist/algorithms/gcra.js +15 -0
  12. package/dist/algorithms/gcra.js.map +1 -0
  13. package/dist/index.d.ts +7 -3
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +15 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/limiter.d.ts +4 -0
  18. package/dist/limiter.d.ts.map +1 -1
  19. package/dist/limiter.js +21 -2
  20. package/dist/limiter.js.map +1 -1
  21. package/dist/middleware/bun.d.ts.map +1 -1
  22. package/dist/middleware/bun.js +21 -10
  23. package/dist/middleware/bun.js.map +1 -1
  24. package/dist/middleware/express.d.ts.map +1 -1
  25. package/dist/middleware/express.js +23 -13
  26. package/dist/middleware/express.js.map +1 -1
  27. package/dist/middleware/fastify.d.ts.map +1 -1
  28. package/dist/middleware/fastify.js +19 -8
  29. package/dist/middleware/fastify.js.map +1 -1
  30. package/dist/middleware/hono.d.ts.map +1 -1
  31. package/dist/middleware/hono.js +26 -12
  32. package/dist/middleware/hono.js.map +1 -1
  33. package/dist/middleware/koa.d.ts.map +1 -1
  34. package/dist/middleware/koa.js +24 -13
  35. package/dist/middleware/koa.js.map +1 -1
  36. package/dist/middleware/nest.d.ts.map +1 -1
  37. package/dist/middleware/nest.js +22 -12
  38. package/dist/middleware/nest.js.map +1 -1
  39. package/dist/observability/index.d.ts +4 -0
  40. package/dist/observability/index.d.ts.map +1 -0
  41. package/dist/observability/index.js +10 -0
  42. package/dist/observability/index.js.map +1 -0
  43. package/dist/observability/opentelemetry.d.ts +28 -0
  44. package/dist/observability/opentelemetry.d.ts.map +1 -0
  45. package/dist/observability/opentelemetry.js +85 -0
  46. package/dist/observability/opentelemetry.js.map +1 -0
  47. package/dist/observability/prometheus.d.ts +27 -0
  48. package/dist/observability/prometheus.d.ts.map +1 -0
  49. package/dist/observability/prometheus.js +88 -0
  50. package/dist/observability/prometheus.js.map +1 -0
  51. package/dist/observability/types.d.ts +9 -0
  52. package/dist/observability/types.d.ts.map +1 -0
  53. package/dist/observability/types.js +3 -0
  54. package/dist/observability/types.js.map +1 -0
  55. package/dist/prometheus.d.ts +3 -0
  56. package/dist/prometheus.d.ts.map +1 -0
  57. package/dist/prometheus.js +9 -0
  58. package/dist/prometheus.js.map +1 -0
  59. package/dist/scripts/concurrencyAcquire.lua +34 -0
  60. package/dist/scripts/concurrencyRelease.lua +9 -0
  61. package/dist/scripts/gcra.lua +38 -0
  62. package/dist/stores/factory.d.ts.map +1 -1
  63. package/dist/stores/factory.js +5 -1
  64. package/dist/stores/factory.js.map +1 -1
  65. package/dist/stores/memcached-store.d.ts +3 -0
  66. package/dist/stores/memcached-store.d.ts.map +1 -1
  67. package/dist/stores/memcached-store.js +94 -0
  68. package/dist/stores/memcached-store.js.map +1 -1
  69. package/dist/stores/redis-store.d.ts +8 -1
  70. package/dist/stores/redis-store.d.ts.map +1 -1
  71. package/dist/stores/redis-store.js +46 -3
  72. package/dist/stores/redis-store.js.map +1 -1
  73. package/dist/stores/types.d.ts +3 -0
  74. package/dist/stores/types.d.ts.map +1 -1
  75. package/dist/types/index.d.ts +33 -5
  76. package/dist/types/index.d.ts.map +1 -1
  77. package/dist/utils/defaults.d.ts +3 -1
  78. package/dist/utils/defaults.d.ts.map +1 -1
  79. package/dist/utils/defaults.js +43 -2
  80. package/dist/utils/defaults.js.map +1 -1
  81. package/dist/utils/limit-execution.d.ts +40 -0
  82. package/dist/utils/limit-execution.d.ts.map +1 -0
  83. package/dist/utils/limit-execution.js +50 -0
  84. package/dist/utils/limit-execution.js.map +1 -0
  85. package/dist/utils/memcached.d.ts +1 -0
  86. package/dist/utils/memcached.d.ts.map +1 -1
  87. package/dist/utils/memcached.js +12 -0
  88. package/dist/utils/memcached.js.map +1 -1
  89. package/dist/utils/metrics.d.ts.map +1 -1
  90. package/dist/utils/metrics.js +41 -2
  91. package/dist/utils/metrics.js.map +1 -1
  92. package/dist/utils/redis.d.ts +4 -1
  93. package/dist/utils/redis.d.ts.map +1 -1
  94. package/dist/utils/redis.js +37 -4
  95. package/dist/utils/redis.js.map +1 -1
  96. package/dist/utils/scripts.d.ts +16 -2
  97. package/dist/utils/scripts.d.ts.map +1 -1
  98. package/dist/utils/scripts.js +79 -33
  99. package/dist/utils/scripts.js.map +1 -1
  100. package/package.json +29 -3
package/README.md CHANGED
@@ -12,6 +12,12 @@ npm install limitly ioredis
12
12
  # Memcached backend
13
13
  npm install limitly memcached
14
14
 
15
+ # OpenTelemetry
16
+ npm install limitly ioredis @opentelemetry/api
17
+
18
+ # Prometheus
19
+ npm install limitly ioredis prom-client
20
+
15
21
  # NestJS
16
22
  npm install limitly ioredis @nestjs/common @nestjs/core
17
23
  ```
@@ -39,14 +45,7 @@ const app = express();
39
45
  const redis = new Redis();
40
46
  const limiter = createLimiter({ redis });
41
47
 
42
- app.use(
43
- limiter.middleware({
44
- algorithm: "sliding-window",
45
- limit: 100,
46
- window: 60,
47
- key: (req) => req.ip,
48
- }),
49
- );
48
+ app.use(limiter.middleware({ key: (req) => req.ip }));
50
49
  ```
51
50
 
52
51
  ### Fastify
@@ -77,15 +76,7 @@ import { createLimiter } from "limitly";
77
76
  const app = new Hono();
78
77
  const limiter = createLimiter({ redis: new Redis() });
79
78
 
80
- app.use(
81
- "*",
82
- limiter.honoMiddleware({
83
- algorithm: "sliding-window",
84
- limit: 100,
85
- window: 60,
86
- key: (c) => c.req.header("x-api-key"),
87
- })
88
- );
79
+ app.use("*", limiter.honoMiddleware({ key: (c) => c.req.header("x-api-key") }));
89
80
  ```
90
81
 
91
82
  ### Koa
@@ -98,14 +89,7 @@ import { createLimiter } from "limitly";
98
89
  const app = new Koa();
99
90
  const limiter = createLimiter({ redis: new Redis() });
100
91
 
101
- app.use(
102
- limiter.koaMiddleware({
103
- algorithm: "sliding-window",
104
- limit: 100,
105
- window: 60,
106
- key: (ctx) => ctx.ip,
107
- })
108
- );
92
+ app.use(limiter.koaMiddleware({ key: (ctx) => ctx.ip }));
109
93
  ```
110
94
 
111
95
  ### Bun
@@ -117,9 +101,6 @@ import { composeBunHandler, createLimiter } from "limitly";
117
101
  const limiter = createLimiter({ redis: new Redis() });
118
102
 
119
103
  const rateLimit = limiter.bunMiddleware({
120
- algorithm: "sliding-window",
121
- limit: 100,
122
- window: 60,
123
104
  key: (req) => req.headers.get("x-api-key"),
124
105
  });
125
106
 
@@ -142,7 +123,6 @@ import { RateLimit } from "limitly/nest";
142
123
 
143
124
  const limiter = createLimiter({ redis: new Redis() });
144
125
  const NestGuard = limiter.nestGuard({
145
- algorithm: "sliding-window",
146
126
  limit: 100,
147
127
  window: 60,
148
128
  key: (req) => req.ip,
@@ -163,7 +143,7 @@ import { RateLimit } from "limitly/nest";
163
143
  @Controller("api")
164
144
  export class ApiController {
165
145
  @Get()
166
- @RateLimit({ algorithm: "sliding-window", limit: 10, window: 60 })
146
+ @RateLimit({ limit: 10, window: 60 })
167
147
  findAll() {
168
148
  return { ok: true };
169
149
  }
@@ -191,7 +171,7 @@ export class AppModule {}
191
171
 
192
172
  ## Default Algorithm
193
173
 
194
- If you omit algorithm options, limitly uses **sliding-window** with `limit: 100` and `window: 60`:
174
+ If you omit algorithm options, limitly uses **GCRA** with `limit: 100` and `window: 60`:
195
175
 
196
176
  ```typescript
197
177
  const limiter = createLimiter({ redis: new Redis() });
@@ -201,7 +181,7 @@ app.use(limiter.middleware({ key: (req) => req.ip }));
201
181
  // equivalent to:
202
182
  app.use(
203
183
  limiter.middleware({
204
- algorithm: "sliding-window",
184
+ algorithm: "gcra",
205
185
  limit: 100,
206
186
  window: 60,
207
187
  key: (req) => req.ip,
@@ -226,7 +206,6 @@ Use `limiter.check()` outside middleware — useful for login guards, background
226
206
 
227
207
  ```typescript
228
208
  const result = await limiter.check(req.ip ?? "unknown", {
229
- algorithm: "sliding-window",
230
209
  limit: 5,
231
210
  window: 10,
232
211
  });
@@ -243,6 +222,20 @@ if (!result.allowed) {
243
222
 
244
223
  ## Algorithms
245
224
 
225
+ ### GCRA (default)
226
+
227
+ Generic Cell Rate Algorithm — smooth rate limiting with controlled bursts. Uses a single TAT (theoretical arrival time) per key, so it's memory-efficient compared to sliding window:
228
+
229
+ ```typescript
230
+ limiter.middleware({
231
+ limit: 100,
232
+ window: 60, // seconds — algorithm defaults to gcra
233
+ key: (req) => req.ip,
234
+ });
235
+ ```
236
+
237
+ GCRA enforces an average rate of `limit / window` while allowing short bursts up to `limit`. Ideal when you want token-bucket-like behavior with predictable storage costs.
238
+
246
239
  ### Sliding Window
247
240
 
248
241
  Uses Redis Sorted Sets (or Memcached counters) for rate limiting over a rolling time window.
@@ -267,6 +260,45 @@ limiter.middleware({
267
260
  });
268
261
  ```
269
262
 
263
+ ### Concurrency
264
+
265
+ Limits simultaneous in-flight requests per key. Slots are acquired on entry and released when the response finishes (middleware handles this automatically):
266
+
267
+ ```typescript
268
+ limiter.middleware({
269
+ algorithm: "concurrency",
270
+ limit: 10, // max concurrent requests
271
+ ttl: 300, // lease TTL in seconds for stale slot cleanup
272
+ key: (req) => req.ip,
273
+ });
274
+ ```
275
+
276
+ For manual acquire/release outside middleware:
277
+
278
+ ```typescript
279
+ const acquired = await limiter.acquire("job-42", {
280
+ algorithm: "concurrency",
281
+ limit: 5,
282
+ ttl: 120,
283
+ });
284
+
285
+ if (!acquired.allowed) {
286
+ throw new Error("Too many concurrent jobs");
287
+ }
288
+
289
+ try {
290
+ await runJob();
291
+ } finally {
292
+ await limiter.release("job-42", acquired.slotId!, {
293
+ algorithm: "concurrency",
294
+ limit: 5,
295
+ ttl: 120,
296
+ });
297
+ }
298
+ ```
299
+
300
+ Storage keys use the `cc:` prefix: `{keyPrefix}:cc:{id}`.
301
+
270
302
  ## Storage Backends
271
303
 
272
304
  limitly supports Redis, Valkey, DragonflyDB (Redis-compatible), and Memcached.
@@ -287,13 +319,35 @@ createLimiter({ store: "valkey", redis: "redis://localhost:6379" });
287
319
  // DragonflyDB
288
320
  createLimiter({ store: "dragonfly", redis: { host: "localhost", port: 6379 } });
289
321
 
290
- // Cluster
322
+ // Cluster (auto-pipelining, script warmup, master reads)
291
323
  createLimiter({
292
324
  store: "redis",
325
+ redis: {
326
+ nodes: [
327
+ { host: "127.0.0.1", port: 7000 },
328
+ { host: "127.0.0.1", port: 7001 },
329
+ ],
330
+ options: {
331
+ redisOptions: { password: "secret" },
332
+ },
333
+ },
334
+ });
335
+
336
+ // Optional: pin all keys to one slot (use only when you need co-location)
337
+ createLimiter({
293
338
  redis: { nodes: [{ host: "127.0.0.1", port: 7000 }] },
339
+ hashTag: "limitly",
294
340
  });
295
341
  ```
296
342
 
343
+ Cluster optimizations built into limitly:
344
+
345
+ - **ioredis `defineCommand`** — Lua scripts are registered cluster-aware, avoiding per-node `NOSCRIPT` fallbacks
346
+ - **Script warmup** — scripts are preloaded on all master nodes at startup (disable with `warmupScripts: false`)
347
+ - **Auto-pipelining** — enabled by default for higher throughput under concurrent load
348
+ - **Hash tags** — optional `hashTag` for slot pinning; leave unset to spread keys across slots (recommended)
349
+ - **Master reads** — `scaleReads: "master"` by default so checks always hit the authoritative node
350
+
297
351
  ### Memcached
298
352
 
299
353
  Memcached uses counter-based sliding window and CAS token bucket (no Lua required):
@@ -349,6 +403,8 @@ Key format:
349
403
  ```
350
404
  {keyPrefix}:sw:{id} — sliding window
351
405
  {keyPrefix}:tb:{id} — token bucket
406
+ {keyPrefix}:gcra:{id} — GCRA
407
+ {keyPrefix}:cc:{id} — concurrency
352
408
  ```
353
409
 
354
410
  ## Response Headers
@@ -368,9 +424,6 @@ Disable with `headers: false`.
368
424
 
369
425
  ```typescript
370
426
  limiter.middleware({
371
- algorithm: "sliding-window",
372
- limit: 100,
373
- window: 60,
374
427
  onLimitReached(req, res) {
375
428
  res.status(429).json({ code: "RATE_LIMITED" });
376
429
  },
@@ -391,9 +444,6 @@ const limiter = createLimiter({
391
444
 
392
445
  // Per-route override
393
446
  limiter.middleware({
394
- algorithm: "sliding-window",
395
- limit: 100,
396
- window: 60,
397
447
  onMetrics: (event) => metrics.increment(`ratelimit.${event.type}`),
398
448
  });
399
449
  ```
@@ -417,6 +467,123 @@ onMetrics: [logToConsole, sendToDatadog]
417
467
 
418
468
  `limiter.check()` and all framework middleware use the same metrics pipeline.
419
469
 
470
+ ## OpenTelemetry
471
+
472
+ Use the `limitly/otel` helpers to emit OTEL metrics and traces. Only `@opentelemetry/api` is required — bring your own SDK/exporter:
473
+
474
+ ```typescript
475
+ import Redis from "ioredis";
476
+ import { createLimiter } from "limitly";
477
+ import { createOpenTelemetryInstrumentation } from "limitly/otel";
478
+
479
+ const otel = createOpenTelemetryInstrumentation();
480
+
481
+ const limiter = createLimiter({
482
+ redis: new Redis(),
483
+ tracer: otel.tracer,
484
+ onMetrics: otel.onMetrics,
485
+ });
486
+
487
+ app.use(limiter.middleware({ key: (req) => req.ip }));
488
+ ```
489
+
490
+ `createOpenTelemetryInstrumentation()` returns:
491
+
492
+ - **`tracer`** — creates `limitly.check` spans with `limitly.algorithm`, `limitly.store`, `limitly.outcome`, `limitly.limit`, and `limitly.remaining` attributes
493
+ - **`onMetrics`** — records `limitly.check.total` (counter) and `limitly.check.duration` (histogram, ms)
494
+
495
+ Use the pieces independently when needed:
496
+
497
+ ```typescript
498
+ import {
499
+ createOpenTelemetryMetricsHook,
500
+ createOpenTelemetryTracer,
501
+ } from "limitly/otel";
502
+
503
+ const limiter = createLimiter({
504
+ redis: new Redis(),
505
+ tracer: createOpenTelemetryTracer(),
506
+ onMetrics: createOpenTelemetryMetricsHook({ includeKey: false }),
507
+ });
508
+ ```
509
+
510
+ Combine with custom hooks:
511
+
512
+ ```typescript
513
+ onMetrics: [otel.onMetrics, customHook]
514
+ ```
515
+
516
+ ## Prometheus
517
+
518
+ Use `limitly/prometheus` to expose rate limit metrics for scraping:
519
+
520
+ ```typescript
521
+ import express from "express";
522
+ import Redis from "ioredis";
523
+ import { createLimiter } from "limitly";
524
+ import {
525
+ createPrometheusExporter,
526
+ createPrometheusHandler,
527
+ } from "limitly/prometheus";
528
+
529
+ const app = express();
530
+ const prometheus = createPrometheusExporter();
531
+
532
+ const limiter = createLimiter({
533
+ redis: new Redis(),
534
+ onMetrics: prometheus.onMetrics,
535
+ });
536
+
537
+ app.use(limiter.middleware({ key: (req) => req.ip }));
538
+ app.get("/metrics", createPrometheusHandler(prometheus));
539
+ ```
540
+
541
+ Metrics exposed:
542
+
543
+ | Metric | Type | Labels |
544
+ |--------|------|--------|
545
+ | `limitly_check_total` | Counter | `algorithm`, `outcome`, `store` |
546
+ | `limitly_check_duration_seconds` | Histogram | `algorithm`, `outcome`, `store` |
547
+
548
+ `outcome` is one of `allowed`, `blocked`, `error`, or `fail_open`.
549
+
550
+ Options:
551
+
552
+ | Option | Default | Description |
553
+ |--------|---------|-------------|
554
+ | `register` | new `Registry` | Prometheus registry |
555
+ | `prefix` | `"limitly"` | Metric name prefix |
556
+ | `includeKey` | `false` | Add rate limit key as a label (high cardinality) |
557
+ | `durationBuckets` | `[0.001, 0.005, 0.01, …, 1]` | Histogram buckets (seconds) |
558
+
559
+ Use a shared registry with your existing metrics:
560
+
561
+ ```typescript
562
+ import { Registry } from "prom-client";
563
+
564
+ const register = new Registry();
565
+ const prometheus = createPrometheusExporter({ register });
566
+
567
+ onMetrics: [prometheus.onMetrics, otherHook];
568
+ ```
569
+
570
+ Scrape manually without HTTP middleware:
571
+
572
+ ```typescript
573
+ const body = await prometheus.getMetrics();
574
+ ```
575
+
576
+ Use `createPrometheusMetricsHook` when you only need the `onMetrics` hook without the HTTP handler:
577
+
578
+ ```typescript
579
+ import { createPrometheusMetricsHook } from "limitly/prometheus";
580
+
581
+ const limiter = createLimiter({
582
+ redis: new Redis(),
583
+ onMetrics: createPrometheusMetricsHook({ prefix: "api" }),
584
+ });
585
+ ```
586
+
420
587
  ## Fail Open / Closed
421
588
 
422
589
  When Redis is unavailable:
@@ -436,9 +603,12 @@ Implement custom algorithms with the `RateLimitStrategy` interface:
436
603
  ```typescript
437
604
  interface RateLimitStrategy {
438
605
  consume(key: string): Promise<RateLimitResult>;
606
+ release?(key: string, slotId: string): Promise<void>;
439
607
  }
440
608
  ```
441
609
 
610
+ `release` is optional — only needed for concurrency-style strategies that hold a slot until the response finishes.
611
+
442
612
  ## Performance
443
613
 
444
614
  - Atomic operations via Lua scripts (`EVALSHA`)
@@ -0,0 +1,11 @@
1
+ import type { ConcurrencyConfig, RateLimitResult, RateLimitStrategy } from "../types";
2
+ import type { RateLimitStore } from "../stores/types";
3
+ export declare class ConcurrencyStrategy implements RateLimitStrategy {
4
+ private readonly store;
5
+ private readonly limit;
6
+ private readonly ttl;
7
+ constructor(store: RateLimitStore, config: ConcurrencyConfig);
8
+ consume(key: string): Promise<RateLimitResult>;
9
+ release(key: string, slotId: string): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=concurrency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"concurrency.d.ts","sourceRoot":"","sources":["../../src/algorithms/concurrency.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EACf,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGtD,qBAAa,mBAAoB,YAAW,iBAAiB;IAC3D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAEjB,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,iBAAiB;IAMtD,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAI9C,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG1D"}
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConcurrencyStrategy = void 0;
4
+ const defaults_1 = require("../utils/defaults");
5
+ class ConcurrencyStrategy {
6
+ constructor(store, config) {
7
+ this.store = store;
8
+ this.limit = config.limit;
9
+ this.ttl = config.ttl ?? defaults_1.DEFAULT_CONCURRENCY.ttl;
10
+ }
11
+ async consume(key) {
12
+ return this.store.concurrencyAcquire(key, this.limit, this.ttl);
13
+ }
14
+ async release(key, slotId) {
15
+ await this.store.concurrencyRelease(key, slotId);
16
+ }
17
+ }
18
+ exports.ConcurrencyStrategy = ConcurrencyStrategy;
19
+ //# sourceMappingURL=concurrency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"concurrency.js","sourceRoot":"","sources":["../../src/algorithms/concurrency.ts"],"names":[],"mappings":";;;AAMA,gDAAwD;AAExD,MAAa,mBAAmB;IAK9B,YAAY,KAAqB,EAAE,MAAyB;QAC1D,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,IAAI,8BAAmB,CAAC,GAAI,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IAClE,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,MAAc;QACvC,MAAM,IAAI,CAAC,KAAK,CAAC,kBAAkB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IACnD,CAAC;CACF;AAlBD,kDAkBC"}
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/algorithms/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAItD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,eAAe,GACtB,iBAAiB,CAanB"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/algorithms/factory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AACnE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAMtD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,eAAe,GACtB,iBAAiB,CAiBnB"}
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createStrategy = createStrategy;
4
+ const concurrency_1 = require("./concurrency");
5
+ const gcra_1 = require("./gcra");
4
6
  const sliding_window_1 = require("./sliding-window");
5
7
  const token_bucket_1 = require("./token-bucket");
6
8
  function createStrategy(store, config) {
@@ -9,6 +11,10 @@ function createStrategy(store, config) {
9
11
  return new sliding_window_1.SlidingWindowStrategy(store, config);
10
12
  case "token-bucket":
11
13
  return new token_bucket_1.TokenBucketStrategy(store, config);
14
+ case "concurrency":
15
+ return new concurrency_1.ConcurrencyStrategy(store, config);
16
+ case "gcra":
17
+ return new gcra_1.GcraStrategy(store, config);
12
18
  default: {
13
19
  const exhaustive = config;
14
20
  throw new Error(`Unknown algorithm: ${exhaustive.algorithm}`);
@@ -1 +1 @@
1
- {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/algorithms/factory.ts"],"names":[],"mappings":";;AAKA,wCAgBC;AAnBD,qDAAyD;AACzD,iDAAqD;AAErD,SAAgB,cAAc,CAC5B,KAAqB,EACrB,MAAuB;IAEvB,QAAQ,MAAM,CAAC,SAAS,EAAE,CAAC;QACzB,KAAK,gBAAgB;YACnB,OAAO,IAAI,sCAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAClD,KAAK,cAAc;YACjB,OAAO,IAAI,kCAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAChD,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,UAAU,GAAU,MAAM,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,sBAAuB,UAA8B,CAAC,SAAS,EAAE,CAClE,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/algorithms/factory.ts"],"names":[],"mappings":";;AAOA,wCAoBC;AAzBD,+CAAoD;AACpD,iCAAsC;AACtC,qDAAyD;AACzD,iDAAqD;AAErD,SAAgB,cAAc,CAC5B,KAAqB,EACrB,MAAuB;IAEvB,QAAQ,MAAM,CAAC,SAAS,EAAE,CAAC;QACzB,KAAK,gBAAgB;YACnB,OAAO,IAAI,sCAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAClD,KAAK,cAAc;YACjB,OAAO,IAAI,kCAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAChD,KAAK,aAAa;YAChB,OAAO,IAAI,iCAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAChD,KAAK,MAAM;YACT,OAAO,IAAI,mBAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QACzC,OAAO,CAAC,CAAC,CAAC;YACR,MAAM,UAAU,GAAU,MAAM,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,sBAAuB,UAA8B,CAAC,SAAS,EAAE,CAClE,CAAC;QACJ,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { GcraConfig, RateLimitResult, RateLimitStrategy } from "../types";
2
+ import type { RateLimitStore } from "../stores/types";
3
+ export declare class GcraStrategy implements RateLimitStrategy {
4
+ private readonly store;
5
+ private readonly limit;
6
+ private readonly window;
7
+ constructor(store: RateLimitStore, config: GcraConfig);
8
+ consume(key: string): Promise<RateLimitResult>;
9
+ }
10
+ //# sourceMappingURL=gcra.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcra.d.ts","sourceRoot":"","sources":["../../src/algorithms/gcra.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,qBAAa,YAAa,YAAW,iBAAiB;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,KAAK,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU;IAM/C,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;CAGrD"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GcraStrategy = void 0;
4
+ class GcraStrategy {
5
+ constructor(store, config) {
6
+ this.store = store;
7
+ this.limit = config.limit;
8
+ this.window = config.window;
9
+ }
10
+ async consume(key) {
11
+ return this.store.gcra(key, this.limit, this.window);
12
+ }
13
+ }
14
+ exports.GcraStrategy = GcraStrategy;
15
+ //# sourceMappingURL=gcra.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gcra.js","sourceRoot":"","sources":["../../src/algorithms/gcra.ts"],"names":[],"mappings":";;;AAGA,MAAa,YAAY;IAKvB,YAAY,KAAqB,EAAE,MAAkB;QACnD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW;QACvB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACvD,CAAC;CACF;AAdD,oCAcC"}
package/dist/index.d.ts CHANGED
@@ -7,16 +7,20 @@ export { applyRateLimitHeaders, composeBunHandler, createBunMiddleware, jsonResp
7
7
  export type { BunMiddleware, BunNext } from "./middleware/bun";
8
8
  export { createNestGuard, limitlyNestModule, LimitlyModule, RateLimit, RATE_LIMIT_KEY, } from "./middleware/nest";
9
9
  export type { NestRateLimitOptions } from "./middleware/nest";
10
+ export { ConcurrencyStrategy } from "./algorithms/concurrency";
11
+ export { GcraStrategy } from "./algorithms/gcra";
10
12
  export { SlidingWindowStrategy } from "./algorithms/sliding-window";
11
13
  export { TokenBucketStrategy } from "./algorithms/token-bucket";
12
14
  export type { RateLimitStrategy } from "./algorithms/strategy";
13
- export { DEFAULT_KEY_PREFIX } from "./utils/redis";
14
- export { DEFAULT_SLIDING_WINDOW, DEFAULT_TOKEN_BUCKET, resolveAlgorithmConfig, resolveMiddlewareOptions, } from "./utils/defaults";
15
+ export { DEFAULT_CLUSTER_OPTIONS, DEFAULT_KEY_PREFIX, buildKey, createRedisClient, isRedisCluster, } from "./utils/redis";
16
+ export { getClusterScriptDefinitions, registerRedisScripts, warmupRedisScripts, } from "./utils/scripts";
17
+ export { DEFAULT_CONCURRENCY, DEFAULT_GCRA, DEFAULT_SLIDING_WINDOW, DEFAULT_TOKEN_BUCKET, resolveAlgorithmConfig, resolveMiddlewareOptions, } from "./utils/defaults";
15
18
  export { consumeRateLimit } from "./utils/metrics";
16
19
  export type { RateLimitCheckOutcome } from "./utils/metrics";
20
+ export type { RateLimitSpan, RateLimitTracer } from "./observability/types";
17
21
  export { createStore, resolveStoreType } from "./stores/factory";
18
22
  export { RedisStore } from "./stores/redis-store";
19
23
  export { MemcachedStore } from "./stores/memcached-store";
20
24
  export type { RateLimitStore, StoreType } from "./stores/types";
21
- export type { AlgorithmConfig, BaseMiddlewareOptions, MemcachedClient, MemcachedConfig, MemcachedOptions, MiddlewareOptions, MiddlewareOptionsInput, RateLimitHeaders, RateLimitMetricsEvent, RateLimitMetricsHook, RateLimitResult, RedisClient, RedisConfig, RedisLimitOptions, SlidingWindowConfig, TokenBucketConfig, } from "./types";
25
+ export type { AlgorithmConfig, ConcurrencyConfig, GcraConfig, BaseMiddlewareOptions, MemcachedClient, MemcachedConfig, MemcachedOptions, MiddlewareOptions, MiddlewareOptionsInput, RateLimitHeaders, RateLimitMetricsEvent, RateLimitMetricsHook, RateLimitResult, RedisClient, RedisClusterConfig, RedisConfig, RedisLimitOptions, SlidingWindowConfig, TokenBucketConfig, } from "./types";
22
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChE,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EACL,qBAAqB,EACrB,iBAAiB,EACjB,mBAAmB,EACnB,YAAY,GACb,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EACL,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,SAAS,EACT,cAAc,GACf,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,YAAY,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EACL,uBAAuB,EACvB,kBAAkB,EAClB,QAAQ,EACR,iBAAiB,EACjB,cAAc,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,2BAA2B,EAC3B,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,mBAAmB,EACnB,YAAY,EACZ,sBAAsB,EACtB,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChE,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,UAAU,EACV,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,EAChB,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EACf,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MemcachedStore = exports.RedisStore = exports.resolveStoreType = exports.createStore = exports.consumeRateLimit = exports.resolveMiddlewareOptions = exports.resolveAlgorithmConfig = exports.DEFAULT_TOKEN_BUCKET = exports.DEFAULT_SLIDING_WINDOW = exports.DEFAULT_KEY_PREFIX = exports.TokenBucketStrategy = exports.SlidingWindowStrategy = exports.RATE_LIMIT_KEY = exports.RateLimit = exports.LimitlyModule = exports.limitlyNestModule = exports.createNestGuard = exports.jsonResponse = exports.createBunMiddleware = exports.composeBunHandler = exports.applyRateLimitHeaders = exports.createKoaMiddleware = exports.createHonoMiddleware = exports.redisLimitPlugin = exports.createFastifyPlugin = exports.createExpressMiddleware = exports.createLimiter = exports.RedisLimit = void 0;
3
+ exports.MemcachedStore = exports.RedisStore = exports.resolveStoreType = exports.createStore = exports.consumeRateLimit = exports.resolveMiddlewareOptions = exports.resolveAlgorithmConfig = exports.DEFAULT_TOKEN_BUCKET = exports.DEFAULT_SLIDING_WINDOW = exports.DEFAULT_GCRA = exports.DEFAULT_CONCURRENCY = exports.warmupRedisScripts = exports.registerRedisScripts = exports.getClusterScriptDefinitions = exports.isRedisCluster = exports.createRedisClient = exports.buildKey = exports.DEFAULT_KEY_PREFIX = exports.DEFAULT_CLUSTER_OPTIONS = exports.TokenBucketStrategy = exports.SlidingWindowStrategy = exports.GcraStrategy = exports.ConcurrencyStrategy = exports.RATE_LIMIT_KEY = exports.RateLimit = exports.LimitlyModule = exports.limitlyNestModule = exports.createNestGuard = exports.jsonResponse = exports.createBunMiddleware = exports.composeBunHandler = exports.applyRateLimitHeaders = exports.createKoaMiddleware = exports.createHonoMiddleware = exports.redisLimitPlugin = exports.createFastifyPlugin = exports.createExpressMiddleware = exports.createLimiter = exports.RedisLimit = void 0;
4
4
  var limiter_1 = require("./limiter");
5
5
  Object.defineProperty(exports, "RedisLimit", { enumerable: true, get: function () { return limiter_1.RedisLimit; } });
6
6
  Object.defineProperty(exports, "createLimiter", { enumerable: true, get: function () { return limiter_1.createLimiter; } });
@@ -24,13 +24,27 @@ Object.defineProperty(exports, "limitlyNestModule", { enumerable: true, get: fun
24
24
  Object.defineProperty(exports, "LimitlyModule", { enumerable: true, get: function () { return nest_1.LimitlyModule; } });
25
25
  Object.defineProperty(exports, "RateLimit", { enumerable: true, get: function () { return nest_1.RateLimit; } });
26
26
  Object.defineProperty(exports, "RATE_LIMIT_KEY", { enumerable: true, get: function () { return nest_1.RATE_LIMIT_KEY; } });
27
+ var concurrency_1 = require("./algorithms/concurrency");
28
+ Object.defineProperty(exports, "ConcurrencyStrategy", { enumerable: true, get: function () { return concurrency_1.ConcurrencyStrategy; } });
29
+ var gcra_1 = require("./algorithms/gcra");
30
+ Object.defineProperty(exports, "GcraStrategy", { enumerable: true, get: function () { return gcra_1.GcraStrategy; } });
27
31
  var sliding_window_1 = require("./algorithms/sliding-window");
28
32
  Object.defineProperty(exports, "SlidingWindowStrategy", { enumerable: true, get: function () { return sliding_window_1.SlidingWindowStrategy; } });
29
33
  var token_bucket_1 = require("./algorithms/token-bucket");
30
34
  Object.defineProperty(exports, "TokenBucketStrategy", { enumerable: true, get: function () { return token_bucket_1.TokenBucketStrategy; } });
31
35
  var redis_1 = require("./utils/redis");
36
+ Object.defineProperty(exports, "DEFAULT_CLUSTER_OPTIONS", { enumerable: true, get: function () { return redis_1.DEFAULT_CLUSTER_OPTIONS; } });
32
37
  Object.defineProperty(exports, "DEFAULT_KEY_PREFIX", { enumerable: true, get: function () { return redis_1.DEFAULT_KEY_PREFIX; } });
38
+ Object.defineProperty(exports, "buildKey", { enumerable: true, get: function () { return redis_1.buildKey; } });
39
+ Object.defineProperty(exports, "createRedisClient", { enumerable: true, get: function () { return redis_1.createRedisClient; } });
40
+ Object.defineProperty(exports, "isRedisCluster", { enumerable: true, get: function () { return redis_1.isRedisCluster; } });
41
+ var scripts_1 = require("./utils/scripts");
42
+ Object.defineProperty(exports, "getClusterScriptDefinitions", { enumerable: true, get: function () { return scripts_1.getClusterScriptDefinitions; } });
43
+ Object.defineProperty(exports, "registerRedisScripts", { enumerable: true, get: function () { return scripts_1.registerRedisScripts; } });
44
+ Object.defineProperty(exports, "warmupRedisScripts", { enumerable: true, get: function () { return scripts_1.warmupRedisScripts; } });
33
45
  var defaults_1 = require("./utils/defaults");
46
+ Object.defineProperty(exports, "DEFAULT_CONCURRENCY", { enumerable: true, get: function () { return defaults_1.DEFAULT_CONCURRENCY; } });
47
+ Object.defineProperty(exports, "DEFAULT_GCRA", { enumerable: true, get: function () { return defaults_1.DEFAULT_GCRA; } });
34
48
  Object.defineProperty(exports, "DEFAULT_SLIDING_WINDOW", { enumerable: true, get: function () { return defaults_1.DEFAULT_SLIDING_WINDOW; } });
35
49
  Object.defineProperty(exports, "DEFAULT_TOKEN_BUCKET", { enumerable: true, get: function () { return defaults_1.DEFAULT_TOKEN_BUCKET; } });
36
50
  Object.defineProperty(exports, "resolveAlgorithmConfig", { enumerable: true, get: function () { return defaults_1.resolveAlgorithmConfig; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qCAAsD;AAA7C,qGAAA,UAAU,OAAA;AAAE,wGAAA,aAAa,OAAA;AAClC,gDAA+D;AAAtD,kHAAA,uBAAuB,OAAA;AAChC,gDAA6E;AAApE,8GAAA,mBAAmB,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAC9C,0CAAyD;AAAhD,4GAAA,oBAAoB,OAAA;AAC7B,wCAAuD;AAA9C,0GAAA,mBAAmB,OAAA;AAC5B,wCAK0B;AAJxB,4GAAA,qBAAqB,OAAA;AACrB,wGAAA,iBAAiB,OAAA;AACjB,0GAAA,mBAAmB,OAAA;AACnB,mGAAA,YAAY,OAAA;AAGd,0CAM2B;AALzB,uGAAA,eAAe,OAAA;AACf,yGAAA,iBAAiB,OAAA;AACjB,qGAAA,aAAa,OAAA;AACb,iGAAA,SAAS,OAAA;AACT,sGAAA,cAAc,OAAA;AAGhB,8DAAoE;AAA3D,uHAAA,qBAAqB,OAAA;AAC9B,0DAAgE;AAAvD,mHAAA,mBAAmB,OAAA;AAE5B,uCAAmD;AAA1C,2GAAA,kBAAkB,OAAA;AAC3B,6CAK0B;AAJxB,kHAAA,sBAAsB,OAAA;AACtB,gHAAA,oBAAoB,OAAA;AACpB,kHAAA,sBAAsB,OAAA;AACtB,oHAAA,wBAAwB,OAAA;AAE1B,2CAAmD;AAA1C,2GAAA,gBAAgB,OAAA;AAEzB,4CAAiE;AAAxD,sGAAA,WAAW,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AACtC,oDAAkD;AAAzC,yGAAA,UAAU,OAAA;AACnB,4DAA0D;AAAjD,iHAAA,cAAc,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qCAAsD;AAA7C,qGAAA,UAAU,OAAA;AAAE,wGAAA,aAAa,OAAA;AAClC,gDAA+D;AAAtD,kHAAA,uBAAuB,OAAA;AAChC,gDAA6E;AAApE,8GAAA,mBAAmB,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AAC9C,0CAAyD;AAAhD,4GAAA,oBAAoB,OAAA;AAC7B,wCAAuD;AAA9C,0GAAA,mBAAmB,OAAA;AAC5B,wCAK0B;AAJxB,4GAAA,qBAAqB,OAAA;AACrB,wGAAA,iBAAiB,OAAA;AACjB,0GAAA,mBAAmB,OAAA;AACnB,mGAAA,YAAY,OAAA;AAGd,0CAM2B;AALzB,uGAAA,eAAe,OAAA;AACf,yGAAA,iBAAiB,OAAA;AACjB,qGAAA,aAAa,OAAA;AACb,iGAAA,SAAS,OAAA;AACT,sGAAA,cAAc,OAAA;AAGhB,wDAA+D;AAAtD,kHAAA,mBAAmB,OAAA;AAC5B,0CAAiD;AAAxC,oGAAA,YAAY,OAAA;AACrB,8DAAoE;AAA3D,uHAAA,qBAAqB,OAAA;AAC9B,0DAAgE;AAAvD,mHAAA,mBAAmB,OAAA;AAE5B,uCAMuB;AALrB,gHAAA,uBAAuB,OAAA;AACvB,2GAAA,kBAAkB,OAAA;AAClB,iGAAA,QAAQ,OAAA;AACR,0GAAA,iBAAiB,OAAA;AACjB,uGAAA,cAAc,OAAA;AAEhB,2CAIyB;AAHvB,sHAAA,2BAA2B,OAAA;AAC3B,+GAAA,oBAAoB,OAAA;AACpB,6GAAA,kBAAkB,OAAA;AAEpB,6CAO0B;AANxB,+GAAA,mBAAmB,OAAA;AACnB,wGAAA,YAAY,OAAA;AACZ,kHAAA,sBAAsB,OAAA;AACtB,gHAAA,oBAAoB,OAAA;AACpB,kHAAA,sBAAsB,OAAA;AACtB,oHAAA,wBAAwB,OAAA;AAE1B,2CAAmD;AAA1C,2GAAA,gBAAgB,OAAA;AAGzB,4CAAiE;AAAxD,sGAAA,WAAW,OAAA;AAAE,2GAAA,gBAAgB,OAAA;AACtC,oDAAkD;AAAzC,yGAAA,UAAU,OAAA;AACnB,4DAA0D;AAAjD,iHAAA,cAAc,OAAA"}
package/dist/limiter.d.ts CHANGED
@@ -15,6 +15,10 @@ export declare class RedisLimit {
15
15
  getMemcached(): MemcachedClient;
16
16
  createStrategy(config: AlgorithmConfig): RateLimitStrategy;
17
17
  createStrategyFromOptions(options?: MiddlewareOptionsInput): RateLimitStrategy;
18
+ acquire(key: string, config?: MiddlewareOptionsInput, options?: {
19
+ failOpen?: boolean;
20
+ }): Promise<RateLimitResult>;
21
+ release(key: string, slotId: string, config?: MiddlewareOptionsInput): Promise<void>;
18
22
  check(key: string, config?: MiddlewareOptionsInput, options?: {
19
23
  failOpen?: boolean;
20
24
  }): Promise<RateLimitResult>;
@@ -1 +1 @@
1
- {"version":3,"file":"limiter.d.ts","sourceRoot":"","sources":["../src/limiter.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAQrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;gBAE5C,OAAO,EAAE,iBAAiB;IAStC,QAAQ,IAAI,cAAc;IAI1B,YAAY,IAAI,cAAc,CAAC,MAAM,CAAC;IAItC,iBAAiB,IAAI,sBAAsB;IAI3C,cAAc,CAAC,OAAO,GAAE,sBAA2B,GAAG,iBAAiB;IAIvE,QAAQ,IAAI,WAAW;IASvB,YAAY,IAAI,eAAe;IAS/B,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,iBAAiB;IAI1D,yBAAyB,CACvB,OAAO,GAAE,sBAA2B,GACnC,iBAAiB;IAOd,KAAK,CACT,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,sBAA2B,EACnC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAC/B,OAAO,CAAC,eAAe,CAAC;IAsB3B,UAAU,CAAC,OAAO,GAAE,sBAA2B;IAI/C,cAAc,CAAC,OAAO,GAAE,sBAA2B;IAInD,aAAa,CAAC,OAAO,GAAE,sBAA2B;IAIlD,aAAa,CAAC,OAAO,GAAE,sBAA2B;IAIlD,IAAI,aAAa,IAAI,kBAAkB,CAAC,sBAAsB,CAAC,CAE9D;IAED,SAAS,CAAC,cAAc,GAAE,sBAA2B,GAAG,IAAI,CAAC,WAAW,CAAC;IAIzE,OAAO,CAAC,oBAAoB;CAW7B;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAEpE;AAED,YAAY,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,CAAC"}
1
+ {"version":3,"file":"limiter.d.ts","sourceRoot":"","sources":["../src/limiter.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAQrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,EACf,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiB;IACvC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAyB;gBAE5C,OAAO,EAAE,iBAAiB;IAUtC,QAAQ,IAAI,cAAc;IAI1B,YAAY,IAAI,cAAc,CAAC,MAAM,CAAC;IAItC,iBAAiB,IAAI,sBAAsB;IAI3C,cAAc,CAAC,OAAO,GAAE,sBAA2B,GAAG,iBAAiB;IAIvE,QAAQ,IAAI,WAAW;IASvB,YAAY,IAAI,eAAe;IAS/B,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,iBAAiB;IAI1D,yBAAyB,CACvB,OAAO,GAAE,sBAA2B,GACnC,iBAAiB;IAOd,OAAO,CACX,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,sBAA2B,EACnC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAC/B,OAAO,CAAC,eAAe,CAAC;IAIrB,OAAO,CACX,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,sBAA2B,GAClC,OAAO,CAAC,IAAI,CAAC;IAgBV,KAAK,CACT,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,sBAA2B,EACnC,OAAO,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAE,GAC/B,OAAO,CAAC,eAAe,CAAC;IAwB3B,UAAU,CAAC,OAAO,GAAE,sBAA2B;IAI/C,cAAc,CAAC,OAAO,GAAE,sBAA2B;IAInD,aAAa,CAAC,OAAO,GAAE,sBAA2B;IAIlD,aAAa,CAAC,OAAO,GAAE,sBAA2B;IAIlD,IAAI,aAAa,IAAI,kBAAkB,CAAC,sBAAsB,CAAC,CAE9D;IAED,SAAS,CAAC,cAAc,GAAE,sBAA2B,GAAG,IAAI,CAAC,WAAW,CAAC;IAIzE,OAAO,CAAC,oBAAoB;CAe7B;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAEpE;AAED,YAAY,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,CAAC"}
package/dist/limiter.js CHANGED
@@ -18,6 +18,7 @@ class RedisLimit {
18
18
  this.failOpen = options.failOpen ?? true;
19
19
  this.defaultOptions = {
20
20
  onMetrics: options.onMetrics,
21
+ tracer: options.tracer,
21
22
  ...options.default,
22
23
  };
23
24
  }
@@ -51,9 +52,23 @@ class RedisLimit {
51
52
  createStrategyFromOptions(options = {}) {
52
53
  return (0, factory_1.createStrategy)(this.store, (0, defaults_1.resolveAlgorithmConfig)(options, this.defaultOptions));
53
54
  }
55
+ async acquire(key, config = {}, options) {
56
+ return this.check(key, config, options);
57
+ }
58
+ async release(key, slotId, config = {}) {
59
+ const resolved = this.resolveOptions(config);
60
+ if (resolved.algorithm !== "concurrency") {
61
+ throw new Error('release() requires algorithm: "concurrency"');
62
+ }
63
+ const strategy = this.createStrategy((0, defaults_1.resolveAlgorithmConfig)(config, this.defaultOptions));
64
+ if (!strategy.release) {
65
+ throw new Error("Configured strategy does not support release()");
66
+ }
67
+ await strategy.release(key, slotId);
68
+ }
54
69
  async check(key, config = {}, options) {
55
70
  const resolved = this.resolveOptions(config);
56
- const strategy = this.createStrategy(resolved);
71
+ const strategy = this.createStrategy((0, defaults_1.resolveAlgorithmConfig)(config, this.defaultOptions));
57
72
  const shouldFailOpen = options?.failOpen ?? this.failOpen;
58
73
  const outcome = await (0, metrics_1.consumeRateLimit)({
59
74
  strategy,
@@ -89,7 +104,11 @@ class RedisLimit {
89
104
  return (0, nest_1.createNestGuard)(this)(defaultOptions);
90
105
  }
91
106
  createFailOpenResult(config) {
92
- const limit = config.algorithm === "sliding-window" ? config.limit : config.capacity;
107
+ const limit = config.algorithm === "sliding-window" ||
108
+ config.algorithm === "concurrency" ||
109
+ config.algorithm === "gcra"
110
+ ? config.limit
111
+ : config.capacity;
93
112
  return {
94
113
  allowed: true,
95
114
  limit,
@@ -1 +1 @@
1
- {"version":3,"file":"limiter.js","sourceRoot":"","sources":["../src/limiter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"limiter.js","sourceRoot":"","sources":["../src/limiter.ts"],"names":[],"mappings":"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"}