hono-rate-limiter 0.5.3 → 0.5.4
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/dist/index.cjs +60 -10
- package/dist/index.d.cts +37 -1
- package/dist/index.d.ts +37 -1
- package/dist/index.js +60 -10
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -360,6 +360,19 @@ const scripts = {
|
|
|
360
360
|
local timeToExpire = redis.call("PTTL", KEYS[1])
|
|
361
361
|
|
|
362
362
|
return { totalHits, timeToExpire }
|
|
363
|
+
`.replaceAll(/^\s+/gm, "").trim(),
|
|
364
|
+
// Mirrors the `MemoryStore` behaviour: never go below zero, and never
|
|
365
|
+
// disturb the expiry. A raw DECR on a missing key (e.g. the window expired
|
|
366
|
+
// while a slow handler ran) would create it at -1 with no TTL, and that
|
|
367
|
+
// negative value would leak into the next window, letting a client exceed
|
|
368
|
+
// the configured limit. Reading the value first and only decrementing when
|
|
369
|
+
// it is a positive number avoids that. A nil GET means the window already
|
|
370
|
+
// ended, so the correct action is to do nothing.
|
|
371
|
+
decrement: `
|
|
372
|
+
local current = tonumber(redis.call("GET", KEYS[1]))
|
|
373
|
+
if current and current > 0 then
|
|
374
|
+
redis.call("DECR", KEYS[1])
|
|
375
|
+
end
|
|
363
376
|
`.replaceAll(/^\s+/gm, "").trim()
|
|
364
377
|
};
|
|
365
378
|
|
|
@@ -389,6 +402,13 @@ class RedisStore {
|
|
|
389
402
|
this.resetExpiryOnChange = options.resetExpiryOnChange ?? false;
|
|
390
403
|
this.incrementScriptSha = this.loadIncrementScript();
|
|
391
404
|
this.getScriptSha = this.loadGetScript();
|
|
405
|
+
this.decrementScriptSha = this.loadDecrementScript();
|
|
406
|
+
this.incrementScriptSha.catch(() => {
|
|
407
|
+
});
|
|
408
|
+
this.getScriptSha.catch(() => {
|
|
409
|
+
});
|
|
410
|
+
this.decrementScriptSha.catch(() => {
|
|
411
|
+
});
|
|
392
412
|
}
|
|
393
413
|
/**
|
|
394
414
|
* Loads the script used to increment a client's hit count.
|
|
@@ -410,6 +430,16 @@ class RedisStore {
|
|
|
410
430
|
}
|
|
411
431
|
return result;
|
|
412
432
|
}
|
|
433
|
+
/**
|
|
434
|
+
* Loads the script used to decrement a client's hit count.
|
|
435
|
+
*/
|
|
436
|
+
async loadDecrementScript() {
|
|
437
|
+
const result = await this.client.scriptLoad(scripts.decrement);
|
|
438
|
+
if (typeof result !== "string") {
|
|
439
|
+
throw new TypeError("unexpected reply from redis client");
|
|
440
|
+
}
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
413
443
|
/**
|
|
414
444
|
* Runs the increment command, and retries it if the script is not loaded.
|
|
415
445
|
*/
|
|
@@ -427,6 +457,23 @@ class RedisStore {
|
|
|
427
457
|
return evalCommand();
|
|
428
458
|
}
|
|
429
459
|
}
|
|
460
|
+
/**
|
|
461
|
+
* Runs the decrement command, and retries it if the script is not loaded.
|
|
462
|
+
*/
|
|
463
|
+
async retryableDecrement(key) {
|
|
464
|
+
const evalCommand = async () => this.client.evalsha(
|
|
465
|
+
await this.decrementScriptSha,
|
|
466
|
+
[this.prefixKey(key)],
|
|
467
|
+
[]
|
|
468
|
+
);
|
|
469
|
+
try {
|
|
470
|
+
const result = await evalCommand();
|
|
471
|
+
return result;
|
|
472
|
+
} catch {
|
|
473
|
+
this.decrementScriptSha = this.loadDecrementScript();
|
|
474
|
+
return evalCommand();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
430
477
|
/**
|
|
431
478
|
* Method to prefix the keys with the given text.
|
|
432
479
|
*
|
|
@@ -474,10 +521,16 @@ class RedisStore {
|
|
|
474
521
|
/**
|
|
475
522
|
* Method to decrement a client's hit counter.
|
|
476
523
|
*
|
|
524
|
+
* Uses a Lua script so the read-and-decrement is atomic. It never takes the
|
|
525
|
+
* counter below zero and never touches the key's expiry, mirroring the
|
|
526
|
+
* `MemoryStore`. This prevents a decrement that lands after the window has
|
|
527
|
+
* expired from creating a negative, TTL-less key that would leak extra
|
|
528
|
+
* requests into the next window.
|
|
529
|
+
*
|
|
477
530
|
* @param key {string} - The identifier for a client
|
|
478
531
|
*/
|
|
479
532
|
async decrement(key) {
|
|
480
|
-
await this.
|
|
533
|
+
await this.retryableDecrement(key);
|
|
481
534
|
}
|
|
482
535
|
/**
|
|
483
536
|
* Method to reset a client's hit counter.
|
|
@@ -537,6 +590,11 @@ class UnstorageStore {
|
|
|
537
590
|
* Method to increment a client's hit counter. If the current time is within an active window,
|
|
538
591
|
* it increments the existing hit count. Otherwise, it starts a new window with a hit count of 1.
|
|
539
592
|
*
|
|
593
|
+
* @remarks
|
|
594
|
+
* This read-modify-write is not atomic. Concurrent calls for the same key
|
|
595
|
+
* may under-count and let a client exceed the limit. See the class-level
|
|
596
|
+
* remarks for details and alternatives.
|
|
597
|
+
*
|
|
540
598
|
* @param key {string} - The identifier for a client
|
|
541
599
|
*
|
|
542
600
|
* @returns {ClientRateLimitInfo} - An object containing:
|
|
@@ -659,26 +717,18 @@ function webSocketLimiter(config) {
|
|
|
659
717
|
decremented = true;
|
|
660
718
|
}
|
|
661
719
|
};
|
|
662
|
-
const shouldSkipRequest = async () => {
|
|
663
|
-
if (skipSuccessfulRequests) await decrementKey();
|
|
664
|
-
};
|
|
665
720
|
if (totalHits > _limit) {
|
|
666
|
-
await shouldSkipRequest();
|
|
667
721
|
return handler(event, ws, options);
|
|
668
722
|
}
|
|
669
723
|
try {
|
|
670
724
|
await events.onMessage?.(event, ws);
|
|
671
|
-
await
|
|
725
|
+
if (skipSuccessfulRequests) await decrementKey();
|
|
672
726
|
} catch (error) {
|
|
673
727
|
if (skipFailedRequests) await decrementKey();
|
|
674
728
|
throw error;
|
|
675
729
|
}
|
|
676
730
|
},
|
|
677
731
|
onError: async (event, ws) => {
|
|
678
|
-
if (skipFailedRequests) {
|
|
679
|
-
const key = await keyGenerator(c);
|
|
680
|
-
await store.decrement(key);
|
|
681
|
-
}
|
|
682
732
|
events.onError?.(event, ws);
|
|
683
733
|
}
|
|
684
734
|
};
|
package/dist/index.d.cts
CHANGED
|
@@ -143,7 +143,7 @@ type HonoConfigType<E extends Env = Env, P extends string = string, I extends In
|
|
|
143
143
|
*/
|
|
144
144
|
skipSuccessfulRequests: boolean;
|
|
145
145
|
/**
|
|
146
|
-
* Method to determine whether or not the request counts as '
|
|
146
|
+
* Method to determine whether or not the request counts as 'successful'. Used
|
|
147
147
|
* when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
|
|
148
148
|
*
|
|
149
149
|
* By default, requests with a response status code less than 400 are considered
|
|
@@ -439,6 +439,7 @@ declare class RedisStore<E extends Env$1 = Env$1, P extends string = string, I e
|
|
|
439
439
|
*/
|
|
440
440
|
incrementScriptSha: Promise<string>;
|
|
441
441
|
getScriptSha: Promise<string>;
|
|
442
|
+
decrementScriptSha: Promise<string>;
|
|
442
443
|
/**
|
|
443
444
|
* @constructor for `RedisStore`.
|
|
444
445
|
*
|
|
@@ -453,10 +454,18 @@ declare class RedisStore<E extends Env$1 = Env$1, P extends string = string, I e
|
|
|
453
454
|
* Loads the script used to fetch a client's hit count and expiry time.
|
|
454
455
|
*/
|
|
455
456
|
loadGetScript(): Promise<string>;
|
|
457
|
+
/**
|
|
458
|
+
* Loads the script used to decrement a client's hit count.
|
|
459
|
+
*/
|
|
460
|
+
loadDecrementScript(): Promise<string>;
|
|
456
461
|
/**
|
|
457
462
|
* Runs the increment command, and retries it if the script is not loaded.
|
|
458
463
|
*/
|
|
459
464
|
retryableIncrement(key: string): Promise<RedisReply>;
|
|
465
|
+
/**
|
|
466
|
+
* Runs the decrement command, and retries it if the script is not loaded.
|
|
467
|
+
*/
|
|
468
|
+
retryableDecrement(key: string): Promise<RedisReply>;
|
|
460
469
|
/**
|
|
461
470
|
* Method to prefix the keys with the given text.
|
|
462
471
|
*
|
|
@@ -490,6 +499,12 @@ declare class RedisStore<E extends Env$1 = Env$1, P extends string = string, I e
|
|
|
490
499
|
/**
|
|
491
500
|
* Method to decrement a client's hit counter.
|
|
492
501
|
*
|
|
502
|
+
* Uses a Lua script so the read-and-decrement is atomic. It never takes the
|
|
503
|
+
* counter below zero and never touches the key's expiry, mirroring the
|
|
504
|
+
* `MemoryStore`. This prevents a decrement that lands after the window has
|
|
505
|
+
* expired from creating a negative, TTL-less key that would leak extra
|
|
506
|
+
* requests into the next window.
|
|
507
|
+
*
|
|
493
508
|
* @param key {string} - The identifier for a client
|
|
494
509
|
*/
|
|
495
510
|
decrement(key: string): Promise<void>;
|
|
@@ -510,6 +525,22 @@ type UnstorageInstance = {
|
|
|
510
525
|
* A `Store` that stores the hit count for each client using Unstorage
|
|
511
526
|
*
|
|
512
527
|
* {@link https://unstorage.unjs.io/}
|
|
528
|
+
*
|
|
529
|
+
* @remarks
|
|
530
|
+
* **Not safe under high concurrency.** Unstorage exposes no atomic
|
|
531
|
+
* increment or compare-and-swap primitive, so `increment()` and
|
|
532
|
+
* `decrement()` perform a non-atomic read-modify-write against the backing
|
|
533
|
+
* driver. When many requests for the same key arrive at once, they can all
|
|
534
|
+
* read the same hit count before any of them writes it back, so the counter
|
|
535
|
+
* under-counts and a client can exceed the configured limit. The wider the
|
|
536
|
+
* driver's read/write latency (e.g. Vercel KV, Cloudflare KV, S3), the wider
|
|
537
|
+
* this window. The same applies across multiple server instances sharing one
|
|
538
|
+
* backend.
|
|
539
|
+
*
|
|
540
|
+
* If you need correct counting under concurrent load, use a store backed by
|
|
541
|
+
* an atomic counter such as `RedisStore` (which does all its work inside a
|
|
542
|
+
* Lua script). `MemoryStore` is also race-free within a single process, but
|
|
543
|
+
* does not share state across instances.
|
|
513
544
|
*/
|
|
514
545
|
declare class UnstorageStore<E extends Env$1 = Env$1, P extends string = string, I extends Input$1 = Input$1> implements Store<E, P, I> {
|
|
515
546
|
/**
|
|
@@ -559,6 +590,11 @@ declare class UnstorageStore<E extends Env$1 = Env$1, P extends string = string,
|
|
|
559
590
|
* Method to increment a client's hit counter. If the current time is within an active window,
|
|
560
591
|
* it increments the existing hit count. Otherwise, it starts a new window with a hit count of 1.
|
|
561
592
|
*
|
|
593
|
+
* @remarks
|
|
594
|
+
* This read-modify-write is not atomic. Concurrent calls for the same key
|
|
595
|
+
* may under-count and let a client exceed the limit. See the class-level
|
|
596
|
+
* remarks for details and alternatives.
|
|
597
|
+
*
|
|
562
598
|
* @param key {string} - The identifier for a client
|
|
563
599
|
*
|
|
564
600
|
* @returns {ClientRateLimitInfo} - An object containing:
|
package/dist/index.d.ts
CHANGED
|
@@ -143,7 +143,7 @@ type HonoConfigType<E extends Env = Env, P extends string = string, I extends In
|
|
|
143
143
|
*/
|
|
144
144
|
skipSuccessfulRequests: boolean;
|
|
145
145
|
/**
|
|
146
|
-
* Method to determine whether or not the request counts as '
|
|
146
|
+
* Method to determine whether or not the request counts as 'successful'. Used
|
|
147
147
|
* when either `skipSuccessfulRequests` or `skipFailedRequests` is set to true.
|
|
148
148
|
*
|
|
149
149
|
* By default, requests with a response status code less than 400 are considered
|
|
@@ -439,6 +439,7 @@ declare class RedisStore<E extends Env$1 = Env$1, P extends string = string, I e
|
|
|
439
439
|
*/
|
|
440
440
|
incrementScriptSha: Promise<string>;
|
|
441
441
|
getScriptSha: Promise<string>;
|
|
442
|
+
decrementScriptSha: Promise<string>;
|
|
442
443
|
/**
|
|
443
444
|
* @constructor for `RedisStore`.
|
|
444
445
|
*
|
|
@@ -453,10 +454,18 @@ declare class RedisStore<E extends Env$1 = Env$1, P extends string = string, I e
|
|
|
453
454
|
* Loads the script used to fetch a client's hit count and expiry time.
|
|
454
455
|
*/
|
|
455
456
|
loadGetScript(): Promise<string>;
|
|
457
|
+
/**
|
|
458
|
+
* Loads the script used to decrement a client's hit count.
|
|
459
|
+
*/
|
|
460
|
+
loadDecrementScript(): Promise<string>;
|
|
456
461
|
/**
|
|
457
462
|
* Runs the increment command, and retries it if the script is not loaded.
|
|
458
463
|
*/
|
|
459
464
|
retryableIncrement(key: string): Promise<RedisReply>;
|
|
465
|
+
/**
|
|
466
|
+
* Runs the decrement command, and retries it if the script is not loaded.
|
|
467
|
+
*/
|
|
468
|
+
retryableDecrement(key: string): Promise<RedisReply>;
|
|
460
469
|
/**
|
|
461
470
|
* Method to prefix the keys with the given text.
|
|
462
471
|
*
|
|
@@ -490,6 +499,12 @@ declare class RedisStore<E extends Env$1 = Env$1, P extends string = string, I e
|
|
|
490
499
|
/**
|
|
491
500
|
* Method to decrement a client's hit counter.
|
|
492
501
|
*
|
|
502
|
+
* Uses a Lua script so the read-and-decrement is atomic. It never takes the
|
|
503
|
+
* counter below zero and never touches the key's expiry, mirroring the
|
|
504
|
+
* `MemoryStore`. This prevents a decrement that lands after the window has
|
|
505
|
+
* expired from creating a negative, TTL-less key that would leak extra
|
|
506
|
+
* requests into the next window.
|
|
507
|
+
*
|
|
493
508
|
* @param key {string} - The identifier for a client
|
|
494
509
|
*/
|
|
495
510
|
decrement(key: string): Promise<void>;
|
|
@@ -510,6 +525,22 @@ type UnstorageInstance = {
|
|
|
510
525
|
* A `Store` that stores the hit count for each client using Unstorage
|
|
511
526
|
*
|
|
512
527
|
* {@link https://unstorage.unjs.io/}
|
|
528
|
+
*
|
|
529
|
+
* @remarks
|
|
530
|
+
* **Not safe under high concurrency.** Unstorage exposes no atomic
|
|
531
|
+
* increment or compare-and-swap primitive, so `increment()` and
|
|
532
|
+
* `decrement()` perform a non-atomic read-modify-write against the backing
|
|
533
|
+
* driver. When many requests for the same key arrive at once, they can all
|
|
534
|
+
* read the same hit count before any of them writes it back, so the counter
|
|
535
|
+
* under-counts and a client can exceed the configured limit. The wider the
|
|
536
|
+
* driver's read/write latency (e.g. Vercel KV, Cloudflare KV, S3), the wider
|
|
537
|
+
* this window. The same applies across multiple server instances sharing one
|
|
538
|
+
* backend.
|
|
539
|
+
*
|
|
540
|
+
* If you need correct counting under concurrent load, use a store backed by
|
|
541
|
+
* an atomic counter such as `RedisStore` (which does all its work inside a
|
|
542
|
+
* Lua script). `MemoryStore` is also race-free within a single process, but
|
|
543
|
+
* does not share state across instances.
|
|
513
544
|
*/
|
|
514
545
|
declare class UnstorageStore<E extends Env$1 = Env$1, P extends string = string, I extends Input$1 = Input$1> implements Store<E, P, I> {
|
|
515
546
|
/**
|
|
@@ -559,6 +590,11 @@ declare class UnstorageStore<E extends Env$1 = Env$1, P extends string = string,
|
|
|
559
590
|
* Method to increment a client's hit counter. If the current time is within an active window,
|
|
560
591
|
* it increments the existing hit count. Otherwise, it starts a new window with a hit count of 1.
|
|
561
592
|
*
|
|
593
|
+
* @remarks
|
|
594
|
+
* This read-modify-write is not atomic. Concurrent calls for the same key
|
|
595
|
+
* may under-count and let a client exceed the limit. See the class-level
|
|
596
|
+
* remarks for details and alternatives.
|
|
597
|
+
*
|
|
562
598
|
* @param key {string} - The identifier for a client
|
|
563
599
|
*
|
|
564
600
|
* @returns {ClientRateLimitInfo} - An object containing:
|
package/dist/index.js
CHANGED
|
@@ -358,6 +358,19 @@ const scripts = {
|
|
|
358
358
|
local timeToExpire = redis.call("PTTL", KEYS[1])
|
|
359
359
|
|
|
360
360
|
return { totalHits, timeToExpire }
|
|
361
|
+
`.replaceAll(/^\s+/gm, "").trim(),
|
|
362
|
+
// Mirrors the `MemoryStore` behaviour: never go below zero, and never
|
|
363
|
+
// disturb the expiry. A raw DECR on a missing key (e.g. the window expired
|
|
364
|
+
// while a slow handler ran) would create it at -1 with no TTL, and that
|
|
365
|
+
// negative value would leak into the next window, letting a client exceed
|
|
366
|
+
// the configured limit. Reading the value first and only decrementing when
|
|
367
|
+
// it is a positive number avoids that. A nil GET means the window already
|
|
368
|
+
// ended, so the correct action is to do nothing.
|
|
369
|
+
decrement: `
|
|
370
|
+
local current = tonumber(redis.call("GET", KEYS[1]))
|
|
371
|
+
if current and current > 0 then
|
|
372
|
+
redis.call("DECR", KEYS[1])
|
|
373
|
+
end
|
|
361
374
|
`.replaceAll(/^\s+/gm, "").trim()
|
|
362
375
|
};
|
|
363
376
|
|
|
@@ -387,6 +400,13 @@ class RedisStore {
|
|
|
387
400
|
this.resetExpiryOnChange = options.resetExpiryOnChange ?? false;
|
|
388
401
|
this.incrementScriptSha = this.loadIncrementScript();
|
|
389
402
|
this.getScriptSha = this.loadGetScript();
|
|
403
|
+
this.decrementScriptSha = this.loadDecrementScript();
|
|
404
|
+
this.incrementScriptSha.catch(() => {
|
|
405
|
+
});
|
|
406
|
+
this.getScriptSha.catch(() => {
|
|
407
|
+
});
|
|
408
|
+
this.decrementScriptSha.catch(() => {
|
|
409
|
+
});
|
|
390
410
|
}
|
|
391
411
|
/**
|
|
392
412
|
* Loads the script used to increment a client's hit count.
|
|
@@ -408,6 +428,16 @@ class RedisStore {
|
|
|
408
428
|
}
|
|
409
429
|
return result;
|
|
410
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
* Loads the script used to decrement a client's hit count.
|
|
433
|
+
*/
|
|
434
|
+
async loadDecrementScript() {
|
|
435
|
+
const result = await this.client.scriptLoad(scripts.decrement);
|
|
436
|
+
if (typeof result !== "string") {
|
|
437
|
+
throw new TypeError("unexpected reply from redis client");
|
|
438
|
+
}
|
|
439
|
+
return result;
|
|
440
|
+
}
|
|
411
441
|
/**
|
|
412
442
|
* Runs the increment command, and retries it if the script is not loaded.
|
|
413
443
|
*/
|
|
@@ -425,6 +455,23 @@ class RedisStore {
|
|
|
425
455
|
return evalCommand();
|
|
426
456
|
}
|
|
427
457
|
}
|
|
458
|
+
/**
|
|
459
|
+
* Runs the decrement command, and retries it if the script is not loaded.
|
|
460
|
+
*/
|
|
461
|
+
async retryableDecrement(key) {
|
|
462
|
+
const evalCommand = async () => this.client.evalsha(
|
|
463
|
+
await this.decrementScriptSha,
|
|
464
|
+
[this.prefixKey(key)],
|
|
465
|
+
[]
|
|
466
|
+
);
|
|
467
|
+
try {
|
|
468
|
+
const result = await evalCommand();
|
|
469
|
+
return result;
|
|
470
|
+
} catch {
|
|
471
|
+
this.decrementScriptSha = this.loadDecrementScript();
|
|
472
|
+
return evalCommand();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
428
475
|
/**
|
|
429
476
|
* Method to prefix the keys with the given text.
|
|
430
477
|
*
|
|
@@ -472,10 +519,16 @@ class RedisStore {
|
|
|
472
519
|
/**
|
|
473
520
|
* Method to decrement a client's hit counter.
|
|
474
521
|
*
|
|
522
|
+
* Uses a Lua script so the read-and-decrement is atomic. It never takes the
|
|
523
|
+
* counter below zero and never touches the key's expiry, mirroring the
|
|
524
|
+
* `MemoryStore`. This prevents a decrement that lands after the window has
|
|
525
|
+
* expired from creating a negative, TTL-less key that would leak extra
|
|
526
|
+
* requests into the next window.
|
|
527
|
+
*
|
|
475
528
|
* @param key {string} - The identifier for a client
|
|
476
529
|
*/
|
|
477
530
|
async decrement(key) {
|
|
478
|
-
await this.
|
|
531
|
+
await this.retryableDecrement(key);
|
|
479
532
|
}
|
|
480
533
|
/**
|
|
481
534
|
* Method to reset a client's hit counter.
|
|
@@ -535,6 +588,11 @@ class UnstorageStore {
|
|
|
535
588
|
* Method to increment a client's hit counter. If the current time is within an active window,
|
|
536
589
|
* it increments the existing hit count. Otherwise, it starts a new window with a hit count of 1.
|
|
537
590
|
*
|
|
591
|
+
* @remarks
|
|
592
|
+
* This read-modify-write is not atomic. Concurrent calls for the same key
|
|
593
|
+
* may under-count and let a client exceed the limit. See the class-level
|
|
594
|
+
* remarks for details and alternatives.
|
|
595
|
+
*
|
|
538
596
|
* @param key {string} - The identifier for a client
|
|
539
597
|
*
|
|
540
598
|
* @returns {ClientRateLimitInfo} - An object containing:
|
|
@@ -657,26 +715,18 @@ function webSocketLimiter(config) {
|
|
|
657
715
|
decremented = true;
|
|
658
716
|
}
|
|
659
717
|
};
|
|
660
|
-
const shouldSkipRequest = async () => {
|
|
661
|
-
if (skipSuccessfulRequests) await decrementKey();
|
|
662
|
-
};
|
|
663
718
|
if (totalHits > _limit) {
|
|
664
|
-
await shouldSkipRequest();
|
|
665
719
|
return handler(event, ws, options);
|
|
666
720
|
}
|
|
667
721
|
try {
|
|
668
722
|
await events.onMessage?.(event, ws);
|
|
669
|
-
await
|
|
723
|
+
if (skipSuccessfulRequests) await decrementKey();
|
|
670
724
|
} catch (error) {
|
|
671
725
|
if (skipFailedRequests) await decrementKey();
|
|
672
726
|
throw error;
|
|
673
727
|
}
|
|
674
728
|
},
|
|
675
729
|
onError: async (event, ws) => {
|
|
676
|
-
if (skipFailedRequests) {
|
|
677
|
-
const key = await keyGenerator(c);
|
|
678
|
-
await store.decrement(key);
|
|
679
|
-
}
|
|
680
730
|
events.onError?.(event, ws);
|
|
681
731
|
}
|
|
682
732
|
};
|