@nxtedition/scheduler 4.1.11 → 4.1.13

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
@@ -62,7 +62,7 @@ Unspecified priorities inherit the cap from the priority just below them — cap
62
62
 
63
63
  #### Starvation prevention
64
64
 
65
- Per-priority caps act as backpressure: when running tasks exceed a priority's cap, new tasks at that priority queue. If higher-priority tasks keep arriving, the capped queue could be starved indefinitely. To prevent that, the dispatch loop's fairness lottery (already used to give lower priorities a turn under uniform concurrency) **bypasses per-priority caps** when it picks a queued priority — so a fully-capped queue still drains slowly. The overall `max` is still respected.
65
+ Per-priority caps act as backpressure: when running tasks exceed a priority's cap, new tasks at that priority queue. If higher-priority tasks keep arriving, the capped queue could be starved indefinitely. To prevent that, the dispatch loop's fairness lottery (already used to give lower priorities a turn under uniform concurrency) **bypasses per-priority caps** when it picks a queued priority — so a fully-capped queue still drains slowly. The overall `max` is still respected. When the lottery picks an empty tier, its share goes to the highest-priority backlogged queue (within caps), so unused tiers never inflate lower-priority throughput.
66
66
 
67
67
  In `SharedArrayBuffer` mode, the second constructor argument carries per-priority limits (the buffer carries `max` across workers):
68
68
 
@@ -79,9 +79,9 @@ For more control, use `acquire` / `release` directly:
79
79
 
80
80
  ```js
81
81
  scheduler.acquire(
82
- (opaque) => {
82
+ async (opaque) => {
83
83
  try {
84
- doWork(opaque)
84
+ await doWork(opaque)
85
85
  } finally {
86
86
  scheduler.release()
87
87
  }
@@ -91,6 +91,8 @@ scheduler.acquire(
91
91
  )
92
92
  ```
93
93
 
94
+ **Error contract:** if `fn` throws **synchronously**, the scheduler releases the slot itself (the error propagates to the `acquire()` caller on the fast path, and surfaces as a deferred uncaught exception when thrown from a queued dispatch). Do **not** also call `release()` on the synchronous-throw path — a `try`/`finally` around _synchronous_ work would double-release and free a slot belonging to some other in-flight task. With an `async` callback as above the `finally` is correct: a rejecting async function does not throw synchronously, so the slot is yours to release exactly once.
95
+
94
96
  ### Multi-Worker Coordination
95
97
 
96
98
  Share a concurrency limit across worker threads using `SharedArrayBuffer`:
@@ -115,6 +117,27 @@ The global `max` is a hard cap across all workers — a worker that finds the gl
115
117
 
116
118
  `Atomics.waitAsync` does not keep the Node event loop alive. If a worker is fully idle (`running === 0`) with tasks queued waiting for capacity, you need something else holding the loop open — usually trivial in real applications (HTTP servers, intervals, etc.), but standalone scripts may need a `setInterval(() => {}, …)` until their work is done.
117
119
 
120
+ ### Child Schedulers (hierarchical limits)
121
+
122
+ `scheduler.child(opts?)` creates a child that enforces an **additional** concurrency limit _on top of_ its parent. A task dispatched through a child runs only when it is admitted by **both** the child's own per-priority cap **and** the parent's overall `max`. This composes the classic "global pool of N, M per worker" shape: a shared parent caps total concurrency across the whole process while each worker uses a local child to cap its own share.
123
+
124
+ ```js
125
+ // main.js — one shared global pool of 16
126
+ const sharedState = Scheduler.makeSharedState(16)
127
+ for (let i = 0; i < 4; i++) new Worker('./worker.js', { workerData: sharedState })
128
+
129
+ // worker.js — at most 4 concurrent reads on THIS worker, ≤16 across the whole process
130
+ const parent = new Scheduler(workerData)
131
+ const reads = parent.child({ concurrency: 4 })
132
+ const data = await reads.run(() => fs.readFile(path), 'high')
133
+ ```
134
+
135
+ - `opts` mirrors the constructor (`{ concurrency }`), so children get their own per-priority caps too: `parent.child({ concurrency: { max: 4, low: 1 } })`. Omit it for an unlimited local cap (only the parent constrains).
136
+ - The child has its own independent **priority lottery and starvation prevention**; the parent's overall `max` is the global gate — a **soft limit** when the parent/root is shared (the same bounded read-then-increment overshoot across workers as a plain shared `Scheduler`; see the dispose note above). The parent's _own_ per-priority caps constrain only tasks submitted **directly** to the parent — child tasks see the parent solely as an overall `max`.
137
+ - Children may be **nested** arbitrarily; the tightest cap in the chain wins, and every level's `max` is enforced.
138
+ - A child is a local (non-shared) scheduler that delegates all global accounting — and, when the root is shared, all cross-thread waiting — up the parent chain; only the root touches the `SharedArrayBuffer`.
139
+ - `child[Symbol.dispose]()` returns the child's in-flight slots to the parent, rejects its queued `run()` promises, and unregisters it. Disposing a parent disposes its descendants. Children of a long-lived shared root live until process exit unless disposed — dispose short-lived children explicitly.
140
+
118
141
  ### UV Thread Pool Scheduling
119
142
 
120
143
  A practical use case is coordinating access to the libuv thread pool (`UV_THREADPOOL_SIZE`) across multiple worker threads. For example, several HTTP file-serving workers can share a scheduler so that file-system operations (which consume UV thread pool slots) are prioritized and throttled globally:
@@ -186,23 +209,39 @@ const { running, pending, queues } = scheduler.stats
186
209
 
187
210
  ### `scheduler.run(fn, priority?, opaque?): Promise<T>`
188
211
 
189
- Execute `fn` within the scheduler. Returns a promise that resolves with the return value of `fn`. After the scheduler has been disposed, `run()` rejects with `Error('Scheduler is disposed')` instead of hanging.
212
+ Execute `fn` within the scheduler. Returns a promise that resolves with the return value of `fn` (thenables are assimilated via `Promise.resolve`). After the scheduler has been disposed, `run()` rejects with `Error('Scheduler is disposed')` instead of hanging; with an overall concurrency of `0` (which could never dispatch anything) it rejects with `Error('Scheduler concurrency is 0')`.
190
213
 
191
214
  ### `scheduler.acquire(fn, priority?, opaque?): boolean`
192
215
 
193
- Low-level task acquisition. Returns `true` if a slot was available and `fn` ran synchronously, or `false` if the task was queued (also `false` if the scheduler has been disposed). You **must** call `scheduler.release()` when `fn` completes.
216
+ Low-level task acquisition. Returns `true` if a slot was available and `fn` ran synchronously, or `false` if the task was queued (also `false` if the scheduler has been disposed). The fast path additionally requires that nothing is queued at the same priority, preserving FIFO order within a priority; a fresh task can still start ahead of work queued at a _different_ priority while a freed slot is in flight (a deliberate latency trade-off, bounded to a microtask in shared mode). You **must** call `scheduler.release()` when `fn` completes — except when `fn` throws synchronously, in which case the scheduler has already released the slot (see the Low-Level API example).
217
+
218
+ ### `scheduler.tryAcquire(priority?): boolean`
219
+
220
+ Non-blocking. Takes a slot and returns `true` only if one is free at the given priority **and** nothing is queued at that priority (mirroring `limiter.tryConsume`); otherwise returns `false` without queuing. A successful `tryAcquire` must be paired with `release()`.
194
221
 
195
222
  ### `scheduler.release(): void`
196
223
 
197
224
  Signal task completion. Dequeues the next pending task if concurrency allows.
198
225
 
226
+ ### `scheduler.concurrency`
227
+
228
+ The overall concurrency ceiling (`Infinity` when unlimited). In shared mode this is the buffer's global `max`; a per-instance `options.concurrency.max` is enforced but not reflected here.
229
+
230
+ ### `scheduler.stats`
231
+
232
+ `{ running, pending, queues, shared }` — locally running tasks, locally queued tasks, per-priority queue counts, and (shared mode only) the global `{ running, waiters }` counters. Allocates fresh objects on every read; poll accordingly.
233
+
234
+ ### `scheduler.child(opts?): Scheduler`
235
+
236
+ Create a child scheduler that enforces an additional concurrency limit on top of this one (see [Child Schedulers](#child-schedulers-hierarchical-limits)). `opts` mirrors the constructor (`{ concurrency }`, a number or per-priority object); omit it for an unlimited local cap. A task runs only when admitted by both the child's cap and the parent's overall `max`. Returns a fully-featured `Scheduler` (`run` / `acquire` / `tryAcquire` / `release` / `stats` / `Symbol.dispose`). Throws if the parent is disposed.
237
+
199
238
  ### `Scheduler.makeSharedState(concurrency): SharedArrayBuffer`
200
239
 
201
- Create shared state for cross-worker scheduling.
240
+ Create shared state for cross-worker scheduling. `concurrency` must be `Infinity` or a non-negative integer that fits in a signed 32-bit integer (≤ 2,147,483,647) — the counter is stored as `Int32`.
202
241
 
203
242
  ### `scheduler[Symbol.dispose]()`
204
243
 
205
- Disposes the scheduler: releases any global slots it still holds (shared mode), drops the reference to a pending shared-mode `Atomics.waitAsync`, and makes all further calls inert (`acquire` / `tryAcquire` return `false`; `run` rejects). Use when abandoning a shared-mode Scheduler with tasks still queued so the instance and its parked wait can be garbage-collected (it is otherwise kept alive until process exit so its held slots can be released). Works with `using`.
244
+ Disposes the scheduler: releases any global slots it still holds (shared mode), rejects queued `run()` promises with `Error('Scheduler is disposed')`, drops the reference to a pending shared-mode `Atomics.waitAsync`, and makes all further calls inert (`acquire` / `tryAcquire` return `false`; `run` rejects). Use when abandoning a shared-mode Scheduler with tasks still queued so the instance and its parked wait can be garbage-collected (it is otherwise kept alive until process exit so its held slots can be released). Works with `using`. Note that slots held by still-in-flight tasks are returned to the shared pool immediately — until those tasks actually finish, other workers can transiently admit more than `max` (the limit is soft by design). Disposing a scheduler also disposes its [children](#child-schedulers-hierarchical-limits); disposing a child returns its in-flight slots to the parent and unregisters it.
206
245
 
207
246
  ### `parsePriority(value): number`
208
247
 
@@ -216,7 +255,7 @@ Parse a string or number into a normalized priority value.
216
255
 
217
256
  > Previously exported as `Throttle`. The `Throttle` alias is retained for backward compatibility but is **deprecated** — prefer `Limiter`.
218
257
 
219
- Tokens refill automatically: the constructor starts an internal `unref()`'d timer, and refills are also triggered lazily from `consume()` / `tryConsume()`. There is no manual refill step.
258
+ Tokens refill automatically: the constructor starts an internal timer, and refills are also triggered lazily from `consume()` / `tryConsume()`. There is no manual refill step. While work is queued the timer holds the event loop open (so a script never exits with queued callbacks silently dropped); an idle limiter does not keep the process alive.
220
259
 
221
260
  ### Basic
222
261
 
@@ -284,7 +323,24 @@ const ran = limiter.consume(
284
323
  )
285
324
  ```
286
325
 
287
- `consume` returns `true` if the callback ran immediately (tokens were available and nothing was queued ahead of it), or `false` if it was queued.
326
+ `consume` returns `true` if the callback ran immediately (tokens were available and nothing was queued ahead of it — the call refills the bucket from elapsed time first, so an idle or freshly created limiter answers `true` when the credit covers the request), or `false` if it was queued. Note the boundary is not airtight: a `false` (queued) task can still run synchronously _within_ the same call when the drain the call triggers reaches it immediately.
327
+
328
+ ### Child Limiters (hierarchical rates)
329
+
330
+ `limiter.child(opts)` creates a child token bucket that enforces an **additional** rate _on top of_ its parent. A `consume` of N tokens through the child runs only when N tokens are available in **both** the child's own bucket **and** the parent chain's bucket(s), and the N tokens are debited from every level — exactly mirroring `Scheduler.child()`, for byte-rate instead of concurrency. The canonical shape is a shared parent capping the aggregate rate while each worker caps its own share:
331
+
332
+ ```js
333
+ // main.js — 16 MB/s shared across the whole process
334
+ const sharedState = Limiter.makeSharedState(16_000_000)
335
+ for (let i = 0; i < 4; i++) new Worker('./worker.js', { workerData: sharedState })
336
+
337
+ // worker.js — at most 4 MB/s on THIS worker, ≤16 MB/s across the process
338
+ const parent = new Limiter(workerData)
339
+ const out = parent.child({ tokensPerSecond: 4_000_000 })
340
+ out.consume(() => send(chunk), chunk.length, 'high')
341
+ ```
342
+
343
+ Priorities, `stream()`, and the ~1s anti-starvation force-through all work as usual and apply per level (a forced item is charged into debt on the whole chain, so each level's long-run rate still holds). The child has its own refill timer; the parent needs no knowledge of its children. Children may be nested.
288
344
 
289
345
  ## API
290
346
 
@@ -297,22 +353,74 @@ const ran = limiter.consume(
297
353
 
298
354
  Consume `bytes` tokens, running `fn(opaque)` when they are available. Returns `true` if `fn` ran immediately, `false` if it was queued. `opaque` is passed through to `fn` on both paths.
299
355
 
300
- `bytes` must be a non-negative integer no larger than 2,147,483,647 (the signed Int32 max — tokens are tracked as `Int32`); negative, non-integer, `NaN`, `Infinity`, and out-of-range values throw (a negative or wrapping value would otherwise _add_ tokens via `Atomics.sub` and corrupt the shared bucket). A `bytes` of `0` consumes no tokens and never waits on the bucket. It runs immediately, returning `true`, only when nothing is queued at all (`pending === 0`); otherwise `consume` returns `false`, but the task is still serviced on the very next drain (which the call itself triggers) regardless of the token balance — even while the bucket is empty or in debt. So a weightless task is never blocked by a starved bucket, and it can drain ahead of token-starved work (priority is still honored among tasks that fit). A request larger than `tokensPerSecond` (the bucket's capacity) can never fit a full bucket; rather than stalling forever it is admitted once the bucket fills, drawing the bucket into debt that subsequent refills repay before any other work drains — i.e. it is throttled, not dropped.
356
+ `bytes` must be a non-negative integer no larger than 2,147,483,647 (the signed Int32 max — tokens are tracked as `Int32`); negative, non-integer, `NaN`, `Infinity`, and out-of-range values throw (a negative or wrapping value would otherwise _add_ tokens via `Atomics.sub` and corrupt the shared bucket). A `bytes` of `0` consumes no tokens and never waits on the bucket. It runs immediately, returning `true`, only when nothing is queued at all (`pending === 0`); otherwise `consume` returns `false`, but once it reaches the front of its priority queue it drains regardless of the token balance — even while the bucket is empty or in debt. Queued behind token-starved work at the same priority it waits its turn like any other task (FIFO head-of-line; a different, unblocked priority queue can drain it sooner).
357
+
358
+ **Starvation guard:** a queued task larger than the instantaneous token balance could otherwise starve under sustained traffic — smaller tasks would keep spending each refill's credit first. Any queue whose front item makes no progress for ~1 second is force-admitted into token debt; the negative balance is repaid by subsequent refills before anything else drains, so the long-run rate still holds (the trade-off is a transient burst, at most one stalled item per queue per second). The same mechanism bounds the wait for a request larger than `tokensPerSecond` (the bucket's capacity, which it could never fit): it is admitted once the bucket fills **or** the ~1s stall guard fires, whichever comes first — i.e. it is throttled, not dropped or stalled forever. In shared mode the guard is per-instance best-effort: other workers may keep consuming the shared bucket.
301
359
 
302
360
  ### `limiter.tryConsume(bytes): boolean`
303
361
 
304
362
  Non-blocking. Consumes `bytes` and returns `true` only if enough tokens are available right now and nothing is queued; otherwise returns `false` without queuing.
305
363
 
364
+ ### `limiter.tokensPerSecond`
365
+
366
+ The configured rate (and bucket capacity).
367
+
368
+ ### `limiter.stats`
369
+
370
+ `{ tokens, pending, queues }` — current token balance (negative while in debt), queued tasks, and per-priority queue counts. Allocates fresh objects on every read.
371
+
306
372
  ### `limiter.stream(priority?, options?): Transform`
307
373
 
308
374
  Returns a `Transform` stream that rate-limits data passing through it. Each chunk consumes `chunk.length` tokens; a chunk with no non-negative-integer `length` (e.g. in `objectMode`) is treated as weightless (0 tokens) — it never waits on tokens, though it still preserves the limiter's queue ordering (if other work is already queued, it drains in turn rather than jumping ahead). `options` is forwarded to the underlying `Transform` (e.g. `objectMode`, `highWaterMark`). Destroying the stream while a chunk is still queued in the limiter does not push into the dead stream.
309
375
 
376
+ ### `limiter.child(opts): Limiter`
377
+
378
+ Create a child limiter that enforces an additional rate on top of this one (see [Child Limiters](#child-limiters-hierarchical-rates)). `opts` mirrors the constructor (`{ tokensPerSecond }`). A `consume` runs only when its tokens fit both the child's bucket and the parent chain's, and is debited from both. Returns a fully-featured `Limiter` (`consume` / `tryConsume` / `stream` / `stats`).
379
+
310
380
  ### `Limiter.makeSharedState(tokensPerSecond): SharedArrayBuffer`
311
381
 
312
382
  Create shared state for cross-worker rate limiting.
313
383
 
314
384
  ---
315
385
 
386
+ ## Performance
387
+
388
+ Microbenchmarks from `bench.js` on an AMD EPYC (linux-x64, Node 26). `Mops/s` = millions of ops/sec; higher is better. Numbers are indicative and machine-dependent — run `node --expose-gc bench.js` to reproduce.
389
+
390
+ ### Scheduler hot paths (single thread)
391
+
392
+ | Operation | Throughput |
393
+ | ----------------------------------------- | ----------- |
394
+ | `acquire`+`release` (unlimited, no queue) | ~110 Mops/s |
395
+ | `tryAcquire` fast path | ~150 Mops/s |
396
+ | `acquire`+`release` (SharedArrayBuffer) | ~38 Mops/s |
397
+ | queue drain (concurrency=1, batch) | ~37 Mops/s |
398
+ | `run()` (sync body, Promise per task) | ~4 Mops/s |
399
+
400
+ ### `child()` overhead (single thread)
401
+
402
+ A child adds a local per-priority gate plus one parent-chain acquire/release per task:
403
+
404
+ | Operation | Throughput |
405
+ | ----------------------------------------------- | ---------- |
406
+ | `acquire`+`release` (parent only) | ~71 Mops/s |
407
+ | `acquire`+`release` (through 1 child) | ~36 Mops/s |
408
+ | `acquire`+`release` (through 2 nested children) | ~24 Mops/s |
409
+ | child queue drain (cap=1, batch) | ~20 Mops/s |
410
+
411
+ ### With vs without per-worker `child()` (8 workers, shared global cap=8)
412
+
413
+ The overhead that matters in practice is end-to-end, where real per-task work dominates the extra bookkeeping. With realistic ~50µs tasks a per-worker `child()` costs only **~4%**:
414
+
415
+ | Workload (8 workers) | Plain | With `child(4)` | Overhead |
416
+ | ----------------------------------- | ------------ | --------------- | -------- |
417
+ | 0µs tasks (pure scheduler overhead) | ~1.45 Mops/s | ~1.38 Mops/s | ~5% |
418
+ | 50µs tasks (realistic) | ~132 Kops/s | ~127 Kops/s | ~4% |
419
+
420
+ > An earlier revision sharded the global `RUNNING`/`WAITERS` counters across cache lines to cut contention. Benchmarking on the x64 target showed it was a **net regression** (~25% slower at 32 workers): `RUNNING` is read on every acquire, so aggregating shards costs more coherency traffic than the single-line write contention it avoids. It was reverted to a single counter.
421
+
422
+ ---
423
+
316
424
  ## License
317
425
 
318
426
  MIT
package/lib/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { Scheduler } from './scheduler.ts';
2
+ export type { ConcurrencyOptions } from './scheduler.ts';
2
3
  export { Limiter } from './limiter.ts';
3
4
  /** @deprecated Use `Limiter` instead. */
4
5
  export { Limiter as Throttle } from './limiter.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,yCAAyC;AACzC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAClD,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAA;AACxD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,yCAAyC;AACzC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAClD,YAAY,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,yCAAyC;AACzC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AACtC,yCAAyC;AACzC,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,cAAc,CAAA;AAElD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA"}
package/lib/limiter.d.ts CHANGED
@@ -13,6 +13,27 @@ export declare class Limiter extends Queue {
13
13
  }[];
14
14
  };
15
15
  get tokensPerSecond(): number;
16
+ /**
17
+ * Create a child Limiter that enforces an ADDITIONAL token-rate limit on top of this
18
+ * (the parent). A consume of N tokens through the child runs only when N tokens are
19
+ * available in BOTH the child's own bucket AND the parent chain's bucket(s), and the N
20
+ * tokens are debited from every level.
21
+ *
22
+ * Canonical use: a shared parent caps the aggregate rate (e.g. 16 MB/s across the whole
23
+ * process) while each worker uses a local child to cap its own share (e.g. 4 MB/s):
24
+ *
25
+ * const parent = new Limiter(Limiter.makeSharedState(16_000_000)) // global rate
26
+ * const out = parent.child({ tokensPerSecond: 4_000_000 }) // this worker's rate
27
+ * out.consume(() => send(chunk), chunk.length, 'high')
28
+ *
29
+ * `opts` mirrors the constructor's options. Priorities, streaming, and the ~1s
30
+ * anti-starvation force-through all work as usual and apply per level (a forced item is
31
+ * debited into debt on the whole chain). The child has its own refill timer; the parent
32
+ * needs no knowledge of its children.
33
+ */
34
+ child(opts: {
35
+ tokensPerSecond: number;
36
+ }): Limiter;
16
37
  constructor(opts: SharedArrayBuffer | {
17
38
  tokensPerSecond: number;
18
39
  });
@@ -1 +1 @@
1
- {"version":3,"file":"limiter.d.ts","sourceRoot":"","sources":["../src/limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,EAAuC,KAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAA;AA0BtF,qBAAa,OAAQ,SAAQ,KAAK;;IAChC,OAAO,CAAC,SAAS,CAAY;IAE7B,OAAO,CAAC,OAAO,CAAI;IAUnB,MAAM,CAAC,eAAe,CAAC,eAAe,EAAE,MAAM;IAS9C,IAAI,KAAK;;;;;;MAMR;IAED,IAAI,eAAe,WAElB;gBAEW,IAAI,EAAE,iBAAiB,GAAG;QAAE,eAAe,EAAE,MAAM,CAAA;KAAE;IAwBjE,MAAM,CAAC,QAAQ,GAAE,QAAuB,EAAE,OAAO,CAAC,EAAE,gBAAgB;IAoBpE,OAAO,CAAC,EAAE,EAAE,MAAM,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO;IACvE,OAAO,CAAC,CAAC,EACP,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,EAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,MAAM,EAAE,CAAC,GACR,OAAO;IAmDV,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CA8LnC"}
1
+ {"version":3,"file":"limiter.d.ts","sourceRoot":"","sources":["../src/limiter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE9D,OAAO,EAAuC,KAAK,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAA;AAqCtF,qBAAa,OAAQ,SAAQ,KAAK;;IAChC,OAAO,CAAC,SAAS,CAAY;IAgB7B,OAAO,CAAC,OAAO,CAAI;IAoBnB,MAAM,CAAC,eAAe,CAAC,eAAe,EAAE,MAAM;IAS9C,IAAI,KAAK;;;;;;MAMR;IAED,IAAI,eAAe,WAElB;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,KAAK,CAAC,IAAI,EAAE;QAAE,eAAe,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO;gBAMrC,IAAI,EAAE,iBAAiB,GAAG;QAAE,eAAe,EAAE,MAAM,CAAA;KAAE;IA6BjE,MAAM,CAAC,QAAQ,GAAE,QAAuB,EAAE,OAAO,CAAC,EAAE,gBAAgB;IA8BpE,OAAO,CAAC,EAAE,EAAE,MAAM,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO;IACvE,OAAO,CAAC,CAAC,EACP,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,OAAO,EAC1B,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,GAAG,SAAS,EAC9B,MAAM,EAAE,CAAC,GACR,OAAO;IAyEV,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO;CAmUnC"}