@teikyo/rate-limit 0.2.0 → 0.3.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.
package/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type HTTPPlugin } from '@sozai/http-server';
2
+ import type { Context } from 'hono';
2
3
  import { type Store } from 'hono-rate-limiter';
3
4
  export declare const RATE_LIMIT: import("@sozai/http-server").PluginName<"http:rate-limit", void>;
4
5
  export type RateLimitPluginParams = {
@@ -6,6 +7,8 @@ export type RateLimitPluginParams = {
6
7
  windowMs: number;
7
8
  /** Maximum number of requests per client within a window. */
8
9
  limit: number;
10
+ /** Bucket by IP (default), principal attribution, or a custom key with nullish IP fallback. */
11
+ key?: 'ip' | 'principal' | ((c: Context) => string | null | undefined | Promise<string | null | undefined>);
9
12
  /**
10
13
  * Custom store, defaults to an in-memory store. The plugin calls `shutdown()` on it when
11
14
  * the server closes, so do not pass a store shared with or owned by something else.
@@ -13,7 +16,7 @@ export type RateLimitPluginParams = {
13
16
  store?: Store;
14
17
  };
15
18
  /**
16
- * Rate limit every request by resolved client IP (IPv6 grouped by /64), answering `429` with a
19
+ * Rate limit requests by IP, principal attribution, or a custom key, answering `429` with a
17
20
  * `Retry-After` header past the limit. Health routes are never limited.
18
21
  */
19
22
  export declare function rateLimitPlugin(params: RateLimitPluginParams): HTTPPlugin<'http:rate-limit', void, []>;
package/lib/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { pluginName } from '@sozai/http-server';
2
+ import { attributionOf, getPrincipal } from '@teikyo/principal';
2
3
  import { MemoryStore, rateLimiter } from 'hono-rate-limiter';
3
4
  import { clientKey } from './key.js';
4
5
  export const RATE_LIMIT = pluginName()('http:rate-limit');
@@ -8,12 +9,12 @@ function assertPositiveInteger(name, value) {
8
9
  }
9
10
  }
10
11
  /**
11
- * Rate limit every request by resolved client IP (IPv6 grouped by /64), answering `429` with a
12
+ * Rate limit requests by IP, principal attribution, or a custom key, answering `429` with a
12
13
  * `Retry-After` header past the limit. Health routes are never limited.
13
14
  */ export function rateLimitPlugin(params) {
14
15
  assertPositiveInteger('windowMs', params.windowMs);
15
16
  assertPositiveInteger('limit', params.limit);
16
- const { windowMs, limit } = params;
17
+ const { windowMs, limit, key = 'ip' } = params;
17
18
  return {
18
19
  name: RATE_LIMIT,
19
20
  dependsOn: [],
@@ -25,7 +26,16 @@ function assertPositiveInteger(name, value) {
25
26
  standardHeaders: 'draft-7',
26
27
  // IPv6 clients share one bucket per /64. A request with no resolvable client IP falls
27
28
  // into one shared bucket.
28
- keyGenerator: (c)=>clientKey(ctx.clientIP(c)),
29
+ keyGenerator: async (c)=>{
30
+ if (typeof key === 'function') {
31
+ const customKey = await key(c);
32
+ if (customKey != null) return customKey;
33
+ } else if (key === 'principal') {
34
+ const principal = getPrincipal(c);
35
+ if (principal != null) return `p:${attributionOf(principal).onBehalfOf}`;
36
+ }
37
+ return `ip:${clientKey(ctx.clientIP(c))}`;
38
+ },
29
39
  store
30
40
  }));
31
41
  ctx.onClose(()=>store.shutdown?.());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teikyo/rate-limit",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Rate limiting plugin for Sozai HTTP servers",
5
5
  "repository": {
6
6
  "type": "git",
@@ -19,17 +19,18 @@
19
19
  "lib/*"
20
20
  ],
21
21
  "dependencies": {
22
- "@sozai/http-server": "^0.2.0",
22
+ "@sozai/http-server": "^0.2.1",
23
+ "@teikyo/principal": "^0.1.0",
23
24
  "hono-rate-limiter": "^0.5.4"
24
25
  },
25
26
  "devDependencies": {
26
- "@types/node": "^26.6.5",
27
+ "@types/node": "^26.6.6",
27
28
  "del-cli": "^8.0.1",
28
- "hono": "^4.13.13",
29
- "vitest": "5.0.5"
29
+ "hono": "^4.13.17",
30
+ "vitest": "5.1.0"
30
31
  },
31
32
  "peerDependencies": {
32
- "hono": "^4.13.13"
33
+ "hono": "^4.13.17"
33
34
  },
34
35
  "engines": {
35
36
  "node": ">=24"