halt-rate 0.2.0 → 0.4.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 (38) hide show
  1. package/README.md +34 -921
  2. package/dist/adapters/express.d.mts +1 -1
  3. package/dist/adapters/express.d.ts +1 -1
  4. package/dist/adapters/fastify.d.mts +29 -0
  5. package/dist/adapters/fastify.d.ts +29 -0
  6. package/dist/adapters/fastify.js +38 -0
  7. package/dist/adapters/fastify.js.map +1 -0
  8. package/dist/adapters/fastify.mjs +35 -0
  9. package/dist/adapters/fastify.mjs.map +1 -0
  10. package/dist/adapters/graphql.d.mts +22 -0
  11. package/dist/adapters/graphql.d.ts +22 -0
  12. package/dist/adapters/graphql.js +51 -0
  13. package/dist/adapters/graphql.js.map +1 -0
  14. package/dist/adapters/graphql.mjs +49 -0
  15. package/dist/adapters/graphql.mjs.map +1 -0
  16. package/dist/adapters/hono.d.mts +30 -0
  17. package/dist/adapters/hono.d.ts +30 -0
  18. package/dist/adapters/hono.js +56 -0
  19. package/dist/adapters/hono.js.map +1 -0
  20. package/dist/adapters/hono.mjs +54 -0
  21. package/dist/adapters/hono.mjs.map +1 -0
  22. package/dist/adapters/next.d.mts +1 -1
  23. package/dist/adapters/next.d.ts +1 -1
  24. package/dist/adapters/next.js +26 -3
  25. package/dist/adapters/next.js.map +1 -1
  26. package/dist/adapters/next.mjs +26 -3
  27. package/dist/adapters/next.mjs.map +1 -1
  28. package/dist/index.d.mts +174 -3
  29. package/dist/index.d.ts +174 -3
  30. package/dist/index.js +631 -3
  31. package/dist/index.js.map +1 -1
  32. package/dist/index.mjs +614 -4
  33. package/dist/index.mjs.map +1 -1
  34. package/dist/limiter-DqVVE0Kl.d.mts +310 -0
  35. package/dist/limiter-DqVVE0Kl.d.ts +310 -0
  36. package/package.json +80 -13
  37. package/dist/limiter-HCt8ADZ2.d.mts +0 -177
  38. package/dist/limiter-HCt8ADZ2.d.ts +0 -177
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Decision model for rate limiting results.
3
+ */
4
+ interface Decision {
5
+ /** Whether the request is allowed */
6
+ allowed: boolean;
7
+ /** Maximum number of requests allowed in the window */
8
+ limit: number;
9
+ /** Number of requests remaining in the current window */
10
+ remaining: number;
11
+ /** Unix timestamp when the limit resets */
12
+ resetAt: number;
13
+ /** Seconds to wait before retrying (only set when blocked) */
14
+ retryAfter?: number;
15
+ }
16
+ /**
17
+ * Convert decision to standard rate limit headers.
18
+ */
19
+ declare function toHeaders(decision: Decision): Record<string, string>;
20
+
21
+ /**
22
+ * Policy model for rate limiting configuration.
23
+ */
24
+ declare enum KeyStrategy {
25
+ IP = "ip",
26
+ USER = "user",
27
+ API_KEY = "api_key",
28
+ COMPOSITE = "composite",
29
+ CUSTOM = "custom"
30
+ }
31
+ declare enum Algorithm {
32
+ TOKEN_BUCKET = "token_bucket",
33
+ FIXED_WINDOW = "fixed_window",
34
+ SLIDING_WINDOW = "sliding_window",
35
+ LEAKY_BUCKET = "leaky_bucket"
36
+ }
37
+ interface Policy {
38
+ /** Human-readable policy name */
39
+ name: string;
40
+ /** Maximum number of requests allowed */
41
+ limit: number;
42
+ /** Time window in seconds */
43
+ window: number;
44
+ /** Rate limiting algorithm to use */
45
+ algorithm?: Algorithm;
46
+ /** Strategy for extracting the rate limit key */
47
+ keyStrategy?: KeyStrategy;
48
+ /** Maximum burst size (for token bucket) */
49
+ burst?: number;
50
+ /** Cost per request (default: 1) */
51
+ cost?: number;
52
+ /** Duration to block after limit exceeded (seconds) */
53
+ blockDuration?: number;
54
+ /** Custom function to extract key from request */
55
+ keyExtractor?: (request: any) => string | null;
56
+ /** List of paths or IPs to exempt from rate limiting */
57
+ exemptions?: string[];
58
+ /** Optional plan/tier label for observability tagging (e.g. "pro"). */
59
+ plan?: string;
60
+ }
61
+ /**
62
+ * Validate and normalize policy configuration.
63
+ */
64
+ declare function normalizePolicy(policy: Policy): Required<Policy>;
65
+
66
+ /**
67
+ * In-memory storage backend for rate limiting.
68
+ */
69
+ interface Store {
70
+ get(key: string): any;
71
+ set(key: string, value: any, ttl?: number): void;
72
+ increment(key: string, delta?: number, ttl?: number): number;
73
+ delete(key: string): void;
74
+ }
75
+ declare class InMemoryStore implements Store {
76
+ private data;
77
+ get(key: string): any;
78
+ set(key: string, value: any, ttl?: number): void;
79
+ increment(key: string, delta?: number, ttl?: number): number;
80
+ delete(key: string): void;
81
+ private cleanupExpired;
82
+ /**
83
+ * Clean up all expired keys.
84
+ */
85
+ cleanupAllExpired(): number;
86
+ }
87
+
88
+ /**
89
+ * Atomic-evaluation protocol.
90
+ *
91
+ * Simple stores (e.g. InMemoryStore) expose get/set and let the limiter run the
92
+ * algorithm in-process. Distributed stores that can compute the decision
93
+ * atomically (e.g. RedisStore via Lua) instead expose `evaluate()`: the limiter
94
+ * hands them the resolved policy params + storage key and gets back a finished
95
+ * Decision. The limiter prefers `evaluate()` when present (see core/limiter.ts).
96
+ */
97
+
98
+ /** Everything an atomic store needs to compute a decision for one request. */
99
+ interface EvaluateInput {
100
+ /** Full storage key, already namespaced (e.g. `halt:public_api:1.2.3.4`). */
101
+ key: string;
102
+ /** Algorithm to apply. */
103
+ algorithm: Algorithm;
104
+ /** Requests permitted per window. */
105
+ limit: number;
106
+ /** Window length in seconds. */
107
+ window: number;
108
+ /** Bucket capacity (token/leaky bucket). */
109
+ burst: number;
110
+ /** Cost this request consumes. */
111
+ cost: number;
112
+ /** Seconds to keep the key alive in the backing store. */
113
+ ttl: number;
114
+ }
115
+ /** A store that computes the rate-limit decision atomically on its own. */
116
+ interface AtomicStore {
117
+ evaluate(input: EvaluateInput): Promise<Decision>;
118
+ }
119
+ /** True if the store implements the atomic-evaluation protocol. */
120
+ declare function isAtomicStore(store: unknown): store is AtomicStore;
121
+ /**
122
+ * Minimal structural type for a Redis client (satisfied by ioredis and
123
+ * node-redis v4+). Declared here so Halt has no hard dependency on any concrete
124
+ * Redis package — the user injects their own client.
125
+ */
126
+ interface RedisClientLike {
127
+ eval(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
128
+ evalsha?(sha: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
129
+ script?(subcommand: 'LOAD', script: string): Promise<unknown>;
130
+ }
131
+
132
+ /**
133
+ * Quota management for SaaS platforms.
134
+ */
135
+
136
+ declare enum QuotaPeriod {
137
+ HOURLY = "hourly",
138
+ DAILY = "daily",
139
+ MONTHLY = "monthly",
140
+ YEARLY = "yearly"
141
+ }
142
+ interface Quota {
143
+ name: string;
144
+ limit: number;
145
+ period: QuotaPeriod;
146
+ currentUsage?: number;
147
+ resetAt?: number;
148
+ }
149
+ declare class QuotaManager {
150
+ private store;
151
+ private telemetry?;
152
+ constructor(store: any, telemetry?: TelemetryHooks | undefined);
153
+ private getQuotaKey;
154
+ private calculateResetTime;
155
+ getQuota(identifier: string, quota: Quota): Promise<Quota>;
156
+ checkQuota(identifier: string, quota: Quota, cost?: number): Promise<{
157
+ allowed: boolean;
158
+ quota: Quota;
159
+ }>;
160
+ consumeQuota(identifier: string, quota: Quota, cost?: number): Promise<Quota>;
161
+ resetQuota(identifier: string, quota: Quota): Promise<void>;
162
+ remaining(quota: Quota): number;
163
+ isExceeded(quota: Quota): boolean;
164
+ }
165
+ declare const QUOTA_FREE_MONTHLY: Quota;
166
+ declare const QUOTA_PRO_MONTHLY: Quota;
167
+ declare const QUOTA_ENTERPRISE_MONTHLY: Quota;
168
+ declare const QUOTA_FREE_DAILY: Quota;
169
+ declare const QUOTA_PRO_DAILY: Quota;
170
+
171
+ /**
172
+ * Penalty system for abuse detection and progressive rate limiting.
173
+ */
174
+
175
+ interface PenaltyConfig {
176
+ threshold: number;
177
+ duration: number;
178
+ multiplier: number;
179
+ decayRate: number;
180
+ }
181
+ interface Penalty {
182
+ abuseScore: number;
183
+ penaltyUntil: number | null;
184
+ violations: number;
185
+ lastViolation: number | null;
186
+ }
187
+ declare class PenaltyManager {
188
+ private store;
189
+ private telemetry?;
190
+ private config;
191
+ constructor(store: any, config?: Partial<PenaltyConfig>, telemetry?: TelemetryHooks | undefined);
192
+ private getPenaltyKey;
193
+ getPenalty(identifier: string): Promise<Penalty>;
194
+ recordViolation(identifier: string, severity?: number): Promise<Penalty>;
195
+ applyPenalty(identifier: string, duration?: number): Promise<Penalty>;
196
+ clearPenalty(identifier: string): Promise<void>;
197
+ getRateLimitMultiplier(penalty: Penalty): number;
198
+ isActive(penalty: Penalty): boolean;
199
+ timeRemaining(penalty: Penalty): number;
200
+ private savePenalty;
201
+ }
202
+ declare const PENALTY_LENIENT: PenaltyConfig;
203
+ declare const PENALTY_MODERATE: PenaltyConfig;
204
+ declare const PENALTY_STRICT: PenaltyConfig;
205
+
206
+ /**
207
+ * Telemetry hooks for observability and monitoring.
208
+ */
209
+
210
+ interface TelemetryHooks {
211
+ onCheck?(key: string, decision: Decision, metadata?: Record<string, any>): void;
212
+ onAllowed?(key: string, decision: Decision, metadata?: Record<string, any>): void;
213
+ onBlocked?(key: string, decision: Decision, metadata?: Record<string, any>): void;
214
+ onQuotaCheck?(identifier: string, quota: Quota, allowed: boolean): void;
215
+ onQuotaExceeded?(identifier: string, quota: Quota): void;
216
+ onPenaltyApplied?(identifier: string, penalty: Penalty): void;
217
+ onViolation?(identifier: string, penalty: Penalty, severity: number): void;
218
+ }
219
+ declare class LoggingTelemetry implements TelemetryHooks {
220
+ private logger;
221
+ constructor(logger?: Console);
222
+ onCheck(key: string, decision: Decision, metadata?: Record<string, any>): void;
223
+ onAllowed(key: string, decision: Decision, _metadata?: Record<string, any>): void;
224
+ onBlocked(key: string, decision: Decision, metadata?: Record<string, any>): void;
225
+ onQuotaCheck(identifier: string, quota: Quota, allowed: boolean): void;
226
+ onQuotaExceeded(identifier: string, quota: Quota): void;
227
+ onPenaltyApplied(identifier: string, penalty: Penalty): void;
228
+ onViolation(identifier: string, penalty: Penalty, severity: number): void;
229
+ }
230
+ declare class MetricsTelemetry implements TelemetryHooks {
231
+ private metricsClient;
232
+ constructor(metricsClient: any);
233
+ onCheck(_key: string, _decision: Decision, metadata?: Record<string, any>): void;
234
+ onAllowed(_key: string, decision: Decision, metadata?: Record<string, any>): void;
235
+ onBlocked(_key: string, _decision: Decision, metadata?: Record<string, any>): void;
236
+ onQuotaCheck(_identifier: string, quota: Quota, _allowed: boolean): void;
237
+ onQuotaExceeded(_identifier: string, quota: Quota): void;
238
+ onPenaltyApplied(_identifier: string, penalty: Penalty): void;
239
+ onViolation(_identifier: string, _penalty: Penalty, severity: number): void;
240
+ private getTags;
241
+ }
242
+ declare class CompositeTelemetry implements TelemetryHooks {
243
+ private hooks;
244
+ constructor(hooks: TelemetryHooks[]);
245
+ onCheck(key: string, decision: Decision, metadata?: Record<string, any>): void;
246
+ onAllowed(key: string, decision: Decision, metadata?: Record<string, any>): void;
247
+ onBlocked(key: string, decision: Decision, metadata?: Record<string, any>): void;
248
+ onQuotaCheck(identifier: string, quota: Quota, allowed: boolean): void;
249
+ onQuotaExceeded(identifier: string, quota: Quota): void;
250
+ onPenaltyApplied(identifier: string, penalty: Penalty): void;
251
+ onViolation(identifier: string, penalty: Penalty, severity: number): void;
252
+ }
253
+
254
+ /**
255
+ * Main rate limiter implementation.
256
+ */
257
+
258
+ interface RateLimiterOptions {
259
+ /**
260
+ * Storage backend. Either a simple KV store (InMemoryStore — the limiter runs
261
+ * the algorithm in-process) or an atomic store (RedisStore — the store
262
+ * computes the decision atomically via Lua).
263
+ */
264
+ store: Store | AtomicStore;
265
+ /** Either a static policy or a resolver returning a policy per-request */
266
+ policy: Policy | ((request: any) => Policy | Promise<Policy>);
267
+ trustedProxies?: string[];
268
+ exemptPrivateIps?: boolean;
269
+ /** Optional OpenTelemetry-like tracer (object with startSpan) */
270
+ otelTracer?: any;
271
+ /** Optional metrics recorder: (name, tags?, value?) => void */
272
+ metricsRecorder?: (name: string, tags?: Record<string, string>, value?: number) => void;
273
+ /**
274
+ * Optional high-level observability hooks (StatsCollector, OpenTelemetryMetrics,
275
+ * LoggingTelemetry, or a CompositeTelemetry of several). Called on every
276
+ * rate-limited check with rich metadata (policy, algorithm, endpoint, cost, plan).
277
+ */
278
+ telemetry?: TelemetryHooks;
279
+ }
280
+ declare class RateLimiter {
281
+ private store;
282
+ private policyOrResolver;
283
+ private trustedProxies;
284
+ private exemptPrivateIps;
285
+ private algorithmCache;
286
+ private otelTracer?;
287
+ private metricsRecorder?;
288
+ private telemetry?;
289
+ constructor(options: RateLimiterOptions);
290
+ /** Fan a finished decision out to the telemetry hooks with rich metadata. */
291
+ private emitTelemetry;
292
+ /**
293
+ * Check if request is allowed under rate limit.
294
+ */
295
+ /**
296
+ * Check if request is allowed under rate limit.
297
+ * This method is async because policy resolution may be async (e.g. DB lookup).
298
+ */
299
+ check(request: any, cost?: number): Promise<Decision>;
300
+ /**
301
+ * Extract rate limit key from request based on policy strategy.
302
+ */
303
+ private extractKey;
304
+ /**
305
+ * Check if request is exempt from rate limiting.
306
+ */
307
+ private isExempt;
308
+ }
309
+
310
+ export { type AtomicStore as A, CompositeTelemetry as C, type Decision as D, type EvaluateInput as E, InMemoryStore as I, KeyStrategy as K, LoggingTelemetry as L, MetricsTelemetry as M, type Policy as P, type Quota as Q, type RedisClientLike as R, type Store as S, type TelemetryHooks as T, type Penalty as a, Algorithm as b, PENALTY_LENIENT as c, PENALTY_MODERATE as d, PENALTY_STRICT as e, type PenaltyConfig as f, PenaltyManager as g, QUOTA_ENTERPRISE_MONTHLY as h, QUOTA_FREE_DAILY as i, QUOTA_FREE_MONTHLY as j, QUOTA_PRO_DAILY as k, QUOTA_PRO_MONTHLY as l, QuotaManager as m, QuotaPeriod as n, RateLimiter as o, type RateLimiterOptions as p, isAtomicStore as q, normalizePolicy as r, toHeaders as t };
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Decision model for rate limiting results.
3
+ */
4
+ interface Decision {
5
+ /** Whether the request is allowed */
6
+ allowed: boolean;
7
+ /** Maximum number of requests allowed in the window */
8
+ limit: number;
9
+ /** Number of requests remaining in the current window */
10
+ remaining: number;
11
+ /** Unix timestamp when the limit resets */
12
+ resetAt: number;
13
+ /** Seconds to wait before retrying (only set when blocked) */
14
+ retryAfter?: number;
15
+ }
16
+ /**
17
+ * Convert decision to standard rate limit headers.
18
+ */
19
+ declare function toHeaders(decision: Decision): Record<string, string>;
20
+
21
+ /**
22
+ * Policy model for rate limiting configuration.
23
+ */
24
+ declare enum KeyStrategy {
25
+ IP = "ip",
26
+ USER = "user",
27
+ API_KEY = "api_key",
28
+ COMPOSITE = "composite",
29
+ CUSTOM = "custom"
30
+ }
31
+ declare enum Algorithm {
32
+ TOKEN_BUCKET = "token_bucket",
33
+ FIXED_WINDOW = "fixed_window",
34
+ SLIDING_WINDOW = "sliding_window",
35
+ LEAKY_BUCKET = "leaky_bucket"
36
+ }
37
+ interface Policy {
38
+ /** Human-readable policy name */
39
+ name: string;
40
+ /** Maximum number of requests allowed */
41
+ limit: number;
42
+ /** Time window in seconds */
43
+ window: number;
44
+ /** Rate limiting algorithm to use */
45
+ algorithm?: Algorithm;
46
+ /** Strategy for extracting the rate limit key */
47
+ keyStrategy?: KeyStrategy;
48
+ /** Maximum burst size (for token bucket) */
49
+ burst?: number;
50
+ /** Cost per request (default: 1) */
51
+ cost?: number;
52
+ /** Duration to block after limit exceeded (seconds) */
53
+ blockDuration?: number;
54
+ /** Custom function to extract key from request */
55
+ keyExtractor?: (request: any) => string | null;
56
+ /** List of paths or IPs to exempt from rate limiting */
57
+ exemptions?: string[];
58
+ /** Optional plan/tier label for observability tagging (e.g. "pro"). */
59
+ plan?: string;
60
+ }
61
+ /**
62
+ * Validate and normalize policy configuration.
63
+ */
64
+ declare function normalizePolicy(policy: Policy): Required<Policy>;
65
+
66
+ /**
67
+ * In-memory storage backend for rate limiting.
68
+ */
69
+ interface Store {
70
+ get(key: string): any;
71
+ set(key: string, value: any, ttl?: number): void;
72
+ increment(key: string, delta?: number, ttl?: number): number;
73
+ delete(key: string): void;
74
+ }
75
+ declare class InMemoryStore implements Store {
76
+ private data;
77
+ get(key: string): any;
78
+ set(key: string, value: any, ttl?: number): void;
79
+ increment(key: string, delta?: number, ttl?: number): number;
80
+ delete(key: string): void;
81
+ private cleanupExpired;
82
+ /**
83
+ * Clean up all expired keys.
84
+ */
85
+ cleanupAllExpired(): number;
86
+ }
87
+
88
+ /**
89
+ * Atomic-evaluation protocol.
90
+ *
91
+ * Simple stores (e.g. InMemoryStore) expose get/set and let the limiter run the
92
+ * algorithm in-process. Distributed stores that can compute the decision
93
+ * atomically (e.g. RedisStore via Lua) instead expose `evaluate()`: the limiter
94
+ * hands them the resolved policy params + storage key and gets back a finished
95
+ * Decision. The limiter prefers `evaluate()` when present (see core/limiter.ts).
96
+ */
97
+
98
+ /** Everything an atomic store needs to compute a decision for one request. */
99
+ interface EvaluateInput {
100
+ /** Full storage key, already namespaced (e.g. `halt:public_api:1.2.3.4`). */
101
+ key: string;
102
+ /** Algorithm to apply. */
103
+ algorithm: Algorithm;
104
+ /** Requests permitted per window. */
105
+ limit: number;
106
+ /** Window length in seconds. */
107
+ window: number;
108
+ /** Bucket capacity (token/leaky bucket). */
109
+ burst: number;
110
+ /** Cost this request consumes. */
111
+ cost: number;
112
+ /** Seconds to keep the key alive in the backing store. */
113
+ ttl: number;
114
+ }
115
+ /** A store that computes the rate-limit decision atomically on its own. */
116
+ interface AtomicStore {
117
+ evaluate(input: EvaluateInput): Promise<Decision>;
118
+ }
119
+ /** True if the store implements the atomic-evaluation protocol. */
120
+ declare function isAtomicStore(store: unknown): store is AtomicStore;
121
+ /**
122
+ * Minimal structural type for a Redis client (satisfied by ioredis and
123
+ * node-redis v4+). Declared here so Halt has no hard dependency on any concrete
124
+ * Redis package — the user injects their own client.
125
+ */
126
+ interface RedisClientLike {
127
+ eval(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
128
+ evalsha?(sha: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
129
+ script?(subcommand: 'LOAD', script: string): Promise<unknown>;
130
+ }
131
+
132
+ /**
133
+ * Quota management for SaaS platforms.
134
+ */
135
+
136
+ declare enum QuotaPeriod {
137
+ HOURLY = "hourly",
138
+ DAILY = "daily",
139
+ MONTHLY = "monthly",
140
+ YEARLY = "yearly"
141
+ }
142
+ interface Quota {
143
+ name: string;
144
+ limit: number;
145
+ period: QuotaPeriod;
146
+ currentUsage?: number;
147
+ resetAt?: number;
148
+ }
149
+ declare class QuotaManager {
150
+ private store;
151
+ private telemetry?;
152
+ constructor(store: any, telemetry?: TelemetryHooks | undefined);
153
+ private getQuotaKey;
154
+ private calculateResetTime;
155
+ getQuota(identifier: string, quota: Quota): Promise<Quota>;
156
+ checkQuota(identifier: string, quota: Quota, cost?: number): Promise<{
157
+ allowed: boolean;
158
+ quota: Quota;
159
+ }>;
160
+ consumeQuota(identifier: string, quota: Quota, cost?: number): Promise<Quota>;
161
+ resetQuota(identifier: string, quota: Quota): Promise<void>;
162
+ remaining(quota: Quota): number;
163
+ isExceeded(quota: Quota): boolean;
164
+ }
165
+ declare const QUOTA_FREE_MONTHLY: Quota;
166
+ declare const QUOTA_PRO_MONTHLY: Quota;
167
+ declare const QUOTA_ENTERPRISE_MONTHLY: Quota;
168
+ declare const QUOTA_FREE_DAILY: Quota;
169
+ declare const QUOTA_PRO_DAILY: Quota;
170
+
171
+ /**
172
+ * Penalty system for abuse detection and progressive rate limiting.
173
+ */
174
+
175
+ interface PenaltyConfig {
176
+ threshold: number;
177
+ duration: number;
178
+ multiplier: number;
179
+ decayRate: number;
180
+ }
181
+ interface Penalty {
182
+ abuseScore: number;
183
+ penaltyUntil: number | null;
184
+ violations: number;
185
+ lastViolation: number | null;
186
+ }
187
+ declare class PenaltyManager {
188
+ private store;
189
+ private telemetry?;
190
+ private config;
191
+ constructor(store: any, config?: Partial<PenaltyConfig>, telemetry?: TelemetryHooks | undefined);
192
+ private getPenaltyKey;
193
+ getPenalty(identifier: string): Promise<Penalty>;
194
+ recordViolation(identifier: string, severity?: number): Promise<Penalty>;
195
+ applyPenalty(identifier: string, duration?: number): Promise<Penalty>;
196
+ clearPenalty(identifier: string): Promise<void>;
197
+ getRateLimitMultiplier(penalty: Penalty): number;
198
+ isActive(penalty: Penalty): boolean;
199
+ timeRemaining(penalty: Penalty): number;
200
+ private savePenalty;
201
+ }
202
+ declare const PENALTY_LENIENT: PenaltyConfig;
203
+ declare const PENALTY_MODERATE: PenaltyConfig;
204
+ declare const PENALTY_STRICT: PenaltyConfig;
205
+
206
+ /**
207
+ * Telemetry hooks for observability and monitoring.
208
+ */
209
+
210
+ interface TelemetryHooks {
211
+ onCheck?(key: string, decision: Decision, metadata?: Record<string, any>): void;
212
+ onAllowed?(key: string, decision: Decision, metadata?: Record<string, any>): void;
213
+ onBlocked?(key: string, decision: Decision, metadata?: Record<string, any>): void;
214
+ onQuotaCheck?(identifier: string, quota: Quota, allowed: boolean): void;
215
+ onQuotaExceeded?(identifier: string, quota: Quota): void;
216
+ onPenaltyApplied?(identifier: string, penalty: Penalty): void;
217
+ onViolation?(identifier: string, penalty: Penalty, severity: number): void;
218
+ }
219
+ declare class LoggingTelemetry implements TelemetryHooks {
220
+ private logger;
221
+ constructor(logger?: Console);
222
+ onCheck(key: string, decision: Decision, metadata?: Record<string, any>): void;
223
+ onAllowed(key: string, decision: Decision, _metadata?: Record<string, any>): void;
224
+ onBlocked(key: string, decision: Decision, metadata?: Record<string, any>): void;
225
+ onQuotaCheck(identifier: string, quota: Quota, allowed: boolean): void;
226
+ onQuotaExceeded(identifier: string, quota: Quota): void;
227
+ onPenaltyApplied(identifier: string, penalty: Penalty): void;
228
+ onViolation(identifier: string, penalty: Penalty, severity: number): void;
229
+ }
230
+ declare class MetricsTelemetry implements TelemetryHooks {
231
+ private metricsClient;
232
+ constructor(metricsClient: any);
233
+ onCheck(_key: string, _decision: Decision, metadata?: Record<string, any>): void;
234
+ onAllowed(_key: string, decision: Decision, metadata?: Record<string, any>): void;
235
+ onBlocked(_key: string, _decision: Decision, metadata?: Record<string, any>): void;
236
+ onQuotaCheck(_identifier: string, quota: Quota, _allowed: boolean): void;
237
+ onQuotaExceeded(_identifier: string, quota: Quota): void;
238
+ onPenaltyApplied(_identifier: string, penalty: Penalty): void;
239
+ onViolation(_identifier: string, _penalty: Penalty, severity: number): void;
240
+ private getTags;
241
+ }
242
+ declare class CompositeTelemetry implements TelemetryHooks {
243
+ private hooks;
244
+ constructor(hooks: TelemetryHooks[]);
245
+ onCheck(key: string, decision: Decision, metadata?: Record<string, any>): void;
246
+ onAllowed(key: string, decision: Decision, metadata?: Record<string, any>): void;
247
+ onBlocked(key: string, decision: Decision, metadata?: Record<string, any>): void;
248
+ onQuotaCheck(identifier: string, quota: Quota, allowed: boolean): void;
249
+ onQuotaExceeded(identifier: string, quota: Quota): void;
250
+ onPenaltyApplied(identifier: string, penalty: Penalty): void;
251
+ onViolation(identifier: string, penalty: Penalty, severity: number): void;
252
+ }
253
+
254
+ /**
255
+ * Main rate limiter implementation.
256
+ */
257
+
258
+ interface RateLimiterOptions {
259
+ /**
260
+ * Storage backend. Either a simple KV store (InMemoryStore — the limiter runs
261
+ * the algorithm in-process) or an atomic store (RedisStore — the store
262
+ * computes the decision atomically via Lua).
263
+ */
264
+ store: Store | AtomicStore;
265
+ /** Either a static policy or a resolver returning a policy per-request */
266
+ policy: Policy | ((request: any) => Policy | Promise<Policy>);
267
+ trustedProxies?: string[];
268
+ exemptPrivateIps?: boolean;
269
+ /** Optional OpenTelemetry-like tracer (object with startSpan) */
270
+ otelTracer?: any;
271
+ /** Optional metrics recorder: (name, tags?, value?) => void */
272
+ metricsRecorder?: (name: string, tags?: Record<string, string>, value?: number) => void;
273
+ /**
274
+ * Optional high-level observability hooks (StatsCollector, OpenTelemetryMetrics,
275
+ * LoggingTelemetry, or a CompositeTelemetry of several). Called on every
276
+ * rate-limited check with rich metadata (policy, algorithm, endpoint, cost, plan).
277
+ */
278
+ telemetry?: TelemetryHooks;
279
+ }
280
+ declare class RateLimiter {
281
+ private store;
282
+ private policyOrResolver;
283
+ private trustedProxies;
284
+ private exemptPrivateIps;
285
+ private algorithmCache;
286
+ private otelTracer?;
287
+ private metricsRecorder?;
288
+ private telemetry?;
289
+ constructor(options: RateLimiterOptions);
290
+ /** Fan a finished decision out to the telemetry hooks with rich metadata. */
291
+ private emitTelemetry;
292
+ /**
293
+ * Check if request is allowed under rate limit.
294
+ */
295
+ /**
296
+ * Check if request is allowed under rate limit.
297
+ * This method is async because policy resolution may be async (e.g. DB lookup).
298
+ */
299
+ check(request: any, cost?: number): Promise<Decision>;
300
+ /**
301
+ * Extract rate limit key from request based on policy strategy.
302
+ */
303
+ private extractKey;
304
+ /**
305
+ * Check if request is exempt from rate limiting.
306
+ */
307
+ private isExempt;
308
+ }
309
+
310
+ export { type AtomicStore as A, CompositeTelemetry as C, type Decision as D, type EvaluateInput as E, InMemoryStore as I, KeyStrategy as K, LoggingTelemetry as L, MetricsTelemetry as M, type Policy as P, type Quota as Q, type RedisClientLike as R, type Store as S, type TelemetryHooks as T, type Penalty as a, Algorithm as b, PENALTY_LENIENT as c, PENALTY_MODERATE as d, PENALTY_STRICT as e, type PenaltyConfig as f, PenaltyManager as g, QUOTA_ENTERPRISE_MONTHLY as h, QUOTA_FREE_DAILY as i, QUOTA_FREE_MONTHLY as j, QUOTA_PRO_DAILY as k, QUOTA_PRO_MONTHLY as l, QuotaManager as m, QuotaPeriod as n, RateLimiter as o, type RateLimiterOptions as p, isAtomicStore as q, normalizePolicy as r, toHeaders as t };