halt-rate 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.
@@ -1,177 +0,0 @@
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
- }
59
- /**
60
- * Validate and normalize policy configuration.
61
- */
62
- declare function normalizePolicy(policy: Policy): Required<Policy>;
63
-
64
- /**
65
- * In-memory storage backend for rate limiting.
66
- */
67
- interface Store {
68
- get(key: string): any;
69
- set(key: string, value: any, ttl?: number): void;
70
- increment(key: string, delta?: number, ttl?: number): number;
71
- delete(key: string): void;
72
- }
73
- declare class InMemoryStore implements Store {
74
- private data;
75
- get(key: string): any;
76
- set(key: string, value: any, ttl?: number): void;
77
- increment(key: string, delta?: number, ttl?: number): number;
78
- delete(key: string): void;
79
- private cleanupExpired;
80
- /**
81
- * Clean up all expired keys.
82
- */
83
- cleanupAllExpired(): number;
84
- }
85
-
86
- /**
87
- * Atomic-evaluation protocol.
88
- *
89
- * Simple stores (e.g. InMemoryStore) expose get/set and let the limiter run the
90
- * algorithm in-process. Distributed stores that can compute the decision
91
- * atomically (e.g. RedisStore via Lua) instead expose `evaluate()`: the limiter
92
- * hands them the resolved policy params + storage key and gets back a finished
93
- * Decision. The limiter prefers `evaluate()` when present (see core/limiter.ts).
94
- */
95
-
96
- /** Everything an atomic store needs to compute a decision for one request. */
97
- interface EvaluateInput {
98
- /** Full storage key, already namespaced (e.g. `halt:public_api:1.2.3.4`). */
99
- key: string;
100
- /** Algorithm to apply. */
101
- algorithm: Algorithm;
102
- /** Requests permitted per window. */
103
- limit: number;
104
- /** Window length in seconds. */
105
- window: number;
106
- /** Bucket capacity (token/leaky bucket). */
107
- burst: number;
108
- /** Cost this request consumes. */
109
- cost: number;
110
- /** Seconds to keep the key alive in the backing store. */
111
- ttl: number;
112
- }
113
- /** A store that computes the rate-limit decision atomically on its own. */
114
- interface AtomicStore {
115
- evaluate(input: EvaluateInput): Promise<Decision>;
116
- }
117
- /** True if the store implements the atomic-evaluation protocol. */
118
- declare function isAtomicStore(store: unknown): store is AtomicStore;
119
- /**
120
- * Minimal structural type for a Redis client (satisfied by ioredis and
121
- * node-redis v4+). Declared here so Halt has no hard dependency on any concrete
122
- * Redis package — the user injects their own client.
123
- */
124
- interface RedisClientLike {
125
- eval(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
126
- evalsha?(sha: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
127
- script?(subcommand: 'LOAD', script: string): Promise<unknown>;
128
- }
129
-
130
- /**
131
- * Main rate limiter implementation.
132
- */
133
-
134
- interface RateLimiterOptions {
135
- /**
136
- * Storage backend. Either a simple KV store (InMemoryStore — the limiter runs
137
- * the algorithm in-process) or an atomic store (RedisStore — the store
138
- * computes the decision atomically via Lua).
139
- */
140
- store: Store | AtomicStore;
141
- /** Either a static policy or a resolver returning a policy per-request */
142
- policy: Policy | ((request: any) => Policy | Promise<Policy>);
143
- trustedProxies?: string[];
144
- exemptPrivateIps?: boolean;
145
- /** Optional OpenTelemetry-like tracer (object with startSpan) */
146
- otelTracer?: any;
147
- /** Optional metrics recorder: (name, tags?, value?) => void */
148
- metricsRecorder?: (name: string, tags?: Record<string, string>, value?: number) => void;
149
- }
150
- declare class RateLimiter {
151
- private store;
152
- private policyOrResolver;
153
- private trustedProxies;
154
- private exemptPrivateIps;
155
- private algorithmCache;
156
- private otelTracer?;
157
- private metricsRecorder?;
158
- constructor(options: RateLimiterOptions);
159
- /**
160
- * Check if request is allowed under rate limit.
161
- */
162
- /**
163
- * Check if request is allowed under rate limit.
164
- * This method is async because policy resolution may be async (e.g. DB lookup).
165
- */
166
- check(request: any, cost?: number): Promise<Decision>;
167
- /**
168
- * Extract rate limit key from request based on policy strategy.
169
- */
170
- private extractKey;
171
- /**
172
- * Check if request is exempt from rate limiting.
173
- */
174
- private isExempt;
175
- }
176
-
177
- export { type AtomicStore as A, type Decision as D, type EvaluateInput as E, InMemoryStore as I, KeyStrategy as K, type Policy as P, type RedisClientLike as R, type Store as S, Algorithm as a, RateLimiter as b, type RateLimiterOptions as c, isAtomicStore as i, normalizePolicy as n, toHeaders as t };
@@ -1,177 +0,0 @@
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
- }
59
- /**
60
- * Validate and normalize policy configuration.
61
- */
62
- declare function normalizePolicy(policy: Policy): Required<Policy>;
63
-
64
- /**
65
- * In-memory storage backend for rate limiting.
66
- */
67
- interface Store {
68
- get(key: string): any;
69
- set(key: string, value: any, ttl?: number): void;
70
- increment(key: string, delta?: number, ttl?: number): number;
71
- delete(key: string): void;
72
- }
73
- declare class InMemoryStore implements Store {
74
- private data;
75
- get(key: string): any;
76
- set(key: string, value: any, ttl?: number): void;
77
- increment(key: string, delta?: number, ttl?: number): number;
78
- delete(key: string): void;
79
- private cleanupExpired;
80
- /**
81
- * Clean up all expired keys.
82
- */
83
- cleanupAllExpired(): number;
84
- }
85
-
86
- /**
87
- * Atomic-evaluation protocol.
88
- *
89
- * Simple stores (e.g. InMemoryStore) expose get/set and let the limiter run the
90
- * algorithm in-process. Distributed stores that can compute the decision
91
- * atomically (e.g. RedisStore via Lua) instead expose `evaluate()`: the limiter
92
- * hands them the resolved policy params + storage key and gets back a finished
93
- * Decision. The limiter prefers `evaluate()` when present (see core/limiter.ts).
94
- */
95
-
96
- /** Everything an atomic store needs to compute a decision for one request. */
97
- interface EvaluateInput {
98
- /** Full storage key, already namespaced (e.g. `halt:public_api:1.2.3.4`). */
99
- key: string;
100
- /** Algorithm to apply. */
101
- algorithm: Algorithm;
102
- /** Requests permitted per window. */
103
- limit: number;
104
- /** Window length in seconds. */
105
- window: number;
106
- /** Bucket capacity (token/leaky bucket). */
107
- burst: number;
108
- /** Cost this request consumes. */
109
- cost: number;
110
- /** Seconds to keep the key alive in the backing store. */
111
- ttl: number;
112
- }
113
- /** A store that computes the rate-limit decision atomically on its own. */
114
- interface AtomicStore {
115
- evaluate(input: EvaluateInput): Promise<Decision>;
116
- }
117
- /** True if the store implements the atomic-evaluation protocol. */
118
- declare function isAtomicStore(store: unknown): store is AtomicStore;
119
- /**
120
- * Minimal structural type for a Redis client (satisfied by ioredis and
121
- * node-redis v4+). Declared here so Halt has no hard dependency on any concrete
122
- * Redis package — the user injects their own client.
123
- */
124
- interface RedisClientLike {
125
- eval(script: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
126
- evalsha?(sha: string, numKeys: number, ...args: (string | number)[]): Promise<unknown>;
127
- script?(subcommand: 'LOAD', script: string): Promise<unknown>;
128
- }
129
-
130
- /**
131
- * Main rate limiter implementation.
132
- */
133
-
134
- interface RateLimiterOptions {
135
- /**
136
- * Storage backend. Either a simple KV store (InMemoryStore — the limiter runs
137
- * the algorithm in-process) or an atomic store (RedisStore — the store
138
- * computes the decision atomically via Lua).
139
- */
140
- store: Store | AtomicStore;
141
- /** Either a static policy or a resolver returning a policy per-request */
142
- policy: Policy | ((request: any) => Policy | Promise<Policy>);
143
- trustedProxies?: string[];
144
- exemptPrivateIps?: boolean;
145
- /** Optional OpenTelemetry-like tracer (object with startSpan) */
146
- otelTracer?: any;
147
- /** Optional metrics recorder: (name, tags?, value?) => void */
148
- metricsRecorder?: (name: string, tags?: Record<string, string>, value?: number) => void;
149
- }
150
- declare class RateLimiter {
151
- private store;
152
- private policyOrResolver;
153
- private trustedProxies;
154
- private exemptPrivateIps;
155
- private algorithmCache;
156
- private otelTracer?;
157
- private metricsRecorder?;
158
- constructor(options: RateLimiterOptions);
159
- /**
160
- * Check if request is allowed under rate limit.
161
- */
162
- /**
163
- * Check if request is allowed under rate limit.
164
- * This method is async because policy resolution may be async (e.g. DB lookup).
165
- */
166
- check(request: any, cost?: number): Promise<Decision>;
167
- /**
168
- * Extract rate limit key from request based on policy strategy.
169
- */
170
- private extractKey;
171
- /**
172
- * Check if request is exempt from rate limiting.
173
- */
174
- private isExempt;
175
- }
176
-
177
- export { type AtomicStore as A, type Decision as D, type EvaluateInput as E, InMemoryStore as I, KeyStrategy as K, type Policy as P, type RedisClientLike as R, type Store as S, Algorithm as a, RateLimiter as b, type RateLimiterOptions as c, isAtomicStore as i, normalizePolicy as n, toHeaders as t };