@npclfg/nano-limit 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 npclfg
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,312 @@
1
+ # nano-limit
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@npclfg/nano-limit)](https://npmjs.com/package/@npclfg/nano-limit)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@npclfg/nano-limit)](https://npmjs.com/package/@npclfg/nano-limit)
5
+ [![license](https://img.shields.io/npm/l/@npclfg/nano-limit)](https://github.com/npclfg/nano-limit/blob/main/LICENSE)
6
+
7
+ Tiny concurrency and rate limiter with priorities, AbortSignal, and zero dependencies.
8
+
9
+ - **Zero dependencies**
10
+ - **TypeScript-first** with full type inference
11
+ - **ESM and CommonJS** support
12
+
13
+ ## The Problem
14
+
15
+ You need to limit API calls. You reach for p-limit but:
16
+
17
+ ```typescript
18
+ // p-limit: only concurrency, no rate limiting
19
+ // p-limit: can't do "max 60 requests per minute"
20
+ // p-limit: no priority queue, no AbortSignal
21
+ ```
22
+
23
+ You try bottleneck but:
24
+
25
+ ```typescript
26
+ // bottleneck: complex API with reservoir, highWater, strategy...
27
+ // bottleneck: must call disconnect() or memory leaks
28
+ // bottleneck: breaks with mock timers in tests
29
+ // bottleneck: last major release was 2019
30
+ ```
31
+
32
+ ## The Fix
33
+
34
+ ```typescript
35
+ import { createLimit } from "@npclfg/nano-limit";
36
+
37
+ // Concurrency + rate limiting in one
38
+ const limit = createLimit({
39
+ concurrency: 5, // max 5 parallel
40
+ rate: 60, // max 60 per minute
41
+ interval: 60000,
42
+ });
43
+
44
+ await limit(() => openai.chat.completions.create({ model: "gpt-4", messages }));
45
+
46
+ // Priority queue: important requests go first
47
+ await limit(() => criticalOperation(), { priority: 10 });
48
+
49
+ // Cancel pending operations
50
+ await limit(() => fetch(url), { signal: controller.signal });
51
+ ```
52
+
53
+ That's it. Concurrency limiting, rate limiting, priorities, and cancellation in one tiny package.
54
+
55
+ ## Why nano-limit?
56
+
57
+ | Feature | p-limit | bottleneck | nano-limit |
58
+ |---------|---------|------------|------------|
59
+ | Dependencies | 2 | 0 | **0** |
60
+ | Concurrency limiting | ✅ | ✅ | ✅ |
61
+ | Rate limiting | ❌ | ✅ | ✅ |
62
+ | Priority queue | ❌ | ✅ | ✅ |
63
+ | AbortSignal | ❌ | ❌ | ✅ |
64
+ | Per-key limiting | ❌ | Manual | ✅ Built-in |
65
+ | Queue size limit | ❌ | ✅ | ✅ |
66
+ | onIdle() | ❌ | ✅ | ✅ |
67
+ | Memory leak risk | Low | disconnect() required | **None** |
68
+ | ESM + CJS | ESM-only (v4+) | ✅ | ✅ |
69
+ | Last updated | Active | 2019 | Active |
70
+
71
+ ## Installation
72
+
73
+ ```bash
74
+ npm install @npclfg/nano-limit
75
+ ```
76
+
77
+ **Requirements:** Node.js 16+ or modern browsers (ES2020)
78
+
79
+ ## Quick Start
80
+
81
+ ```typescript
82
+ import { createLimit } from "@npclfg/nano-limit";
83
+
84
+ // Concurrency only (like p-limit)
85
+ const limit = createLimit({ concurrency: 5 });
86
+ await Promise.all(urls.map(url => limit(() => fetch(url))));
87
+
88
+ // Rate limiting (max 10 per second)
89
+ const rateLimited = createLimit({ rate: 10, interval: 1000 });
90
+
91
+ // Both together
92
+ const apiLimit = createLimit({
93
+ concurrency: 5,
94
+ rate: 60,
95
+ interval: 60000,
96
+ });
97
+ ```
98
+
99
+ ## API Reference
100
+
101
+ ### `createLimit(options?): Limiter`
102
+
103
+ Create a concurrency and/or rate limiter.
104
+
105
+ ```typescript
106
+ const limit = createLimit({
107
+ concurrency: 5, // max concurrent (default: Infinity)
108
+ rate: 100, // max per interval
109
+ interval: 1000, // interval in ms (default: 1000)
110
+ maxQueueSize: 1000 // max queued operations (default: Infinity)
111
+ });
112
+
113
+ const result = await limit(() => fetchData());
114
+ ```
115
+
116
+ #### Options
117
+
118
+ | Option | Type | Default | Description |
119
+ |--------|------|---------|-------------|
120
+ | `concurrency` | `number` | `Infinity` | Maximum concurrent operations |
121
+ | `rate` | `number` | - | Maximum operations per interval |
122
+ | `interval` | `number` | `1000` | Interval in ms for rate limiting |
123
+ | `maxQueueSize` | `number` | `Infinity` | Max queued operations (throws QueueFullError) |
124
+
125
+ #### Limiter Properties
126
+
127
+ | Property | Type | Description |
128
+ |----------|------|-------------|
129
+ | `activeCount` | `number` | Currently running operations |
130
+ | `pendingCount` | `number` | Operations waiting in queue |
131
+ | `clearQueue(reject?)` | `function` | Clear pending operations |
132
+ | `onIdle()` | `Promise<void>` | Resolves when queue is empty and all done |
133
+
134
+ ### Operation Options
135
+
136
+ ```typescript
137
+ await limit(() => fn(), {
138
+ priority: 10, // higher = sooner (default: 0)
139
+ signal: controller.signal // AbortSignal for cancellation
140
+ });
141
+ ```
142
+
143
+ ### `createKeyedLimit(options?): KeyedLimiter`
144
+
145
+ Per-key rate limiting for multi-tenant systems.
146
+
147
+ ```typescript
148
+ const userLimit = createKeyedLimit({
149
+ concurrency: 2,
150
+ rate: 10,
151
+ interval: 1000
152
+ });
153
+
154
+ // Each user gets their own limit
155
+ await userLimit("user-123", () => fetchUserData("123"));
156
+ await userLimit("user-456", () => fetchUserData("456"));
157
+
158
+ // Manage keys
159
+ userLimit.get("user-123"); // Get limiter for key
160
+ userLimit.delete("user-123"); // Remove key
161
+ userLimit.clear(); // Remove all keys
162
+ userLimit.size; // Number of active keys
163
+ ```
164
+
165
+ ### `limited(fn, options?): WrappedFunction`
166
+
167
+ Create a pre-configured limited function.
168
+
169
+ ```typescript
170
+ const fetchWithLimit = limited(
171
+ (url: string) => fetch(url),
172
+ { concurrency: 5, rate: 10, interval: 1000 }
173
+ );
174
+
175
+ await fetchWithLimit("/api/data");
176
+ ```
177
+
178
+ ### Error Types
179
+
180
+ ```typescript
181
+ import { AbortError, QueueClearedError, QueueFullError } from "nano-limit";
182
+
183
+ try {
184
+ await limit(() => fn(), { signal });
185
+ } catch (error) {
186
+ if (error instanceof AbortError) {
187
+ // Operation was aborted
188
+ }
189
+ if (error instanceof QueueClearedError) {
190
+ // clearQueue() was called
191
+ }
192
+ if (error instanceof QueueFullError) {
193
+ // maxQueueSize exceeded
194
+ }
195
+ }
196
+ ```
197
+
198
+ ## Patterns & Recipes
199
+
200
+ ### OpenAI/Anthropic Rate Limiting
201
+
202
+ ```typescript
203
+ const aiLimit = createLimit({
204
+ concurrency: 5, // parallel requests
205
+ rate: 60, // 60 RPM
206
+ interval: 60000,
207
+ });
208
+
209
+ const response = await aiLimit(() =>
210
+ openai.chat.completions.create({
211
+ model: "gpt-4",
212
+ messages,
213
+ })
214
+ );
215
+ ```
216
+
217
+ ### Priority Queue
218
+
219
+ ```typescript
220
+ const limit = createLimit({ concurrency: 1 });
221
+
222
+ // High priority runs first
223
+ limit(() => lowPriorityTask());
224
+ limit(() => criticalTask(), { priority: 10 }); // Runs first
225
+ ```
226
+
227
+ ### Graceful Shutdown
228
+
229
+ ```typescript
230
+ const limit = createLimit({ concurrency: 10 });
231
+
232
+ // On shutdown
233
+ process.on("SIGTERM", async () => {
234
+ limit.clearQueue(); // Cancel pending
235
+ await limit.onIdle(); // Wait for active to finish
236
+ process.exit(0);
237
+ });
238
+ ```
239
+
240
+ ### With Timeout
241
+
242
+ ```typescript
243
+ const controller = new AbortController();
244
+ setTimeout(() => controller.abort(), 5000);
245
+
246
+ try {
247
+ await limit(() => slowOperation(), { signal: controller.signal });
248
+ } catch (error) {
249
+ if (error instanceof AbortError) {
250
+ console.log("Timed out");
251
+ }
252
+ }
253
+ ```
254
+
255
+ ### Backpressure / Queue Overflow
256
+
257
+ ```typescript
258
+ const limit = createLimit({
259
+ concurrency: 5,
260
+ maxQueueSize: 100, // Reject if queue grows too large
261
+ });
262
+
263
+ try {
264
+ await limit(() => processRequest());
265
+ } catch (error) {
266
+ if (error instanceof QueueFullError) {
267
+ // Return 503 Service Unavailable
268
+ }
269
+ }
270
+ ```
271
+
272
+ ### Multi-Tenant API
273
+
274
+ ```typescript
275
+ const userLimit = createKeyedLimit({
276
+ concurrency: 2,
277
+ rate: 100,
278
+ interval: 60000,
279
+ });
280
+
281
+ app.use(async (req, res, next) => {
282
+ try {
283
+ await userLimit(req.user.id, () => next());
284
+ } catch (error) {
285
+ if (error instanceof QueueFullError) {
286
+ res.status(429).send("Too Many Requests");
287
+ }
288
+ }
289
+ });
290
+ ```
291
+
292
+ ## TypeScript Usage
293
+
294
+ Full type inference is supported:
295
+
296
+ ```typescript
297
+ import { createLimit, LimitOptions, Limiter } from "nano-limit";
298
+
299
+ interface ApiResponse {
300
+ data: string[];
301
+ }
302
+
303
+ // Return type is inferred as Promise<ApiResponse>
304
+ const result = await limit(async (): Promise<ApiResponse> => {
305
+ const res = await fetch("/api");
306
+ return res.json();
307
+ });
308
+ ```
309
+
310
+ ## License
311
+
312
+ MIT
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Error thrown when a queued operation is aborted.
3
+ */
4
+ export declare class AbortError extends Error {
5
+ readonly isLimitAbort = true;
6
+ constructor(message?: string);
7
+ }
8
+ /**
9
+ * Error thrown when the queue is cleared.
10
+ */
11
+ export declare class QueueClearedError extends Error {
12
+ readonly isQueueCleared = true;
13
+ constructor(message?: string);
14
+ }
15
+ /**
16
+ * Error thrown when the queue is full.
17
+ */
18
+ export declare class QueueFullError extends Error {
19
+ readonly isQueueFull = true;
20
+ constructor(message?: string);
21
+ }
22
+ /**
23
+ * Options for creating a limiter.
24
+ */
25
+ export interface LimitOptions {
26
+ /** Maximum concurrent operations. Default: Infinity */
27
+ concurrency?: number;
28
+ /** Maximum operations per interval (rate limiting). */
29
+ rate?: number;
30
+ /** Interval in ms for rate limiting. Default: 1000 */
31
+ interval?: number;
32
+ /** Maximum queue size. Throws QueueFullError when exceeded. Default: Infinity */
33
+ maxQueueSize?: number;
34
+ }
35
+ /**
36
+ * Options for a single operation.
37
+ */
38
+ export interface OperationOptions {
39
+ /** Priority (higher = sooner). Default: 0 */
40
+ priority?: number;
41
+ /** AbortSignal to cancel the operation. */
42
+ signal?: AbortSignal;
43
+ }
44
+ /**
45
+ * A limiter function that controls concurrency and rate.
46
+ */
47
+ export interface Limiter {
48
+ /**
49
+ * Execute a function with limiting applied.
50
+ */
51
+ <T>(fn: () => T | Promise<T>, options?: OperationOptions): Promise<T>;
52
+ /** Number of operations currently running. */
53
+ readonly activeCount: number;
54
+ /** Number of operations waiting in the queue. */
55
+ readonly pendingCount: number;
56
+ /**
57
+ * Clear all pending operations from the queue.
58
+ * @param reject If true (default), reject pending promises with QueueClearedError.
59
+ */
60
+ clearQueue(reject?: boolean): void;
61
+ /**
62
+ * Returns a promise that resolves when the queue is empty and all operations are complete.
63
+ */
64
+ onIdle(): Promise<void>;
65
+ }
66
+ /**
67
+ * Create a concurrency and/or rate limiter.
68
+ *
69
+ * @param options - Configuration options.
70
+ * @returns A limiter function.
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * // Concurrency only (like p-limit)
75
+ * const limit = createLimit({ concurrency: 5 });
76
+ * await limit(() => fetch(url));
77
+ *
78
+ * // Rate limiting (max 10 per second)
79
+ * const limit = createLimit({ rate: 10, interval: 1000 });
80
+ *
81
+ * // Both
82
+ * const limit = createLimit({ concurrency: 5, rate: 60, interval: 60000 });
83
+ *
84
+ * // With priority
85
+ * await limit(() => importantCall(), { priority: 10 });
86
+ *
87
+ * // With AbortSignal
88
+ * await limit(() => fetch(url), { signal: controller.signal });
89
+ * ```
90
+ */
91
+ export declare function createLimit(options?: LimitOptions): Limiter;
92
+ /**
93
+ * Create a pre-limited version of a function.
94
+ *
95
+ * @param fn - The function to wrap.
96
+ * @param options - Limit options.
97
+ * @returns A function that applies limiting.
98
+ *
99
+ * @example
100
+ * ```typescript
101
+ * const fetchWithLimit = limited(
102
+ * (url: string) => fetch(url),
103
+ * { concurrency: 5 }
104
+ * );
105
+ * await fetchWithLimit("/api/data");
106
+ * ```
107
+ */
108
+ export declare function limited<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => TResult | Promise<TResult>, options?: LimitOptions): (...args: TArgs) => Promise<TResult>;
109
+ /**
110
+ * A keyed limiter for per-key rate limiting.
111
+ */
112
+ export interface KeyedLimiter {
113
+ /**
114
+ * Execute a function with limiting applied to a specific key.
115
+ */
116
+ <T>(key: string, fn: () => T | Promise<T>, options?: OperationOptions): Promise<T>;
117
+ /**
118
+ * Get the limiter for a specific key.
119
+ */
120
+ get(key: string): Limiter;
121
+ /**
122
+ * Delete a limiter for a specific key.
123
+ */
124
+ delete(key: string): boolean;
125
+ /**
126
+ * Clear all limiters.
127
+ */
128
+ clear(): void;
129
+ /** Number of active keys. */
130
+ readonly size: number;
131
+ }
132
+ /**
133
+ * Create a keyed limiter for per-key rate limiting.
134
+ *
135
+ * @param options - Limit options applied to each key.
136
+ * @returns A keyed limiter.
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * const userLimit = createKeyedLimit({ concurrency: 2, rate: 10, interval: 1000 });
141
+ *
142
+ * // Each user gets their own limit
143
+ * await userLimit("user-123", () => fetchUserData("123"));
144
+ * await userLimit("user-456", () => fetchUserData("456"));
145
+ * ```
146
+ */
147
+ export declare function createKeyedLimit(options?: LimitOptions): KeyedLimiter;
148
+ //# sourceMappingURL=limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limit.d.ts","sourceRoot":"","sources":["../../src/limit.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,YAAY,QAAQ;gBACjB,OAAO,SAA0B;CAI9C;AAED;;GAEG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,cAAc,QAAQ;gBACnB,OAAO,SAAsB;CAI1C;AAED;;GAEG;AACH,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,WAAW,QAAQ;gBAChB,OAAO,SAAkB;CAItC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iFAAiF;IACjF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAWD;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB;;OAEG;IACH,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACtE,8CAA8C;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,iDAAiD;IACjD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B;;;OAGG;IACH,UAAU,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,YAAiB,GAAG,OAAO,CAoO/D;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,OAAO,CAAC,KAAK,SAAS,OAAO,EAAE,EAAE,OAAO,EACtD,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,EAClD,OAAO,GAAE,YAAiB,GACzB,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC,OAAO,CAAC,CAGtC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;OAEG;IACH,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACnF;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC1B;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7B;;OAEG;IACH,KAAK,IAAI,IAAI,CAAC;IACd,6BAA6B;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,YAAiB,GAAG,YAAY,CA2CzE"}
@@ -0,0 +1,317 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueueFullError = exports.QueueClearedError = exports.AbortError = void 0;
4
+ exports.createLimit = createLimit;
5
+ exports.limited = limited;
6
+ exports.createKeyedLimit = createKeyedLimit;
7
+ /**
8
+ * Error thrown when a queued operation is aborted.
9
+ */
10
+ class AbortError extends Error {
11
+ constructor(message = "Operation was aborted") {
12
+ super(message);
13
+ this.isLimitAbort = true;
14
+ this.name = "AbortError";
15
+ }
16
+ }
17
+ exports.AbortError = AbortError;
18
+ /**
19
+ * Error thrown when the queue is cleared.
20
+ */
21
+ class QueueClearedError extends Error {
22
+ constructor(message = "Queue was cleared") {
23
+ super(message);
24
+ this.isQueueCleared = true;
25
+ this.name = "QueueClearedError";
26
+ }
27
+ }
28
+ exports.QueueClearedError = QueueClearedError;
29
+ /**
30
+ * Error thrown when the queue is full.
31
+ */
32
+ class QueueFullError extends Error {
33
+ constructor(message = "Queue is full") {
34
+ super(message);
35
+ this.isQueueFull = true;
36
+ this.name = "QueueFullError";
37
+ }
38
+ }
39
+ exports.QueueFullError = QueueFullError;
40
+ /**
41
+ * Create a concurrency and/or rate limiter.
42
+ *
43
+ * @param options - Configuration options.
44
+ * @returns A limiter function.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * // Concurrency only (like p-limit)
49
+ * const limit = createLimit({ concurrency: 5 });
50
+ * await limit(() => fetch(url));
51
+ *
52
+ * // Rate limiting (max 10 per second)
53
+ * const limit = createLimit({ rate: 10, interval: 1000 });
54
+ *
55
+ * // Both
56
+ * const limit = createLimit({ concurrency: 5, rate: 60, interval: 60000 });
57
+ *
58
+ * // With priority
59
+ * await limit(() => importantCall(), { priority: 10 });
60
+ *
61
+ * // With AbortSignal
62
+ * await limit(() => fetch(url), { signal: controller.signal });
63
+ * ```
64
+ */
65
+ function createLimit(options = {}) {
66
+ const { concurrency = Infinity, rate, interval = 1000, maxQueueSize = Infinity, } = options;
67
+ // Validate options
68
+ if (concurrency !== Infinity && (concurrency <= 0 || !Number.isInteger(concurrency))) {
69
+ throw new TypeError("concurrency must be a positive integer or Infinity");
70
+ }
71
+ if (rate !== undefined && (rate <= 0 || !Number.isInteger(rate))) {
72
+ throw new TypeError("rate must be a positive integer");
73
+ }
74
+ if (interval <= 0) {
75
+ throw new TypeError("interval must be positive");
76
+ }
77
+ if (maxQueueSize !== Infinity && (maxQueueSize <= 0 || !Number.isInteger(maxQueueSize))) {
78
+ throw new TypeError("maxQueueSize must be a positive integer or Infinity");
79
+ }
80
+ const queue = [];
81
+ let activeCount = 0;
82
+ let idleResolvers = [];
83
+ // Token bucket for rate limiting
84
+ let tokens = rate ?? Infinity;
85
+ let lastRefill = Date.now();
86
+ let refillTimeoutId = null;
87
+ function refillTokens() {
88
+ if (rate === undefined)
89
+ return;
90
+ const now = Date.now();
91
+ const elapsed = now - lastRefill;
92
+ if (elapsed >= interval) {
93
+ // Full refill
94
+ const periods = Math.floor(elapsed / interval);
95
+ tokens = Math.min(rate, tokens + rate * periods);
96
+ lastRefill += periods * interval;
97
+ }
98
+ }
99
+ function scheduleRefill() {
100
+ if (rate === undefined || refillTimeoutId !== null)
101
+ return;
102
+ if (queue.length === 0)
103
+ return;
104
+ const now = Date.now();
105
+ const timeSinceLastRefill = now - lastRefill;
106
+ const timeUntilRefill = Math.max(0, interval - timeSinceLastRefill);
107
+ refillTimeoutId = setTimeout(() => {
108
+ refillTimeoutId = null;
109
+ refillTokens();
110
+ processQueue();
111
+ }, timeUntilRefill);
112
+ }
113
+ function checkIdle() {
114
+ if (activeCount === 0 && queue.length === 0 && idleResolvers.length > 0) {
115
+ const resolvers = idleResolvers;
116
+ idleResolvers = [];
117
+ for (const resolve of resolvers) {
118
+ resolve();
119
+ }
120
+ }
121
+ }
122
+ function processQueue() {
123
+ refillTokens();
124
+ while (queue.length > 0 && activeCount < concurrency && tokens > 0) {
125
+ const op = queue.shift();
126
+ // Remove abort listener since we're about to execute
127
+ if (op.onAbort && op.signal) {
128
+ op.signal.removeEventListener("abort", op.onAbort);
129
+ }
130
+ // Check if already aborted
131
+ if (op.signal?.aborted) {
132
+ op.reject(new AbortError());
133
+ continue;
134
+ }
135
+ activeCount++;
136
+ if (rate !== undefined) {
137
+ tokens--;
138
+ }
139
+ Promise.resolve()
140
+ .then(() => op.fn())
141
+ .then((result) => {
142
+ activeCount--;
143
+ op.resolve(result);
144
+ processQueue();
145
+ checkIdle();
146
+ }, (error) => {
147
+ activeCount--;
148
+ op.reject(error);
149
+ processQueue();
150
+ checkIdle();
151
+ });
152
+ }
153
+ // Schedule refill if we're rate limited and have pending work
154
+ if (queue.length > 0 && tokens <= 0) {
155
+ scheduleRefill();
156
+ }
157
+ // Check if we're idle
158
+ checkIdle();
159
+ }
160
+ function enqueue(fn, options = {}) {
161
+ // Validate fn
162
+ if (typeof fn !== "function") {
163
+ throw new TypeError("fn must be a function");
164
+ }
165
+ const { priority = 0, signal } = options;
166
+ return new Promise((resolve, reject) => {
167
+ // Check if already aborted
168
+ if (signal?.aborted) {
169
+ reject(new AbortError());
170
+ return;
171
+ }
172
+ // Check queue size
173
+ if (queue.length >= maxQueueSize) {
174
+ reject(new QueueFullError());
175
+ return;
176
+ }
177
+ const op = {
178
+ fn,
179
+ priority,
180
+ resolve: resolve,
181
+ reject,
182
+ signal,
183
+ };
184
+ // Set up abort handler
185
+ if (signal) {
186
+ op.onAbort = () => {
187
+ const index = queue.indexOf(op);
188
+ if (index !== -1) {
189
+ queue.splice(index, 1);
190
+ reject(new AbortError());
191
+ }
192
+ };
193
+ signal.addEventListener("abort", op.onAbort, { once: true });
194
+ }
195
+ // Insert in priority order (higher priority first)
196
+ let inserted = false;
197
+ for (let i = 0; i < queue.length; i++) {
198
+ if (priority > queue[i].priority) {
199
+ queue.splice(i, 0, op);
200
+ inserted = true;
201
+ break;
202
+ }
203
+ }
204
+ if (!inserted) {
205
+ queue.push(op);
206
+ }
207
+ processQueue();
208
+ });
209
+ }
210
+ function clearQueue(reject = true) {
211
+ // Cancel refill timer
212
+ if (refillTimeoutId !== null) {
213
+ clearTimeout(refillTimeoutId);
214
+ refillTimeoutId = null;
215
+ }
216
+ // Process all queued items
217
+ while (queue.length > 0) {
218
+ const op = queue.shift();
219
+ // Clean up abort listener
220
+ if (op.onAbort && op.signal) {
221
+ op.signal.removeEventListener("abort", op.onAbort);
222
+ }
223
+ if (reject) {
224
+ op.reject(new QueueClearedError());
225
+ }
226
+ }
227
+ // Check if we're now idle
228
+ checkIdle();
229
+ }
230
+ function onIdle() {
231
+ if (activeCount === 0 && queue.length === 0) {
232
+ return Promise.resolve();
233
+ }
234
+ return new Promise((resolve) => {
235
+ idleResolvers.push(resolve);
236
+ });
237
+ }
238
+ const limiter = enqueue;
239
+ Object.defineProperty(limiter, "activeCount", {
240
+ get: () => activeCount,
241
+ });
242
+ Object.defineProperty(limiter, "pendingCount", {
243
+ get: () => queue.length,
244
+ });
245
+ limiter.clearQueue = clearQueue;
246
+ limiter.onIdle = onIdle;
247
+ return limiter;
248
+ }
249
+ /**
250
+ * Create a pre-limited version of a function.
251
+ *
252
+ * @param fn - The function to wrap.
253
+ * @param options - Limit options.
254
+ * @returns A function that applies limiting.
255
+ *
256
+ * @example
257
+ * ```typescript
258
+ * const fetchWithLimit = limited(
259
+ * (url: string) => fetch(url),
260
+ * { concurrency: 5 }
261
+ * );
262
+ * await fetchWithLimit("/api/data");
263
+ * ```
264
+ */
265
+ function limited(fn, options = {}) {
266
+ const limit = createLimit(options);
267
+ return (...args) => limit(() => fn(...args));
268
+ }
269
+ /**
270
+ * Create a keyed limiter for per-key rate limiting.
271
+ *
272
+ * @param options - Limit options applied to each key.
273
+ * @returns A keyed limiter.
274
+ *
275
+ * @example
276
+ * ```typescript
277
+ * const userLimit = createKeyedLimit({ concurrency: 2, rate: 10, interval: 1000 });
278
+ *
279
+ * // Each user gets their own limit
280
+ * await userLimit("user-123", () => fetchUserData("123"));
281
+ * await userLimit("user-456", () => fetchUserData("456"));
282
+ * ```
283
+ */
284
+ function createKeyedLimit(options = {}) {
285
+ const limiters = new Map();
286
+ function getOrCreate(key) {
287
+ let limiter = limiters.get(key);
288
+ if (!limiter) {
289
+ limiter = createLimit(options);
290
+ limiters.set(key, limiter);
291
+ }
292
+ return limiter;
293
+ }
294
+ function keyedLimit(key, fn, options) {
295
+ return getOrCreate(key)(fn, options);
296
+ }
297
+ keyedLimit.get = getOrCreate;
298
+ keyedLimit.delete = (key) => {
299
+ const limiter = limiters.get(key);
300
+ if (limiter) {
301
+ limiter.clearQueue(true);
302
+ return limiters.delete(key);
303
+ }
304
+ return false;
305
+ };
306
+ keyedLimit.clear = () => {
307
+ for (const limiter of limiters.values()) {
308
+ limiter.clearQueue(true);
309
+ }
310
+ limiters.clear();
311
+ };
312
+ Object.defineProperty(keyedLimit, "size", {
313
+ get: () => limiters.size,
314
+ });
315
+ return keyedLimit;
316
+ }
317
+ //# sourceMappingURL=limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limit.js","sourceRoot":"","sources":["../../src/limit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,308 @@
1
+ /**
2
+ * Error thrown when a queued operation is aborted.
3
+ */
4
+ export class AbortError extends Error {
5
+ constructor(message = "Operation was aborted") {
6
+ super(message);
7
+ this.isLimitAbort = true;
8
+ this.name = "AbortError";
9
+ }
10
+ }
11
+ /**
12
+ * Error thrown when the queue is cleared.
13
+ */
14
+ export class QueueClearedError extends Error {
15
+ constructor(message = "Queue was cleared") {
16
+ super(message);
17
+ this.isQueueCleared = true;
18
+ this.name = "QueueClearedError";
19
+ }
20
+ }
21
+ /**
22
+ * Error thrown when the queue is full.
23
+ */
24
+ export class QueueFullError extends Error {
25
+ constructor(message = "Queue is full") {
26
+ super(message);
27
+ this.isQueueFull = true;
28
+ this.name = "QueueFullError";
29
+ }
30
+ }
31
+ /**
32
+ * Create a concurrency and/or rate limiter.
33
+ *
34
+ * @param options - Configuration options.
35
+ * @returns A limiter function.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * // Concurrency only (like p-limit)
40
+ * const limit = createLimit({ concurrency: 5 });
41
+ * await limit(() => fetch(url));
42
+ *
43
+ * // Rate limiting (max 10 per second)
44
+ * const limit = createLimit({ rate: 10, interval: 1000 });
45
+ *
46
+ * // Both
47
+ * const limit = createLimit({ concurrency: 5, rate: 60, interval: 60000 });
48
+ *
49
+ * // With priority
50
+ * await limit(() => importantCall(), { priority: 10 });
51
+ *
52
+ * // With AbortSignal
53
+ * await limit(() => fetch(url), { signal: controller.signal });
54
+ * ```
55
+ */
56
+ export function createLimit(options = {}) {
57
+ const { concurrency = Infinity, rate, interval = 1000, maxQueueSize = Infinity, } = options;
58
+ // Validate options
59
+ if (concurrency !== Infinity && (concurrency <= 0 || !Number.isInteger(concurrency))) {
60
+ throw new TypeError("concurrency must be a positive integer or Infinity");
61
+ }
62
+ if (rate !== undefined && (rate <= 0 || !Number.isInteger(rate))) {
63
+ throw new TypeError("rate must be a positive integer");
64
+ }
65
+ if (interval <= 0) {
66
+ throw new TypeError("interval must be positive");
67
+ }
68
+ if (maxQueueSize !== Infinity && (maxQueueSize <= 0 || !Number.isInteger(maxQueueSize))) {
69
+ throw new TypeError("maxQueueSize must be a positive integer or Infinity");
70
+ }
71
+ const queue = [];
72
+ let activeCount = 0;
73
+ let idleResolvers = [];
74
+ // Token bucket for rate limiting
75
+ let tokens = rate ?? Infinity;
76
+ let lastRefill = Date.now();
77
+ let refillTimeoutId = null;
78
+ function refillTokens() {
79
+ if (rate === undefined)
80
+ return;
81
+ const now = Date.now();
82
+ const elapsed = now - lastRefill;
83
+ if (elapsed >= interval) {
84
+ // Full refill
85
+ const periods = Math.floor(elapsed / interval);
86
+ tokens = Math.min(rate, tokens + rate * periods);
87
+ lastRefill += periods * interval;
88
+ }
89
+ }
90
+ function scheduleRefill() {
91
+ if (rate === undefined || refillTimeoutId !== null)
92
+ return;
93
+ if (queue.length === 0)
94
+ return;
95
+ const now = Date.now();
96
+ const timeSinceLastRefill = now - lastRefill;
97
+ const timeUntilRefill = Math.max(0, interval - timeSinceLastRefill);
98
+ refillTimeoutId = setTimeout(() => {
99
+ refillTimeoutId = null;
100
+ refillTokens();
101
+ processQueue();
102
+ }, timeUntilRefill);
103
+ }
104
+ function checkIdle() {
105
+ if (activeCount === 0 && queue.length === 0 && idleResolvers.length > 0) {
106
+ const resolvers = idleResolvers;
107
+ idleResolvers = [];
108
+ for (const resolve of resolvers) {
109
+ resolve();
110
+ }
111
+ }
112
+ }
113
+ function processQueue() {
114
+ refillTokens();
115
+ while (queue.length > 0 && activeCount < concurrency && tokens > 0) {
116
+ const op = queue.shift();
117
+ // Remove abort listener since we're about to execute
118
+ if (op.onAbort && op.signal) {
119
+ op.signal.removeEventListener("abort", op.onAbort);
120
+ }
121
+ // Check if already aborted
122
+ if (op.signal?.aborted) {
123
+ op.reject(new AbortError());
124
+ continue;
125
+ }
126
+ activeCount++;
127
+ if (rate !== undefined) {
128
+ tokens--;
129
+ }
130
+ Promise.resolve()
131
+ .then(() => op.fn())
132
+ .then((result) => {
133
+ activeCount--;
134
+ op.resolve(result);
135
+ processQueue();
136
+ checkIdle();
137
+ }, (error) => {
138
+ activeCount--;
139
+ op.reject(error);
140
+ processQueue();
141
+ checkIdle();
142
+ });
143
+ }
144
+ // Schedule refill if we're rate limited and have pending work
145
+ if (queue.length > 0 && tokens <= 0) {
146
+ scheduleRefill();
147
+ }
148
+ // Check if we're idle
149
+ checkIdle();
150
+ }
151
+ function enqueue(fn, options = {}) {
152
+ // Validate fn
153
+ if (typeof fn !== "function") {
154
+ throw new TypeError("fn must be a function");
155
+ }
156
+ const { priority = 0, signal } = options;
157
+ return new Promise((resolve, reject) => {
158
+ // Check if already aborted
159
+ if (signal?.aborted) {
160
+ reject(new AbortError());
161
+ return;
162
+ }
163
+ // Check queue size
164
+ if (queue.length >= maxQueueSize) {
165
+ reject(new QueueFullError());
166
+ return;
167
+ }
168
+ const op = {
169
+ fn,
170
+ priority,
171
+ resolve: resolve,
172
+ reject,
173
+ signal,
174
+ };
175
+ // Set up abort handler
176
+ if (signal) {
177
+ op.onAbort = () => {
178
+ const index = queue.indexOf(op);
179
+ if (index !== -1) {
180
+ queue.splice(index, 1);
181
+ reject(new AbortError());
182
+ }
183
+ };
184
+ signal.addEventListener("abort", op.onAbort, { once: true });
185
+ }
186
+ // Insert in priority order (higher priority first)
187
+ let inserted = false;
188
+ for (let i = 0; i < queue.length; i++) {
189
+ if (priority > queue[i].priority) {
190
+ queue.splice(i, 0, op);
191
+ inserted = true;
192
+ break;
193
+ }
194
+ }
195
+ if (!inserted) {
196
+ queue.push(op);
197
+ }
198
+ processQueue();
199
+ });
200
+ }
201
+ function clearQueue(reject = true) {
202
+ // Cancel refill timer
203
+ if (refillTimeoutId !== null) {
204
+ clearTimeout(refillTimeoutId);
205
+ refillTimeoutId = null;
206
+ }
207
+ // Process all queued items
208
+ while (queue.length > 0) {
209
+ const op = queue.shift();
210
+ // Clean up abort listener
211
+ if (op.onAbort && op.signal) {
212
+ op.signal.removeEventListener("abort", op.onAbort);
213
+ }
214
+ if (reject) {
215
+ op.reject(new QueueClearedError());
216
+ }
217
+ }
218
+ // Check if we're now idle
219
+ checkIdle();
220
+ }
221
+ function onIdle() {
222
+ if (activeCount === 0 && queue.length === 0) {
223
+ return Promise.resolve();
224
+ }
225
+ return new Promise((resolve) => {
226
+ idleResolvers.push(resolve);
227
+ });
228
+ }
229
+ const limiter = enqueue;
230
+ Object.defineProperty(limiter, "activeCount", {
231
+ get: () => activeCount,
232
+ });
233
+ Object.defineProperty(limiter, "pendingCount", {
234
+ get: () => queue.length,
235
+ });
236
+ limiter.clearQueue = clearQueue;
237
+ limiter.onIdle = onIdle;
238
+ return limiter;
239
+ }
240
+ /**
241
+ * Create a pre-limited version of a function.
242
+ *
243
+ * @param fn - The function to wrap.
244
+ * @param options - Limit options.
245
+ * @returns A function that applies limiting.
246
+ *
247
+ * @example
248
+ * ```typescript
249
+ * const fetchWithLimit = limited(
250
+ * (url: string) => fetch(url),
251
+ * { concurrency: 5 }
252
+ * );
253
+ * await fetchWithLimit("/api/data");
254
+ * ```
255
+ */
256
+ export function limited(fn, options = {}) {
257
+ const limit = createLimit(options);
258
+ return (...args) => limit(() => fn(...args));
259
+ }
260
+ /**
261
+ * Create a keyed limiter for per-key rate limiting.
262
+ *
263
+ * @param options - Limit options applied to each key.
264
+ * @returns A keyed limiter.
265
+ *
266
+ * @example
267
+ * ```typescript
268
+ * const userLimit = createKeyedLimit({ concurrency: 2, rate: 10, interval: 1000 });
269
+ *
270
+ * // Each user gets their own limit
271
+ * await userLimit("user-123", () => fetchUserData("123"));
272
+ * await userLimit("user-456", () => fetchUserData("456"));
273
+ * ```
274
+ */
275
+ export function createKeyedLimit(options = {}) {
276
+ const limiters = new Map();
277
+ function getOrCreate(key) {
278
+ let limiter = limiters.get(key);
279
+ if (!limiter) {
280
+ limiter = createLimit(options);
281
+ limiters.set(key, limiter);
282
+ }
283
+ return limiter;
284
+ }
285
+ function keyedLimit(key, fn, options) {
286
+ return getOrCreate(key)(fn, options);
287
+ }
288
+ keyedLimit.get = getOrCreate;
289
+ keyedLimit.delete = (key) => {
290
+ const limiter = limiters.get(key);
291
+ if (limiter) {
292
+ limiter.clearQueue(true);
293
+ return limiters.delete(key);
294
+ }
295
+ return false;
296
+ };
297
+ keyedLimit.clear = () => {
298
+ for (const limiter of limiters.values()) {
299
+ limiter.clearQueue(true);
300
+ }
301
+ limiters.clear();
302
+ };
303
+ Object.defineProperty(keyedLimit, "size", {
304
+ get: () => limiters.size,
305
+ });
306
+ return keyedLimit;
307
+ }
308
+ //# sourceMappingURL=limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limit.js","sourceRoot":"","sources":["../../src/limit.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,UAAW,SAAQ,KAAK;IAEnC,YAAY,OAAO,GAAG,uBAAuB;QAC3C,KAAK,CAAC,OAAO,CAAC,CAAC;QAFR,iBAAY,GAAG,IAAI,CAAC;QAG3B,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAE1C,YAAY,OAAO,GAAG,mBAAmB;QACvC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFR,mBAAc,GAAG,IAAI,CAAC;QAG7B,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IAEvC,YAAY,OAAO,GAAG,eAAe;QACnC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFR,gBAAW,GAAG,IAAI,CAAC;QAG1B,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AA0DD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,WAAW,CAAC,UAAwB,EAAE;IACpD,MAAM,EACJ,WAAW,GAAG,QAAQ,EACtB,IAAI,EACJ,QAAQ,GAAG,IAAI,EACf,YAAY,GAAG,QAAQ,GACxB,GAAG,OAAO,CAAC;IAEZ,mBAAmB;IACnB,IAAI,WAAW,KAAK,QAAQ,IAAI,CAAC,WAAW,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;QACrF,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,SAAS,CAAC,iCAAiC,CAAC,CAAC;IACzD,CAAC;IACD,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAClB,MAAM,IAAI,SAAS,CAAC,2BAA2B,CAAC,CAAC;IACnD,CAAC;IACD,IAAI,YAAY,KAAK,QAAQ,IAAI,CAAC,YAAY,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;QACxF,MAAM,IAAI,SAAS,CAAC,qDAAqD,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,KAAK,GAA+B,EAAE,CAAC;IAC7C,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,aAAa,GAAmB,EAAE,CAAC;IAEvC,iCAAiC;IACjC,IAAI,MAAM,GAAG,IAAI,IAAI,QAAQ,CAAC;IAC9B,IAAI,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC5B,IAAI,eAAe,GAAyC,IAAI,CAAC;IAEjE,SAAS,YAAY;QACnB,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO;QAE/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,GAAG,GAAG,UAAU,CAAC;QAEjC,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;YACxB,cAAc;YACd,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,QAAQ,CAAC,CAAC;YAC/C,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,CAAC;YACjD,UAAU,IAAI,OAAO,GAAG,QAAQ,CAAC;QACnC,CAAC;IACH,CAAC;IAED,SAAS,cAAc;QACrB,IAAI,IAAI,KAAK,SAAS,IAAI,eAAe,KAAK,IAAI;YAAE,OAAO;QAC3D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAE/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,mBAAmB,GAAG,GAAG,GAAG,UAAU,CAAC;QAC7C,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,mBAAmB,CAAC,CAAC;QAEpE,eAAe,GAAG,UAAU,CAAC,GAAG,EAAE;YAChC,eAAe,GAAG,IAAI,CAAC;YACvB,YAAY,EAAE,CAAC;YACf,YAAY,EAAE,CAAC;QACjB,CAAC,EAAE,eAAe,CAAC,CAAC;IACtB,CAAC;IAED,SAAS,SAAS;QAChB,IAAI,WAAW,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxE,MAAM,SAAS,GAAG,aAAa,CAAC;YAChC,aAAa,GAAG,EAAE,CAAC;YACnB,KAAK,MAAM,OAAO,IAAI,SAAS,EAAE,CAAC;gBAChC,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS,YAAY;QACnB,YAAY,EAAE,CAAC;QAEf,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,GAAG,WAAW,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;YACnE,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;YAE1B,qDAAqD;YACrD,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC;gBAC5B,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;YACrD,CAAC;YAED,2BAA2B;YAC3B,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;gBACvB,EAAE,CAAC,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC;gBAC5B,SAAS;YACX,CAAC;YAED,WAAW,EAAE,CAAC;YACd,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,MAAM,EAAE,CAAC;YACX,CAAC;YAED,OAAO,CAAC,OAAO,EAAE;iBACd,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;iBACnB,IAAI,CACH,CAAC,MAAM,EAAE,EAAE;gBACT,WAAW,EAAE,CAAC;gBACd,EAAE,CAAC,OAAO,CAAC,MAAe,CAAC,CAAC;gBAC5B,YAAY,EAAE,CAAC;gBACf,SAAS,EAAE,CAAC;YACd,CAAC,EACD,CAAC,KAAK,EAAE,EAAE;gBACR,WAAW,EAAE,CAAC;gBACd,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;gBACjB,YAAY,EAAE,CAAC;gBACf,SAAS,EAAE,CAAC;YACd,CAAC,CACF,CAAC;QACN,CAAC;QAED,8DAA8D;QAC9D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;YACpC,cAAc,EAAE,CAAC;QACnB,CAAC;QAED,sBAAsB;QACtB,SAAS,EAAE,CAAC;IACd,CAAC;IAED,SAAS,OAAO,CACd,EAAwB,EACxB,UAA4B,EAAE;QAE9B,cAAc;QACd,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE,CAAC;YAC7B,MAAM,IAAI,SAAS,CAAC,uBAAuB,CAAC,CAAC;QAC/C,CAAC;QAED,MAAM,EAAE,QAAQ,GAAG,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAEzC,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACxC,2BAA2B;YAC3B,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC;gBACzB,OAAO;YACT,CAAC;YAED,mBAAmB;YACnB,IAAI,KAAK,CAAC,MAAM,IAAI,YAAY,EAAE,CAAC;gBACjC,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC,CAAC;gBAC7B,OAAO;YACT,CAAC;YAED,MAAM,EAAE,GAAuB;gBAC7B,EAAE;gBACF,QAAQ;gBACR,OAAO,EAAE,OAAmC;gBAC5C,MAAM;gBACN,MAAM;aACP,CAAC;YAEF,uBAAuB;YACvB,IAAI,MAAM,EAAE,CAAC;gBACX,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE;oBAChB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,EAA8B,CAAC,CAAC;oBAC5D,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;wBACjB,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;wBACvB,MAAM,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC;oBAC3B,CAAC;gBACH,CAAC,CAAC;gBACF,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/D,CAAC;YAED,mDAAmD;YACnD,IAAI,QAAQ,GAAG,KAAK,CAAC;YACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,IAAI,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;oBACjC,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,EAA8B,CAAC,CAAC;oBACnD,QAAQ,GAAG,IAAI,CAAC;oBAChB,MAAM;gBACR,CAAC;YACH,CAAC;YACD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,KAAK,CAAC,IAAI,CAAC,EAA8B,CAAC,CAAC;YAC7C,CAAC;YAED,YAAY,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,SAAS,UAAU,CAAC,MAAM,GAAG,IAAI;QAC/B,sBAAsB;QACtB,IAAI,eAAe,KAAK,IAAI,EAAE,CAAC;YAC7B,YAAY,CAAC,eAAe,CAAC,CAAC;YAC9B,eAAe,GAAG,IAAI,CAAC;QACzB,CAAC;QAED,2BAA2B;QAC3B,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,EAAG,CAAC;YAE1B,0BAA0B;YAC1B,IAAI,EAAE,CAAC,OAAO,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC;gBAC5B,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC;YACrD,CAAC;YAED,IAAI,MAAM,EAAE,CAAC;gBACX,EAAE,CAAC,MAAM,CAAC,IAAI,iBAAiB,EAAE,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAED,0BAA0B;QAC1B,SAAS,EAAE,CAAC;IACd,CAAC;IAED,SAAS,MAAM;QACb,IAAI,WAAW,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5C,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QACD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;YACnC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,OAAkB,CAAC;IAEnC,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,aAAa,EAAE;QAC5C,GAAG,EAAE,GAAG,EAAE,CAAC,WAAW;KACvB,CAAC,CAAC;IAEH,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,cAAc,EAAE;QAC7C,GAAG,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM;KACxB,CAAC,CAAC;IAEH,OAAO,CAAC,UAAU,GAAG,UAAU,CAAC;IAChC,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAExB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,OAAO,CACrB,EAAkD,EAClD,UAAwB,EAAE;IAE1B,MAAM,KAAK,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,IAAW,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AACtD,CAAC;AA0BD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAwB,EAAE;IACzD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAmB,CAAC;IAE5C,SAAS,WAAW,CAAC,GAAW;QAC9B,IAAI,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;YAC/B,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC7B,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,SAAS,UAAU,CACjB,GAAW,EACX,EAAwB,EACxB,OAA0B;QAE1B,OAAO,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,UAAU,CAAC,GAAG,GAAG,WAAW,CAAC;IAE7B,UAAU,CAAC,MAAM,GAAG,CAAC,GAAW,EAAW,EAAE;QAC3C,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACzB,OAAO,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,UAAU,CAAC,KAAK,GAAG,GAAS,EAAE;QAC5B,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC;YACxC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,QAAQ,CAAC,KAAK,EAAE,CAAC;IACnB,CAAC,CAAC;IAEF,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE;QACxC,GAAG,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI;KACzB,CAAC,CAAC;IAEH,OAAO,UAA0B,CAAC;AACpC,CAAC"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/package.json ADDED
@@ -0,0 +1,63 @@
1
+ {
2
+ "name": "@npclfg/nano-limit",
3
+ "version": "1.0.0",
4
+ "description": "Tiny concurrency and rate limiter with priorities, AbortSignal, and zero dependencies.",
5
+ "author": "npclfg",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/npclfg/nano-limit.git"
9
+ },
10
+ "bugs": {
11
+ "url": "https://github.com/npclfg/nano-limit/issues"
12
+ },
13
+ "homepage": "https://github.com/npclfg/nano-limit#readme",
14
+ "main": "./dist/cjs/limit.js",
15
+ "module": "./dist/esm/limit.js",
16
+ "types": "./dist/cjs/limit.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "import": {
20
+ "types": "./dist/cjs/limit.d.ts",
21
+ "default": "./dist/esm/limit.js"
22
+ },
23
+ "require": {
24
+ "types": "./dist/cjs/limit.d.ts",
25
+ "default": "./dist/cjs/limit.js"
26
+ }
27
+ }
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "README.md",
32
+ "LICENSE"
33
+ ],
34
+ "scripts": {
35
+ "build": "npm run build:cjs && npm run build:esm",
36
+ "build:cjs": "tsc",
37
+ "build:esm": "tsc -p tsconfig.esm.json && node scripts/postbuild-esm.js",
38
+ "test": "npm run build && node test/run.js",
39
+ "verify": "npm run build && node scripts/verify.js",
40
+ "bench": "npm run build && node benchmark/bench.js",
41
+ "sample": "npm run build && node sample/app.js",
42
+ "prepublishOnly": "npm run build && npm test"
43
+ },
44
+ "keywords": [
45
+ "limit",
46
+ "rate-limit",
47
+ "concurrency",
48
+ "throttle",
49
+ "queue",
50
+ "async",
51
+ "promise",
52
+ "p-limit",
53
+ "bottleneck",
54
+ "typescript"
55
+ ],
56
+ "license": "MIT",
57
+ "engines": {
58
+ "node": ">=16"
59
+ },
60
+ "devDependencies": {
61
+ "typescript": "^5.0.0"
62
+ }
63
+ }