@limitkit/core 0.1.5 β†’ 0.1.7

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/README.md CHANGED
@@ -1,117 +1,157 @@
1
- # @limitkit/core
1
+ # πŸ“¦ `@limitkit/core`
2
2
 
3
- Core rate limiting engine for **LimitKit**.
3
+ [![npm version](https://img.shields.io/npm/v/@limitkit/core)](https://www.npmjs.com/package/@limitkit/core)
4
+ [![downloads](https://img.shields.io/npm/dw/@limitkit/core)](https://www.npmjs.com/package/@limitkit/core)
5
+ [![license](https://img.shields.io/npm/l/@limitkit/core)](https://github.com/alphatrann/limitkit/blob/main/LICENSE)
4
6
 
5
- Provides the `RateLimiter`, rule system, and algorithm abstractions used by all LimitKit integrations.
6
-
7
- πŸ‘‰ Main project: https://github.com/alphatrann/limitkit
7
+ **A policy-driven rate limiting engine built on composable rules.**
8
8
 
9
9
  ---
10
10
 
11
- ## Installation
11
+ # πŸ”Œ Works With
12
+
13
+ The core engine is designed to integrate with:
14
+
15
+ * `@limitkit/memory` β†’ in-memory store
16
+ * `@limitkit/redis` β†’ distributed rate limiting
17
+ * `@limitkit/express` β†’ middleware
18
+ * `@limitkit/nest` β†’ guard + decorators
12
19
 
13
- ```bash
14
- npm install @limitkit/core
15
- ````
16
20
 
17
21
  ---
18
22
 
19
- ## Basic Usage
23
+ # ⚑ Quick Start
24
+
25
+ ```bash
26
+ npm install @limitkit/core
27
+ ```
20
28
 
21
29
  ```ts
22
- import { RateLimiter } from "@limitkit/core"
23
- import { InMemoryStore, InMemoryFixedWindow } from "@limitkit/memory"
30
+ import { RateLimiter } from "@limitkit/core";
24
31
 
25
32
  const limiter = new RateLimiter({
26
- store: new InMemoryStore(),
33
+ store,
27
34
  rules: [
28
35
  {
29
36
  name: "global",
30
- key: (req) => req.ip,
31
- policy: new InMemoryFixedWindow({
32
- name: "fixed-window",
33
- window: 60,
34
- limit: 100
35
- })
36
- }
37
- ]
38
- })
37
+ key: "global",
38
+ policy: ...,
39
+ },
40
+ ],
41
+ });
42
+
43
+ const result = await limiter.consume(ctx);
44
+
45
+ if (!result.allowed) {
46
+ console.log("Rate limited");
47
+ }
39
48
  ```
40
49
 
41
50
  ---
42
51
 
43
- ## Supported Algorithms
52
+ # 🧠 Core Idea
44
53
 
45
- LimitKit supports multiple rate limiting algorithms:
54
+ Most rate limiters answer:
46
55
 
47
- * Fixed Window
48
- * Sliding Window
49
- * Sliding Window Counter
50
- * Token Bucket
51
- * Leaky Bucket
52
- * GCRA
56
+ > β€œHow many requests per IP?”
53
57
 
54
- Algorithms are provided by store-specific packages such as `@limitkit/memory` or `@limitkit/redis`.
58
+ LimitKit answers:
55
59
 
56
- ---
60
+ > **β€œWhat rules should control this request?”**
57
61
 
58
- ## Rules
62
+ ```ts
63
+ global β†’ ip β†’ user β†’ endpoint
64
+ ```
65
+
66
+ Rules run top β†’ bottom and stop on first failure.
67
+
68
+ ---
59
69
 
60
- Each rule defines how a request is evaluated.
70
+ # 🧩 Concepts
61
71
 
62
- Example rule:
72
+ A **rule** defines *who*, *how*, and *how much*:
63
73
 
64
74
  ```ts
65
75
  {
66
- name: "per-ip",
67
- key: (req) => req.ip,
68
- policy: new InMemoryFixedWindow({
69
- name: "fixed-window",
70
- window: 60,
71
- limit: 100
72
- })
76
+ name: "user",
77
+ key: (ctx) => ctx.user.id,
78
+ policy: new TokenBucket(...),
79
+ cost: 1
73
80
  }
74
81
  ```
75
82
 
76
- Rules can define:
83
+ * **key** β†’ groups requests (string, function, or async)
84
+ * **policy** β†’ rate limiting algorithm (fixed, sliding, token bucket)
85
+ * **cost** β†’ weight per request (default: 1)
77
86
 
78
- * `name` β€” unique rule identifier
79
- * `key` β€” request key (e.g. IP or user ID)
80
- * `policy` β€” rate limit algorithm
87
+ Policies can also be dynamic:
88
+
89
+ ```ts
90
+ policy: (ctx) => {
91
+ return ctx.user.plan === "pro" ? proPolicy : freePolicy;
92
+ }
93
+ ```
81
94
 
82
95
  ---
83
96
 
84
- ## Custom Algorithms
97
+ # 🎯 Examples
85
98
 
86
- You can create custom algorithms by implementing the `Algorithm` interface.
99
+ ## Layered Limits
87
100
 
88
101
  ```ts
89
- import { Algorithm } from "@limitkit/core"
102
+ rules: [
103
+ { name: "global", key: "global", policy: ... },
104
+ { name: "ip", key: (ctx) => ctx.ip, policy: ... },
105
+ { name: "user", key: (ctx) => ctx.user.id, policy: ... },
106
+ ]
107
+ ```
90
108
 
91
- class MyAlgorithm implements Algorithm<MyConfig> {
109
+ ## SaaS Plans
92
110
 
93
- constructor(public readonly config: MyConfig) {}
111
+ ```ts
112
+ {
113
+ key: (ctx) => ctx.user.id,
114
+ policy: (ctx) => {
115
+ return ctx.user.plan === "pro" ? proPolicy : freePolicy;
116
+ },
117
+ }
118
+ ```
94
119
 
95
- validate(): void {
96
- if (this.config.limit <= 0) {
97
- throw new Error("Invalid configuration")
98
- }
99
- }
120
+ ## Expensive Operations
100
121
 
122
+ ```ts
123
+ {
124
+ key: (ctx) => ctx.user.id,
125
+ cost: (ctx) => ctx.endpoint === "/report" ? 10 : 1,
126
+ policy: tokenBucket,
101
127
  }
102
128
  ```
103
129
 
104
130
  ---
105
131
 
106
- ## Custom Stores
132
+ # πŸ“Š Result
133
+
134
+ ```ts
135
+ {
136
+ allowed: boolean,
137
+ limit: number,
138
+ remaining: number,
139
+ reset: number,
140
+ retryAfter?: number
141
+ }
142
+ ```
107
143
 
108
- Stores control where rate limiting state is stored.
144
+ * **allowed** β†’ request permitted or blocked
145
+ * **limit** β†’ max allowed requests
146
+ * **remaining** β†’ remaining quota
147
+ * **reset** β†’ timestamp (ms) when fully reset
148
+ * **retryAfter** β†’ seconds to wait (if blocked)
109
149
 
110
- Custom stores can implement the `Store` interface.
150
+ ---
111
151
 
112
- Example use cases:
152
+ # 🏁 Summary
113
153
 
114
- * DynamoDB
115
- * PostgreSQL
116
- * MongoDB
117
- * Cloudflare KV
154
+ * Rule-based rate limiting
155
+ * Dynamic, context-aware policies
156
+ * Weighted requests
157
+ * Early exit for performance
package/dist/index.js CHANGED
@@ -154,7 +154,7 @@ var RateLimiter = class {
154
154
  const algorithm = typeof rule.policy === "function" ? await rule.policy(ctx) : rule.policy;
155
155
  const key = typeof rule.key === "function" ? await rule.key(ctx) : rule.key;
156
156
  const cost = typeof rule.cost === "function" ? await rule.cost(ctx) : rule.cost;
157
- if (cost && cost <= 0)
157
+ if (cost !== void 0 && cost <= 0)
158
158
  throw new BadArgumentsException(
159
159
  `Cost must be a positive integer, got cost=${cost}`
160
160
  );
@@ -173,7 +173,7 @@ var RateLimiter = class {
173
173
  if (result.allowed) console.log(debugRules);
174
174
  else console.error(debugRules);
175
175
  }
176
- if (result.remaining === 0) {
176
+ if (!result.allowed) {
177
177
  if (this.debug) {
178
178
  const debugResults = {
179
179
  failedRule: rule.name,
@@ -182,12 +182,7 @@ var RateLimiter = class {
182
182
  };
183
183
  return debugResults;
184
184
  }
185
- return {
186
- ...result,
187
- reset: maxReset,
188
- limit: minLimit,
189
- remaining: minRemaining
190
- };
185
+ return result;
191
186
  }
192
187
  }
193
188
  if (this.debug) {
package/dist/index.mjs CHANGED
@@ -117,7 +117,7 @@ var RateLimiter = class {
117
117
  const algorithm = typeof rule.policy === "function" ? await rule.policy(ctx) : rule.policy;
118
118
  const key = typeof rule.key === "function" ? await rule.key(ctx) : rule.key;
119
119
  const cost = typeof rule.cost === "function" ? await rule.cost(ctx) : rule.cost;
120
- if (cost && cost <= 0)
120
+ if (cost !== void 0 && cost <= 0)
121
121
  throw new BadArgumentsException(
122
122
  `Cost must be a positive integer, got cost=${cost}`
123
123
  );
@@ -136,7 +136,7 @@ var RateLimiter = class {
136
136
  if (result.allowed) console.log(debugRules);
137
137
  else console.error(debugRules);
138
138
  }
139
- if (result.remaining === 0) {
139
+ if (!result.allowed) {
140
140
  if (this.debug) {
141
141
  const debugResults = {
142
142
  failedRule: rule.name,
@@ -145,12 +145,7 @@ var RateLimiter = class {
145
145
  };
146
146
  return debugResults;
147
147
  }
148
- return {
149
- ...result,
150
- reset: maxReset,
151
- limit: minLimit,
152
- remaining: minRemaining
153
- };
148
+ return result;
154
149
  }
155
150
  }
156
151
  if (this.debug) {
package/package.json CHANGED
@@ -1,29 +1,43 @@
1
- {
2
- "name": "@limitkit/core",
3
- "version": "0.1.5",
4
- "main": "dist/index.js",
5
- "module": "dist/index.mjs",
6
- "types": "dist/index.d.ts",
7
- "exports": {
8
- ".": "./dist/index.js"
9
- },
10
- "description": "Core rate limiting engine for LimitKit",
11
- "files": [
12
- "dist"
13
- ],
14
- "publishConfig": {
15
- "access": "public"
16
- },
17
- "license": "MIT",
18
- "repository": {
19
- "type": "git",
20
- "url": "https://github.com/alphatrann/limitkit"
21
- },
22
- "scripts": {
23
- "build": "tsup src/index.ts --format esm,cjs --dts",
24
- "test": "jest --silent"
25
- },
26
- "devDependencies": {
27
- "@types/node": "^25.4.0"
28
- }
29
- }
1
+ {
2
+ "name": "@limitkit/core",
3
+ "version": "0.1.7",
4
+ "main": "dist/index.js",
5
+ "module": "dist/index.mjs",
6
+ "types": "dist/index.d.ts",
7
+ "keywords": [
8
+ "rate-limiter",
9
+ "rate-limit",
10
+ "rate-limiting",
11
+ "throttling",
12
+ "token-bucket",
13
+ "leaky-bucket",
14
+ "sliding-window",
15
+ "sliding-window-counter",
16
+ "gcra",
17
+ "rules-engine",
18
+ "policy-based",
19
+ "nodejs"
20
+ ],
21
+ "exports": {
22
+ ".": "./dist/index.js"
23
+ },
24
+ "description": "Core rate limiting engine for LimitKit",
25
+ "files": [
26
+ "dist"
27
+ ],
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
31
+ "license": "MIT",
32
+ "repository": {
33
+ "type": "git",
34
+ "url": "https://github.com/alphatrann/limitkit"
35
+ },
36
+ "scripts": {
37
+ "build": "tsup src/index.ts --format esm,cjs --dts",
38
+ "test": "jest --silent"
39
+ },
40
+ "devDependencies": {
41
+ "@types/node": "^25.4.0"
42
+ }
43
+ }