@diia-inhouse/redis 3.1.55 → 3.1.59

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.
@@ -18,11 +18,35 @@ interface SetValueOptions {
18
18
  */
19
19
  tags?: string[];
20
20
  }
21
+ /**
22
+ * Parsed reply of the `CL.THROTTLE` command.
23
+ *
24
+ * @see {@link https://www.dragonflydb.io/docs/command-reference/strings/cl.throttle | Dragonfly CL.THROTTLE}
25
+ */
21
26
  interface ThrottleResult {
27
+ /**
28
+ * Whether the action must be limited, i.e. the bucket had no room for the requested quantity.
29
+ */
22
30
  limited: boolean;
31
+ /**
32
+ * Total limit of the key, equals `maxBurst + 1`.
33
+ * Maps to the `X-RateLimit-Limit` header.
34
+ */
23
35
  totalLimit: number;
36
+ /**
37
+ * Remaining limit of the key.
38
+ * Maps to the `X-RateLimit-Remaining` header.
39
+ */
24
40
  remaining: number;
41
+ /**
42
+ * Number of seconds to wait before retrying if the action was limited, otherwise `-1`.
43
+ * Maps to the `Retry-After` header.
44
+ */
25
45
  retryAfterSec: number;
46
+ /**
47
+ * Number of seconds until the limit is fully restored.
48
+ * Maps to the `X-RateLimit-Reset` header.
49
+ */
26
50
  resetAfterSec: number;
27
51
  }
28
52
  type StoreStatusResult = {
@@ -38,6 +38,33 @@ declare class StoreService implements OnHealthCheck, OnDestroy {
38
38
  hdel(key: string, ...fields: string[]): Promise<number>;
39
39
  expire(key: string, seconds: number): Promise<number>;
40
40
  bumpTags(tags: string[]): Promise<"OK" | null>;
41
+ /**
42
+ * Applies a leaky bucket rate limit to the given key via the `CL.THROTTLE` command.
43
+ *
44
+ * The call is atomic: it consumes `quantity` tokens and reports the resulting bucket state in one round trip.
45
+ * Tokens are refilled continuously at `rate / periodSec`, so bursts are smoothed out
46
+ * and there are no edge cases at fixed-window boundaries.
47
+ *
48
+ * @param key - Identifier to rate limit against, e.g. a user id or an IP address.
49
+ * @param maxBurst - Extra tokens allowed on top of the steady rate, i.e. how large a burst may be.
50
+ * The bucket therefore holds `maxBurst + 1` tokens.
51
+ * @param rate - Number of tokens refilled per `periodSec`.
52
+ * @param periodSec - Length of the refill period in seconds.
53
+ * @param quantity - Number of tokens the call consumes.
54
+ * @returns Whether the action is limited plus the rate limit headers state.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * // 10 requests per minute, tolerating a burst of 5 extra requests
59
+ * const { limited, retryAfterSec } = await store.throttle(`login:${userId}`, 5, 10, 60)
60
+ * if (limited) {
61
+ * res.setHeader('Retry-After', String(retryAfterSec))
62
+ * res.writeHead(HttpStatusCode.TOO_MANY_REQUESTS)
63
+ * }
64
+ * ```
65
+ *
66
+ * @see {@link https://www.dragonflydb.io/docs/command-reference/strings/cl.throttle | Dragonfly CL.THROTTLE}
67
+ */
41
68
  throttle(key: string, maxBurst: number, rate: number, periodSec: number, quantity?: number): Promise<ThrottleResult>;
42
69
  flushDb(): Promise<"OK">;
43
70
  private validate;
@@ -138,6 +138,33 @@ var StoreService = class {
138
138
  for (const tagKey of tags) tagsConfig[tagKey] = timestamp;
139
139
  return await this.clientRW.set(this.tagsKey, JSON.stringify(tagsConfig));
140
140
  }
141
+ /**
142
+ * Applies a leaky bucket rate limit to the given key via the `CL.THROTTLE` command.
143
+ *
144
+ * The call is atomic: it consumes `quantity` tokens and reports the resulting bucket state in one round trip.
145
+ * Tokens are refilled continuously at `rate / periodSec`, so bursts are smoothed out
146
+ * and there are no edge cases at fixed-window boundaries.
147
+ *
148
+ * @param key - Identifier to rate limit against, e.g. a user id or an IP address.
149
+ * @param maxBurst - Extra tokens allowed on top of the steady rate, i.e. how large a burst may be.
150
+ * The bucket therefore holds `maxBurst + 1` tokens.
151
+ * @param rate - Number of tokens refilled per `periodSec`.
152
+ * @param periodSec - Length of the refill period in seconds.
153
+ * @param quantity - Number of tokens the call consumes.
154
+ * @returns Whether the action is limited plus the rate limit headers state.
155
+ *
156
+ * @example
157
+ * ```ts
158
+ * // 10 requests per minute, tolerating a burst of 5 extra requests
159
+ * const { limited, retryAfterSec } = await store.throttle(`login:${userId}`, 5, 10, 60)
160
+ * if (limited) {
161
+ * res.setHeader('Retry-After', String(retryAfterSec))
162
+ * res.writeHead(HttpStatusCode.TOO_MANY_REQUESTS)
163
+ * }
164
+ * ```
165
+ *
166
+ * @see {@link https://www.dragonflydb.io/docs/command-reference/strings/cl.throttle | Dragonfly CL.THROTTLE}
167
+ */
141
168
  async throttle(key, maxBurst, rate, periodSec, quantity = 1) {
142
169
  const result = await this.clientRW.call("CL.THROTTLE", key, maxBurst, rate, periodSec, quantity);
143
170
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diia-inhouse/redis",
3
- "version": "3.1.55",
3
+ "version": "3.1.59",
4
4
  "type": "module",
5
5
  "description": "Redis services - redlock, pubsub, cache and store",
6
6
  "main": "dist/index.js",
@@ -47,14 +47,14 @@
47
47
  "@diia-inhouse/validators": ">=2.0.0"
48
48
  },
49
49
  "devDependencies": {
50
- "@diia-inhouse/configs": "7.0.1",
51
- "@diia-inhouse/diia-logger": "4.4.41",
52
- "@diia-inhouse/env": "3.3.44",
53
- "@diia-inhouse/errors": "2.2.16",
54
- "@diia-inhouse/oxc-config": "2.1.1",
55
- "@diia-inhouse/test": "8.3.2",
56
- "@diia-inhouse/types": "14.2.3",
57
- "@diia-inhouse/validators": "2.2.57",
50
+ "@diia-inhouse/configs": "7.1.0",
51
+ "@diia-inhouse/diia-logger": "4.4.49",
52
+ "@diia-inhouse/env": "3.3.48",
53
+ "@diia-inhouse/errors": "2.2.18",
54
+ "@diia-inhouse/oxc-config": "2.1.3",
55
+ "@diia-inhouse/test": "8.3.5",
56
+ "@diia-inhouse/types": "14.2.6",
57
+ "@diia-inhouse/validators": "2.2.61",
58
58
  "@types/node": "25.6.2",
59
59
  "@vitest/coverage-v8": "4.1.5",
60
60
  "@vitest/ui": "4.1.5",