express-adaptive-rate-limiter 1.0.0 → 2.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/README.md +250 -16
- package/dist/algorithms/baseAlgorithm.js +1 -1
- package/dist/algorithms/fixedWindow.js +1 -1
- package/dist/algorithms/leakyBucket.js +1 -1
- package/dist/algorithms/slidingWindowCounter.js +1 -1
- package/dist/algorithms/slidingWindowLog.js +1 -1
- package/dist/algorithms/tokenBucket.js +1 -1
- package/dist/core/blockedKeys.js +1 -0
- package/dist/core/coreMiddleware.js +1 -0
- package/dist/core/limiters/clusterLimiter.js +16 -0
- package/dist/core/limiters/dynamoDbLimiter.js +1 -0
- package/dist/core/limiters/etcdLimiter.js +1 -0
- package/dist/core/limiters/memcachedLimiter.js +1 -0
- package/dist/core/limiters/memoryLimiter.js +1 -0
- package/dist/core/limiters/mongoLimiter.js +1 -0
- package/dist/core/limiters/mongooseLimiter.js +1 -0
- package/dist/core/limiters/mySqlLimiter.js +12 -0
- package/dist/core/limiters/nodeClusterLimiter.js +1 -0
- package/dist/core/limiters/postgresLimiter.js +13 -0
- package/dist/core/limiters/prismaLimiter.js +24 -0
- package/dist/core/limiters/redisLimiter.js +16 -0
- package/dist/core/limiters/sqliteLimiter.js +13 -0
- package/dist/core/limiters/valkeyLimiter.js +16 -0
- package/dist/core/rateLimiter.js +1 -0
- package/dist/core/rateLimiterRes.js +1 -0
- package/dist/core/rateLimiterStore.js +1 -0
- package/dist/core/wrappers/blackAndWhiteLimiter.js +1 -0
- package/dist/core/wrappers/burstyRateLimiter.js +1 -0
- package/dist/core/wrappers/rateLimiterQueue.js +1 -0
- package/dist/core/wrappers/rateLimiterUnion.js +1 -0
- package/dist/core/wrappers/timeoutLimiter.js +1 -0
- package/dist/index.js +1 -1
- package/dist/middleware/expressMiddleware.js +1 -1
- package/dist/middleware/headers.js +1 -1
- package/dist/rateLimit.js +1 -1
- package/dist/rateLimitManager.js +1 -1
- package/dist/stores/baseStore.js +1 -1
- package/dist/stores/luaScripts.js +379 -1
- package/dist/stores/memoryStore.js +1 -1
- package/dist/stores/redisStore.js +1 -1
- package/dist/types/index.d.ts +507 -0
- package/dist/utils/errors.js +1 -1
- package/dist/utils/graphQLParser.js +1 -1
- package/dist/utils/ipUtils.js +1 -1
- package/dist/utils/keyGenerator.js +1 -1
- package/dist/utils/redisClient.js +1 -0
- package/dist/utils/retryAfter.js +1 -1
- package/dist/utils/skipUtils.js +1 -1
- package/dist/utils/time.js +1 -1
- package/dist/utils/validation.js +1 -1
- package/package.json +67 -4
package/dist/types/index.d.ts
CHANGED
|
@@ -211,6 +211,14 @@ export declare class RedisConnectionError extends RateLimiterError {}
|
|
|
211
211
|
export declare class QueueFullError extends RateLimiterError {}
|
|
212
212
|
export declare class QueueTimeoutError extends RateLimiterError {}
|
|
213
213
|
|
|
214
|
+
export declare const errors: {
|
|
215
|
+
RateLimiterError: typeof RateLimiterError;
|
|
216
|
+
ConfigurationError: typeof ConfigurationError;
|
|
217
|
+
RedisConnectionError: typeof RedisConnectionError;
|
|
218
|
+
QueueFullError: typeof QueueFullError;
|
|
219
|
+
QueueTimeoutError: typeof QueueTimeoutError;
|
|
220
|
+
};
|
|
221
|
+
|
|
214
222
|
export interface RateLimitManagerOptions {
|
|
215
223
|
redisClient?: any;
|
|
216
224
|
client?: any;
|
|
@@ -279,4 +287,503 @@ export declare function checkOriginCriteria(req: Request, originConfig: any): bo
|
|
|
279
287
|
export declare function checkIpCriteria(req: Request, ipConfig: any, clientIp?: string): boolean;
|
|
280
288
|
export declare function checkKeyCriteria(req: Request, keyConfig: any, generatedKey?: string): boolean;
|
|
281
289
|
|
|
290
|
+
/* -------------------------------------------------------------------------- */
|
|
291
|
+
/* Core tier - framework agnostic limiters */
|
|
292
|
+
/* -------------------------------------------------------------------------- */
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Result of a core limiter operation. The field names mirror
|
|
296
|
+
* `rate-limiter-flexible` so migrations between the two packages are mechanical.
|
|
297
|
+
*/
|
|
298
|
+
export declare class RateLimiterRes {
|
|
299
|
+
constructor(
|
|
300
|
+
remainingPoints?: number,
|
|
301
|
+
msBeforeNext?: number,
|
|
302
|
+
consumedPoints?: number,
|
|
303
|
+
isFirstInDuration?: boolean
|
|
304
|
+
);
|
|
305
|
+
remainingPoints: number;
|
|
306
|
+
msBeforeNext: number;
|
|
307
|
+
consumedPoints: number;
|
|
308
|
+
isFirstInDuration: boolean;
|
|
309
|
+
toJSON(): {
|
|
310
|
+
remainingPoints: number;
|
|
311
|
+
msBeforeNext: number;
|
|
312
|
+
consumedPoints: number;
|
|
313
|
+
isFirstInDuration: boolean;
|
|
314
|
+
};
|
|
315
|
+
toString(): string;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export interface RateLimiterOptions {
|
|
319
|
+
/** Maximum points consumable within a duration. */
|
|
320
|
+
points?: number;
|
|
321
|
+
/** Alias of `points`. */
|
|
322
|
+
limit?: number;
|
|
323
|
+
/** Window length in seconds. `0` means points are never reset. */
|
|
324
|
+
duration?: number;
|
|
325
|
+
/** Alias of `duration`, in milliseconds. */
|
|
326
|
+
windowMs?: number;
|
|
327
|
+
/** Seconds a key stays blocked once it exceeds its limit. */
|
|
328
|
+
blockDuration?: number;
|
|
329
|
+
/** Spread allowed requests evenly across the window. */
|
|
330
|
+
execEvenly?: boolean;
|
|
331
|
+
/** Minimum delay between two evenly spread executions. */
|
|
332
|
+
execEvenlyMinDelayMs?: number;
|
|
333
|
+
/** Prefix applied to every stored key. */
|
|
334
|
+
keyPrefix?: string;
|
|
335
|
+
/** Fallback limiter used when the primary store fails. */
|
|
336
|
+
insuranceLimiter?: RateLimiterLike;
|
|
337
|
+
/** Points at which a key starts being blocked in process memory. */
|
|
338
|
+
inMemoryBlockOnConsumed?: number;
|
|
339
|
+
/** Seconds a key stays blocked in process memory. */
|
|
340
|
+
inMemoryBlockDuration?: number;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
export interface CoreLimiterOptions extends RateLimiterOptions {
|
|
344
|
+
/** Per-call overrides, e.g. a different window for one request. */
|
|
345
|
+
customDuration?: number;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export interface RateLimiterLike {
|
|
349
|
+
consume(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
350
|
+
penalty(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
351
|
+
reward(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
352
|
+
get(key: string, options?: CoreLimiterOptions): Promise<RateLimiterRes | null>;
|
|
353
|
+
set(
|
|
354
|
+
key: string,
|
|
355
|
+
points: number,
|
|
356
|
+
secDuration?: number,
|
|
357
|
+
options?: CoreLimiterOptions
|
|
358
|
+
): Promise<RateLimiterRes>;
|
|
359
|
+
block(key: string, secDuration: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
360
|
+
delete(key: string, options?: CoreLimiterOptions): Promise<boolean>;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
export declare class RateLimiter {
|
|
364
|
+
constructor(options?: RateLimiterOptions);
|
|
365
|
+
points: number;
|
|
366
|
+
duration: number;
|
|
367
|
+
msDuration: number;
|
|
368
|
+
blockDuration: number;
|
|
369
|
+
msBlockDuration: number;
|
|
370
|
+
execEvenly: boolean;
|
|
371
|
+
execEvenlyMinDelayMs: number;
|
|
372
|
+
keyPrefix: string;
|
|
373
|
+
insuranceLimiter: RateLimiterLike | null;
|
|
374
|
+
getKey(key: string): string;
|
|
375
|
+
parseKey(rlKey: string): string;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export declare class RateLimiterStore extends RateLimiter {
|
|
379
|
+
constructor(options?: RateLimiterOptions);
|
|
380
|
+
inMemoryBlockOnConsumed: number;
|
|
381
|
+
inMemoryBlockDuration: number;
|
|
382
|
+
msInMemoryBlockDuration: number;
|
|
383
|
+
deleteInMemoryBlocked(key: string): boolean;
|
|
384
|
+
deleteInMemoryBlockedAll(): void;
|
|
385
|
+
getInMemoryBlockMsBeforeExpire(rlKey: string): number;
|
|
386
|
+
consume(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
387
|
+
penalty(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
388
|
+
reward(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
389
|
+
get(key: string, options?: CoreLimiterOptions): Promise<RateLimiterRes | null>;
|
|
390
|
+
set(
|
|
391
|
+
key: string,
|
|
392
|
+
points: number,
|
|
393
|
+
secDuration?: number,
|
|
394
|
+
options?: CoreLimiterOptions
|
|
395
|
+
): Promise<RateLimiterRes>;
|
|
396
|
+
block(key: string, secDuration: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
397
|
+
delete(key: string, options?: CoreLimiterOptions): Promise<boolean>;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export declare class BlockedKeys {
|
|
401
|
+
add(key: string, durationSec: number): void;
|
|
402
|
+
addMs(key: string, durationMs: number): void;
|
|
403
|
+
msBeforeExpire(key: string): number;
|
|
404
|
+
isBlocked(key: string): boolean;
|
|
405
|
+
delete(key: string): boolean;
|
|
406
|
+
deleteAll(): void;
|
|
407
|
+
readonly size: number;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
export interface MemoryLimiterOptions extends RateLimiterOptions {
|
|
411
|
+
/** Interval in milliseconds used to sweep expired keys. `0` disables it. */
|
|
412
|
+
sweepIntervalMs?: number;
|
|
413
|
+
/** Maximum number of stored keys. `0` means unlimited. */
|
|
414
|
+
maxEntries?: number;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export declare class MemoryLimiter extends RateLimiterStore {
|
|
418
|
+
constructor(options?: MemoryLimiterOptions);
|
|
419
|
+
clear(): Promise<void>;
|
|
420
|
+
close(): Promise<void>;
|
|
421
|
+
dump(): {
|
|
422
|
+
version: number;
|
|
423
|
+
dumpedAt: number;
|
|
424
|
+
storage: Array<{ key: string; value: number; expiresAt: number | null }>;
|
|
425
|
+
};
|
|
426
|
+
restore(
|
|
427
|
+
dumpData: any,
|
|
428
|
+
detailResponse?: boolean
|
|
429
|
+
): {
|
|
430
|
+
invalid: number;
|
|
431
|
+
expired: number;
|
|
432
|
+
restored: number;
|
|
433
|
+
invalidKeys?: string[];
|
|
434
|
+
expiredKeys?: string[];
|
|
435
|
+
restoredKeys?: string[];
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
export interface RedisLimiterOptions extends RateLimiterOptions {
|
|
440
|
+
storeClient?: any;
|
|
441
|
+
client?: any;
|
|
442
|
+
redis?: any;
|
|
443
|
+
/** Force a client family instead of detecting it. */
|
|
444
|
+
clientKind?: string;
|
|
445
|
+
/** Reject requests while the connection is not ready. */
|
|
446
|
+
rejectIfRedisNotReady?: boolean;
|
|
447
|
+
customIncrTtlLuaScript?: string;
|
|
448
|
+
customSetTtlLuaScript?: string;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export declare class RedisLimiter extends RateLimiterStore {
|
|
452
|
+
constructor(options: RedisLimiterOptions);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
export declare class ValkeyLimiter extends RateLimiterStore {
|
|
456
|
+
constructor(options: RedisLimiterOptions);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export interface ClusterLimiterOptions extends RedisLimiterOptions {
|
|
460
|
+
/** Wrap storage keys in a hash tag so they share a Redis Cluster slot. */
|
|
461
|
+
hashTag?: boolean;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export declare class ClusterLimiter extends RateLimiterStore {
|
|
465
|
+
constructor(options: ClusterLimiterOptions);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export declare const RedisClusterLimiter: typeof ClusterLimiter;
|
|
469
|
+
export type RedisClusterLimiter = ClusterLimiter;
|
|
470
|
+
|
|
471
|
+
export declare class RateLimiterClusterMaster {
|
|
472
|
+
constructor(targetCluster?: any);
|
|
473
|
+
static instance: RateLimiterClusterMaster | null;
|
|
474
|
+
close(): void;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
export declare class RateLimiterClusterMasterPM2 {
|
|
478
|
+
constructor(pm2?: any);
|
|
479
|
+
close(): void;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
export interface RateLimiterClusterOptions extends RateLimiterOptions {
|
|
483
|
+
timeoutMs?: number;
|
|
484
|
+
customProcess?: any;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export declare class RateLimiterCluster extends RateLimiter {
|
|
488
|
+
constructor(options?: RateLimiterClusterOptions);
|
|
489
|
+
timeoutMs: number;
|
|
490
|
+
consume(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
491
|
+
penalty(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
492
|
+
reward(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
493
|
+
get(key: string, options?: CoreLimiterOptions): Promise<RateLimiterRes | null>;
|
|
494
|
+
set(
|
|
495
|
+
key: string,
|
|
496
|
+
points: number,
|
|
497
|
+
secDuration?: number,
|
|
498
|
+
options?: CoreLimiterOptions
|
|
499
|
+
): Promise<RateLimiterRes>;
|
|
500
|
+
block(key: string, secDuration: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
501
|
+
delete(key: string, options?: CoreLimiterOptions): Promise<boolean>;
|
|
502
|
+
close(): void;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
export interface MongoLimiterOptions extends RateLimiterOptions {
|
|
506
|
+
storeClient?: any;
|
|
507
|
+
mongo?: any;
|
|
508
|
+
client?: any;
|
|
509
|
+
dbName?: string;
|
|
510
|
+
tableName?: string;
|
|
511
|
+
disableIndexesCreation?: boolean;
|
|
512
|
+
clearExpiredByTimeout?: boolean;
|
|
513
|
+
clearExpiredTimeoutMs?: number;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
export declare class MongoLimiter extends RateLimiterStore {
|
|
517
|
+
constructor(options: MongoLimiterOptions);
|
|
518
|
+
clearExpired(): Promise<void>;
|
|
519
|
+
close(): Promise<void>;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
export interface MongooseLimiterOptions extends RateLimiterOptions {
|
|
523
|
+
model?: any;
|
|
524
|
+
storeClient?: any;
|
|
525
|
+
mongoose?: any;
|
|
526
|
+
client?: any;
|
|
527
|
+
modelName?: string;
|
|
528
|
+
schema?: any;
|
|
529
|
+
disableIndexesCreation?: boolean;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
export declare class MongooseLimiter extends RateLimiterStore {
|
|
533
|
+
constructor(options: MongooseLimiterOptions);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
export interface DynamoDbLimiterOptions extends RateLimiterOptions {
|
|
537
|
+
storeClient?: any;
|
|
538
|
+
client?: any;
|
|
539
|
+
dynamoDb?: any;
|
|
540
|
+
tableName?: string;
|
|
541
|
+
commands?: any;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export declare class DynamoDbLimiter extends RateLimiterStore {
|
|
545
|
+
constructor(options: DynamoDbLimiterOptions);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
export interface SqlLimiterOptions extends RateLimiterOptions {
|
|
549
|
+
storeClient?: any;
|
|
550
|
+
client?: any;
|
|
551
|
+
tableName?: string;
|
|
552
|
+
/** `true` when the table already exists. */
|
|
553
|
+
tableCreated?: boolean;
|
|
554
|
+
clearExpiredByTimeout?: boolean;
|
|
555
|
+
clearExpiredTimeoutMs?: number;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
export interface PrismaLimiterOptions extends SqlLimiterOptions {
|
|
559
|
+
prisma?: any;
|
|
560
|
+
/** SQL dialect used for the raw statements. */
|
|
561
|
+
dialect?: 'postgres' | 'postgresql' | 'mysql';
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export declare class MySqlLimiter extends RateLimiterStore {
|
|
565
|
+
constructor(options: SqlLimiterOptions);
|
|
566
|
+
createTable(): Promise<void>;
|
|
567
|
+
clear(): Promise<void>;
|
|
568
|
+
clearExpired(): Promise<void>;
|
|
569
|
+
close(): Promise<void>;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
export declare class PostgresLimiter extends RateLimiterStore {
|
|
573
|
+
constructor(options: SqlLimiterOptions);
|
|
574
|
+
createTable(): Promise<void>;
|
|
575
|
+
clear(): Promise<void>;
|
|
576
|
+
clearExpired(): Promise<void>;
|
|
577
|
+
close(): Promise<void>;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export declare class SqliteLimiter extends RateLimiterStore {
|
|
581
|
+
constructor(options: SqlLimiterOptions);
|
|
582
|
+
createTable(): Promise<void>;
|
|
583
|
+
clear(): Promise<void>;
|
|
584
|
+
clearExpired(): Promise<void>;
|
|
585
|
+
close(): Promise<void>;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
export declare class PrismaLimiter extends RateLimiterStore {
|
|
589
|
+
constructor(options: PrismaLimiterOptions);
|
|
590
|
+
createTable(): Promise<void>;
|
|
591
|
+
clear(): Promise<void>;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
export interface MemcachedLimiterOptions extends RateLimiterOptions {
|
|
595
|
+
storeClient?: any;
|
|
596
|
+
client?: any;
|
|
597
|
+
memcached?: any;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
export declare class MemcachedLimiter extends RateLimiterStore {
|
|
601
|
+
constructor(options: MemcachedLimiterOptions);
|
|
602
|
+
clear(): void;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
export interface EtcdLimiterOptions extends RateLimiterOptions {
|
|
606
|
+
storeClient?: any;
|
|
607
|
+
client?: any;
|
|
608
|
+
etcd?: any;
|
|
609
|
+
maxRetries?: number;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export declare class EtcdLimiter extends RateLimiterStore {
|
|
613
|
+
constructor(options: EtcdLimiterOptions);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
export declare class RateLimiterQueueError extends RateLimiterError {}
|
|
617
|
+
|
|
618
|
+
export interface RateLimiterQueueOptions {
|
|
619
|
+
maxQueueSize?: number;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
export declare class RateLimiterQueue {
|
|
623
|
+
constructor(limiter: RateLimiterLike, options?: RateLimiterQueueOptions);
|
|
624
|
+
removeTokens(tokens: number, key?: string, expiresUnixAt?: number): Promise<number>;
|
|
625
|
+
getTokensRemaining(key?: string): Promise<number>;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
export declare class RateLimiterUnion {
|
|
629
|
+
constructor(...limiters: RateLimiterLike[]);
|
|
630
|
+
consume(key: string, points?: number): Promise<Record<string, RateLimiterRes>>;
|
|
631
|
+
get(key: string): Promise<Record<string, RateLimiterRes | null>>;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export declare class BurstyRateLimiter {
|
|
635
|
+
constructor(rateLimiter: RateLimiterLike, burstLimiter: RateLimiterLike);
|
|
636
|
+
consume(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
637
|
+
get(key: string): Promise<RateLimiterRes>;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
export interface BlackAndWhiteLimiterOptions {
|
|
641
|
+
limiter: RateLimiterLike;
|
|
642
|
+
blackList?: string[];
|
|
643
|
+
whiteList?: string[];
|
|
644
|
+
isBlackListed?: (key: string) => boolean;
|
|
645
|
+
isWhiteListed?: (key: string) => boolean;
|
|
646
|
+
runActionAnyway?: boolean;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
export declare class BlackAndWhiteLimiter {
|
|
650
|
+
constructor(options: BlackAndWhiteLimiterOptions);
|
|
651
|
+
consume(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
652
|
+
penalty(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
653
|
+
reward(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
654
|
+
get(key: string, options?: CoreLimiterOptions): Promise<RateLimiterRes | null>;
|
|
655
|
+
set(
|
|
656
|
+
key: string,
|
|
657
|
+
points: number,
|
|
658
|
+
secDuration?: number,
|
|
659
|
+
options?: CoreLimiterOptions
|
|
660
|
+
): Promise<RateLimiterRes>;
|
|
661
|
+
block(key: string, secDuration: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
662
|
+
delete(key: string, options?: CoreLimiterOptions): Promise<boolean>;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export interface TimeoutLimiterOptions {
|
|
666
|
+
limiter: RateLimiterLike;
|
|
667
|
+
timeoutMs: number;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
export declare class TimeoutLimiter {
|
|
671
|
+
constructor(options: TimeoutLimiterOptions);
|
|
672
|
+
consume(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
673
|
+
penalty(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
674
|
+
reward(key: string, points?: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
675
|
+
get(key: string, options?: CoreLimiterOptions): Promise<RateLimiterRes | null>;
|
|
676
|
+
set(
|
|
677
|
+
key: string,
|
|
678
|
+
points: number,
|
|
679
|
+
secDuration?: number,
|
|
680
|
+
options?: CoreLimiterOptions
|
|
681
|
+
): Promise<RateLimiterRes>;
|
|
682
|
+
block(key: string, secDuration: number, options?: CoreLimiterOptions): Promise<RateLimiterRes>;
|
|
683
|
+
delete(key: string, options?: CoreLimiterOptions): Promise<boolean>;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
export interface CoreMiddlewareOptions {
|
|
687
|
+
keyGenerator?: KeyGenerator;
|
|
688
|
+
cost?: number | CostFunction;
|
|
689
|
+
skip?: SkipOption;
|
|
690
|
+
customSkip?: SkipOption;
|
|
691
|
+
customskip?: SkipOption;
|
|
692
|
+
handler?: RateLimitHandler;
|
|
693
|
+
onError?: ErrorHandler;
|
|
694
|
+
message?: string | object | ((req: Request, res: Response) => any);
|
|
695
|
+
statusCode?: number;
|
|
696
|
+
standardHeaders?: boolean | 'draft-6' | 'draft-7';
|
|
697
|
+
legacyHeaders?: boolean;
|
|
698
|
+
failOpen?: boolean;
|
|
699
|
+
windowMs?: number;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
export interface CoreMiddleware {
|
|
703
|
+
(req: Request, res: Response, next: NextFunction): Promise<any>;
|
|
704
|
+
resetKey(keyOrReq: string | Request): Promise<boolean>;
|
|
705
|
+
getState(keyOrReq: string | Request): Promise<RateLimiterRes | null>;
|
|
706
|
+
limiter: RateLimiterLike;
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
export declare function createCoreMiddleware(
|
|
710
|
+
limiter: RateLimiterLike,
|
|
711
|
+
options?: CoreMiddlewareOptions
|
|
712
|
+
): CoreMiddleware;
|
|
713
|
+
|
|
714
|
+
export type RedisClientKind = 'ioredis' | 'node-redis-v3' | 'node-redis-v4' | 'unknown';
|
|
715
|
+
|
|
716
|
+
export declare function detectRedisClientKind(client: any): RedisClientKind;
|
|
717
|
+
|
|
718
|
+
export declare class RedisClientAdapter {
|
|
719
|
+
constructor(client: any, options?: { clientKind?: RedisClientKind });
|
|
720
|
+
readonly kind: RedisClientKind;
|
|
721
|
+
readonly client: any;
|
|
722
|
+
usesObjectEval(): boolean;
|
|
723
|
+
static scriptSha(script: string): string;
|
|
724
|
+
evalScript(script: string, keys?: string[], args?: any[]): Promise<any>;
|
|
725
|
+
getWithTtl(key: string): Promise<{ value: string; ttlMs: number } | null>;
|
|
726
|
+
pttl(key: string): Promise<number>;
|
|
727
|
+
del(key: string): Promise<boolean>;
|
|
728
|
+
delMany(keys: string[]): Promise<number>;
|
|
729
|
+
setWithTtl(key: string, value: string, ttlMs?: number): Promise<any>;
|
|
730
|
+
scanKeys(pattern: string): Promise<string[]>;
|
|
731
|
+
isReady(rlKey: string, isReadonly: boolean): boolean;
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
/* -------------------------------------------------------------------------- */
|
|
735
|
+
/* rate-limiter-flexible 100% Drop-in Compatibility Aliases */
|
|
736
|
+
/* -------------------------------------------------------------------------- */
|
|
737
|
+
|
|
738
|
+
export declare const RateLimiterMemory: typeof MemoryLimiter;
|
|
739
|
+
export type RateLimiterMemory = MemoryLimiter;
|
|
740
|
+
|
|
741
|
+
export declare const RateLimiterRedis: typeof RedisLimiter;
|
|
742
|
+
export type RateLimiterRedis = RedisLimiter;
|
|
743
|
+
|
|
744
|
+
export declare const RateLimiterMongo: typeof MongoLimiter;
|
|
745
|
+
export type RateLimiterMongo = MongoLimiter;
|
|
746
|
+
|
|
747
|
+
export declare const RateLimiterMongoose: typeof MongooseLimiter;
|
|
748
|
+
export type RateLimiterMongoose = MongooseLimiter;
|
|
749
|
+
|
|
750
|
+
export declare const RateLimiterMySQL: typeof MySqlLimiter;
|
|
751
|
+
export type RateLimiterMySQL = MySqlLimiter;
|
|
752
|
+
|
|
753
|
+
export declare const RateLimiterPostgres: typeof PostgresLimiter;
|
|
754
|
+
export type RateLimiterPostgres = PostgresLimiter;
|
|
755
|
+
|
|
756
|
+
export declare const RateLimiterSQLite: typeof SqliteLimiter;
|
|
757
|
+
export type RateLimiterSQLite = SqliteLimiter;
|
|
758
|
+
|
|
759
|
+
export declare const RateLimiterPrisma: typeof PrismaLimiter;
|
|
760
|
+
export type RateLimiterPrisma = PrismaLimiter;
|
|
761
|
+
|
|
762
|
+
export declare const RateLimiterMemcache: typeof MemcachedLimiter;
|
|
763
|
+
export type RateLimiterMemcache = MemcachedLimiter;
|
|
764
|
+
|
|
765
|
+
export declare const RateLimiterMemcached: typeof MemcachedLimiter;
|
|
766
|
+
export type RateLimiterMemcached = MemcachedLimiter;
|
|
767
|
+
|
|
768
|
+
export declare const RateLimiterDynamo: typeof DynamoDbLimiter;
|
|
769
|
+
export type RateLimiterDynamo = DynamoDbLimiter;
|
|
770
|
+
|
|
771
|
+
export declare const RateLimiterDynamoDb: typeof DynamoDbLimiter;
|
|
772
|
+
export type RateLimiterDynamoDb = DynamoDbLimiter;
|
|
773
|
+
|
|
774
|
+
export declare const RateLimiterValkey: typeof ValkeyLimiter;
|
|
775
|
+
export type RateLimiterValkey = ValkeyLimiter;
|
|
776
|
+
|
|
777
|
+
export declare const RateLimiterEtcd: typeof EtcdLimiter;
|
|
778
|
+
export type RateLimiterEtcd = EtcdLimiter;
|
|
779
|
+
|
|
780
|
+
export declare const RateLimiterAbstract: typeof RateLimiter;
|
|
781
|
+
export type RateLimiterAbstract = RateLimiter;
|
|
782
|
+
|
|
783
|
+
export declare const RateLimiterStoreAbstract: typeof RateLimiterStore;
|
|
784
|
+
export type RateLimiterStoreAbstract = RateLimiterStore;
|
|
785
|
+
|
|
786
|
+
export declare const RateLimiterBlackAndWhite: typeof BlackAndWhiteLimiter;
|
|
787
|
+
export type RateLimiterBlackAndWhite = BlackAndWhiteLimiter;
|
|
788
|
+
|
|
282
789
|
export default rateLimit;
|
package/dist/utils/errors.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export class RateLimiterError extends Error{constructor(message){super(message);this.name=this.constructor.name;Error.captureStackTrace(this,this.constructor);}static[Symbol.hasInstance](instance){if(!instance)return false;return instance.name==='RateLimiterError'||instance.name==='ConfigurationError'||instance.name==='RedisConnectionError'||instance.name==='QueueFullError'||instance.name==='QueueTimeoutError'||super[Symbol.hasInstance](instance);}}export class ConfigurationError extends RateLimiterError{constructor(message){super(message);this.name='ConfigurationError';}static[Symbol.hasInstance](instance){return Boolean(instance&&instance.name==='ConfigurationError');}}export class RedisConnectionError extends RateLimiterError{constructor(message,cause){super(message);this.name='RedisConnectionError';if(cause)this.cause=cause;}static[Symbol.hasInstance](instance){return Boolean(instance&&instance.name==='RedisConnectionError');}}export class QueueFullError extends RateLimiterError{constructor(message='Rate limiter queue is full'){super(message);this.name='QueueFullError';}static[Symbol.hasInstance](instance){return Boolean(instance&&instance.name==='QueueFullError');}}export class QueueTimeoutError extends RateLimiterError{constructor(message='Rate limiter request queue timed out'){super(message);this.name='QueueTimeoutError';}static[Symbol.hasInstance](instance){return Boolean(instance&&instance.name==='QueueTimeoutError');}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import{createRequire}from'module';const cache=new Map();const MAX_CACHE_SIZE=2000;let graphqlModule=null;try{const req=typeof __filename!=='undefined'?createRequire(__filename):typeof import.meta!=='undefined'&&import.meta.url?createRequire(import.meta.url):createRequire(process.cwd());graphqlModule=req('graphql');}catch{graphqlModule=null;}export function extractGraphQLRootFieldFast(queryStr,opName=null){if(!queryStr||typeof queryStr!=='string')return null;const cacheKey=opName?`${opName}::${queryStr}`:queryStr;if(cache.has(cacheKey)){return cache.get(cacheKey);}let result=null;try{if(graphqlModule&&typeof graphqlModule.parse==='function'){const document=graphqlModule.parse(queryStr);let operation=null;if(opName&&typeof graphqlModule.getOperationAST==='function'){operation=graphqlModule.getOperationAST(document,opName);}if(!operation){operation=document.definitions?.find(def=>def.kind==='OperationDefinition'&&(!opName||def.name?.value===opName))||document.definitions?.find(def=>def.kind==='OperationDefinition');}if(operation?.selectionSet?.selections){const rootFields=[];for(const sel of operation.selectionSet.selections){if(sel.kind==='Field'){rootFields.push(sel.name.value);}}if(rootFields.length>0){result=rootFields.join(',');}}}}catch{result=null;}if(cache.size>=MAX_CACHE_SIZE){const firstKey=cache.keys().next().value;if(firstKey)cache.delete(firstKey);}cache.set(cacheKey,result);return result;}
|
package/dist/utils/ipUtils.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export function cleanIp(rawIp){if(!rawIp||typeof rawIp!=='string'){return'127.0.0.1';}const ip=rawIp.trim();if(ip.startsWith('::ffff:')){return ip.slice(7);}if(ip.includes('::')){const parts=ip.split(':');const last=parts[parts.length-1];if(last.includes('.')){return last;}}return ip;}export function ipToInt(ipStr){const ip=cleanIp(ipStr);const parts=ip.split('.');if(parts.length!==4)return null;const a=Number(parts[0]);const b=Number(parts[1]);const c=Number(parts[2]);const d=Number(parts[3]);if(Number.isNaN(a)||Number.isNaN(b)||Number.isNaN(c)||Number.isNaN(d)||a<0||a>255||b<0||b>255||c<0||c>255||d<0||d>255){return null;}return(a<<24|b<<16|c<<8|d)>>>0;}const whitelistCache=new WeakMap();const stringWhitelistCache=new Map();const STRING_CACHE_LIMIT=64;export function compileWhitelistCached(whitelistInput){if(!whitelistInput)return[];if(typeof whitelistInput==='string'){const cached=stringWhitelistCache.get(whitelistInput);if(cached)return cached;const compiled=compileWhitelist(whitelistInput);if(stringWhitelistCache.size>=STRING_CACHE_LIMIT){stringWhitelistCache.clear();}stringWhitelistCache.set(whitelistInput,compiled);return compiled;}if(typeof whitelistInput==='object'){const cached=whitelistCache.get(whitelistInput);const length=Array.isArray(whitelistInput)?whitelistInput.length:-1;if(cached&&cached.length===length&&(!Array.isArray(whitelistInput)||Array.isArray(cached.elements)&&cached.elements.every((el,i)=>el===whitelistInput[i]))){return cached.compiled;}const compiled=compileWhitelist(whitelistInput);const elements=Array.isArray(whitelistInput)?[...whitelistInput]:null;whitelistCache.set(whitelistInput,{length,elements,compiled});return compiled;}return compileWhitelist(whitelistInput);}export function compileWhitelist(whitelistInput){if(!whitelistInput)return[];if(Array.isArray(whitelistInput)&&whitelistInput.length>0&&typeof whitelistInput[0]==='object'&&whitelistInput[0]!==null&&'type'in whitelistInput[0]){return whitelistInput;}const list=Array.isArray(whitelistInput)?whitelistInput:typeof whitelistInput==='string'?whitelistInput.split(','):[];const compiled=[];for(const entry of list){const trimmed=typeof entry==='string'?entry.trim():'';if(!trimmed)continue;if(trimmed.includes('/')){const[subnet,bitsStr]=trimmed.split('/');const bits=Number(bitsStr);const net=ipToInt(subnet);if(net!==null&&!Number.isNaN(bits)&&bits>=0&&bits<=32){const mask=bits===0?0:~0<<32-bits>>>0;compiled.push({type:'cidr',net:(net&mask)>>>0,mask,raw:trimmed});}}else{const net=ipToInt(trimmed);compiled.push({type:'exact',net,cleaned:cleanIp(trimmed),raw:trimmed});}}return compiled;}export function isIpInWhitelist(ipAddr,compiledWhitelist){if(!ipAddr||!compiledWhitelist||compiledWhitelist.length===0){return false;}const rawAddresses=Array.isArray(ipAddr)?ipAddr:typeof ipAddr==='string'?ipAddr.split(','):[ipAddr];for(const raw of rawAddresses){const cleaned=cleanIp(raw);const intVal=ipToInt(cleaned);for(const rule of compiledWhitelist){if(rule.type==='cidr'){if(intVal!==null&&(intVal&rule.mask)>>>0===rule.net){return true;}}else if(rule.type==='exact'){if(intVal!==null&&rule.net!==null){if(intVal===rule.net)return true;}else if(cleaned===rule.cleaned){return true;}}}}return false;}export function validateIpRangeV4(ipAddr,whitelist){if(!ipAddr)return false;const wl=whitelist!==undefined?whitelist:process.env['Platform.WHITELIST_IPS'];const compiled=compileWhitelistCached(wl);return isIpInWhitelist(ipAddr,compiled);}export function validateSecondaryIps(req,whitelist,options={}){if(!req)return false;try{const wl=whitelist!==undefined?whitelist:process.env['Platform.WHITELIST_IPS'];const compiled=compileWhitelistCached(wl);const isTrusted=Boolean(options.trustProxy??req.trustProxy??(req.app&&typeof req.app.get==='function'&&req.app.get('trust proxy'))??(process.env.TRUST_PROXY==='true'||process.env.TRUSTED_PROXY==='true'));const headers=req.headers||{};const xcld=headers['x-xcld'];if(isTrusted&&(xcld===true||xcld==='true')){const realIp=headers['x-real-ip'];if(realIp&&isIpInWhitelist(realIp,compiled)){return true;}const xForwardedFor=headers['x-forwarded-for'];if(xForwardedFor){const clientXff=typeof xForwardedFor==='string'?xForwardedFor.split(',')[0].trim():'';if(clientXff&&isIpInWhitelist(clientXff,compiled)){return true;}}return false;}const clientIp=req.ip||req.socket&&req.socket.remoteAddress||'';return isIpInWhitelist(clientIp,compiled);}catch{return false;}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import{extractGraphQLRootFieldFast}from'./graphQLParser.js';export function normalizeIp(ip){if(!ip||typeof ip!=='string'){return'127.0.0.1';}const clean=ip.trim();if(clean.startsWith('::ffff:')){return clean.slice(7);}return clean;}export function ipKeyGenerator(req){if(!req)return'127.0.0.1';const ip=req.ip||req.socket&&req.socket.remoteAddress||'127.0.0.1';return normalizeIp(ip);}function normalizePathSegments(path){if(!path||typeof path!=='string')return'';return path.replace(/\/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}(?=\/|$)/g,'/:id').replace(/\/[0-9a-fA-F]{24}(?=\/|$)/g,'/:id').replace(/\/\d+(?=\/|$)/g,'/:id');}export function getEndpoint(req){if(!req)return'';if(req.route&&req.route.path){const routePath=typeof req.route.path==='string'?req.route.path:String(req.route.path);return req.baseUrl?`${req.baseUrl}${routePath}`:routePath;}if(req.app&&req.app._router&&Array.isArray(req.app._router.stack)){const pathToCheck=req.path||(req.url?req.url.split('?')[0]:'');for(const layer of req.app._router.stack){if(layer.route&&layer.regexp&&layer.regexp.test(pathToCheck)){const routePath=typeof layer.route.path==='string'?layer.route.path:String(layer.route.path);return req.baseUrl?`${req.baseUrl}${routePath}`:routePath;}}}const rawPath=req.baseUrl?`${req.baseUrl}${req.path||''}`:req.path||(req.originalUrl?req.originalUrl.split('?')[0]:'')||'';return normalizePathSegments(rawPath);}export function defaultKeyGenerator(req){const ip=ipKeyGenerator(req);const endpoint=getEndpoint(req);return endpoint?`${ip}:${endpoint}`:ip;}export function graphQLKeyGenerator(req){const cleanIp=ipKeyGenerator(req);let path=getEndpoint(req);if(path.toLowerCase().includes('graphql')){let body=req.body;if(typeof body==='string'){try{body=JSON.parse(body);}catch{body=null;}}const queryStr=body?.query||req.query?.query;const opName=body?.operationName&&typeof body.operationName==='string'&&body.operationName.trim()?body.operationName.trim():null;const gqlField=extractGraphQLRootFieldFast(queryStr,opName);if(gqlField){path+=`:${gqlField}`;}}const method=req?.method?`${req.method}:`:'';return`${cleanIp}:${method}${path}`;}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import crypto from'crypto';export const REDIS_CLIENT_KIND={IOREDIS:'ioredis',NODE_REDIS_V3:'node-redis-v3',NODE_REDIS_V4:'node-redis-v4',UNKNOWN:'unknown'};export function detectRedisClientKind(client){if(!client||typeof client!=='object'){return REDIS_CLIENT_KIND.UNKNOWN;}if(typeof client.isReady==='boolean'){return REDIS_CLIENT_KIND.NODE_REDIS_V4;}if(typeof client.status==='string'){return REDIS_CLIENT_KIND.IOREDIS;}if(typeof client.send_command==='function'&&typeof client.sendCommand!=='function'){return REDIS_CLIENT_KIND.NODE_REDIS_V3;}if((typeof client.ready==='boolean'||typeof client.connected==='boolean')&&typeof client.status!=='string'&&typeof client.isReady!=='boolean'){return REDIS_CLIENT_KIND.NODE_REDIS_V3;}if(typeof client.isReady==='function'){return REDIS_CLIENT_KIND.NODE_REDIS_V3;}if(typeof client.sendCommand==='function'){return REDIS_CLIENT_KIND.NODE_REDIS_V4;}return REDIS_CLIENT_KIND.UNKNOWN;}function invokeClientMethod(client,method,args){return new Promise((resolve,reject)=>{let settled=false;const cb=(err,res)=>{if(settled)return;settled=true;if(err)reject(err);else resolve(res);};try{const res=client[method](...args,cb);if(res&&typeof res.then==='function'){settled=true;res.then(resolve,reject);}}catch(err){if(!settled){settled=true;reject(err);}}});}export class RedisClientAdapter{constructor(client,options={}){this.client=client;this.kind=options.clientKind||detectRedisClientKind(client);this._scriptFallback=new Map();}usesObjectEval(){return this.kind===REDIS_CLIENT_KIND.NODE_REDIS_V4;}static scriptSha(script){return crypto.createHash('sha1').update(script).digest('hex');}async evalScript(script,keys=[],args=[]){const stringArgs=args.map(a=>String(a));const sha=RedisClientAdapter.scriptSha(script);const useObjectForm=this.usesObjectEval();if(!this._scriptFallback.get(script)){if(typeof this.client.evalsha==='function'){try{return await this._callEvalsha(sha,keys,stringArgs,useObjectForm);}catch(err){if(!isNoScriptError(err)){throw err;}this._scriptFallback.set(script,true);}}else{this._scriptFallback.set(script,true);}}const res=await this._callEval(script,keys,stringArgs,useObjectForm);this._scriptFallback.delete(script);return res;}async _callEvalsha(sha,keys,stringArgs,useObjectForm){if(useObjectForm){return this.client.evalsha(sha,{keys,arguments:stringArgs});}return invokeClientMethod(this.client,'evalsha',[sha,keys.length,...keys,...stringArgs]);}async _callEval(script,keys,stringArgs,useObjectForm){if(typeof this.client.eval==='function'){if(useObjectForm){return this.client.eval(script,{keys,arguments:stringArgs});}return invokeClientMethod(this.client,'eval',[script,keys.length,...keys,...stringArgs]);}if(typeof this.client.sendCommand==='function'){const command=['EVAL',script,String(keys.length),...keys,...stringArgs];return this._sendCommand(command);}if(typeof this.client.send_command==='function'){return invokeClientMethod(this.client,'send_command',['EVAL',[script,keys.length,...keys,...stringArgs]]);}throw new Error('Redis client does not support eval, evalsha, or sendCommand');}async sendCommand(command){return this._sendCommand(command);}async _sendCommand(command){if(this.kind===REDIS_CLIENT_KIND.NODE_REDIS_V4){return this.client.sendCommand(command);}if(typeof this.client.sendCommand==='function'){return this.client.sendCommand(...command);}if(typeof this.client.send_command==='function'){const[cmd,...args]=command;return invokeClientMethod(this.client,'send_command',[cmd,args]);}throw new Error('Redis client does not support sendCommand or send_command');}async getWithTtl(key){let value;if(typeof this.client.get==='function'){value=await invokeClientMethod(this.client,'get',[key]);}else if(typeof this.client.sendCommand==='function'||typeof this.client.send_command==='function'){value=await this._sendCommand(['GET',key]);}else{throw new Error('Redis client does not support get or sendCommand');}if(value===null||value===undefined){return null;}const ttlMs=await this.pttl(key);return{value,ttlMs};}async pttl(key){if(typeof this.client.pttl==='function'){return invokeClientMethod(this.client,'pttl',[key]);}if(typeof this.client.pTTL==='function'){return invokeClientMethod(this.client,'pTTL',[key]);}if(typeof this.client.sendCommand==='function'||typeof this.client.send_command==='function'){return this._sendCommand(['PTTL',key]);}throw new Error('Redis client does not support pttl or sendCommand');}async del(key){let count;if(typeof this.client.del==='function'){count=await invokeClientMethod(this.client,'del',[key]);}else if(typeof this.client.sendCommand==='function'||typeof this.client.send_command==='function'){count=await this._sendCommand(['DEL',key]);}else{throw new Error('Redis client does not support del or sendCommand');}return count>0;}async delMany(keys){if(!keys||keys.length===0){return 0;}if(typeof this.client.del==='function'){return this.kind===REDIS_CLIENT_KIND.NODE_REDIS_V4?this.client.del(keys):invokeClientMethod(this.client,'del',keys);}if(typeof this.client.sendCommand==='function'||typeof this.client.send_command==='function'){return this._sendCommand(['DEL',...keys]);}throw new Error('Redis client does not support del or sendCommand');}async setWithTtl(key,value,ttlMs){const hasTtl=typeof ttlMs==='number'&&ttlMs>0;if(this.kind===REDIS_CLIENT_KIND.NODE_REDIS_V4){if(typeof this.client.set==='function'){return hasTtl?this.client.set(key,value,{PX:ttlMs}):this.client.set(key,value);}if(typeof this.client.sendCommand==='function'){const cmd=hasTtl?['SET',key,value,'PX',String(ttlMs)]:['SET',key,value];return this._sendCommand(cmd);}}if(this.kind===REDIS_CLIENT_KIND.IOREDIS||this.kind===REDIS_CLIENT_KIND.NODE_REDIS_V3){if(typeof this.client.set==='function'){if(!hasTtl){return invokeClientMethod(this.client,'set',[key,value]);}return invokeClientMethod(this.client,'set',[key,value,'PX',ttlMs]);}}if(typeof this.client.sendCommand==='function'||typeof this.client.send_command==='function'){const cmd=hasTtl?['SET',key,value,'PX',String(ttlMs)]:['SET',key,value];return this._sendCommand(cmd);}if(typeof this.client.set==='function'){if(!hasTtl){return invokeClientMethod(this.client,'set',[key,value]);}try{const res=await this.client.set(key,value,{PX:ttlMs});if(res!==undefined)return res;}catch{}return invokeClientMethod(this.client,'set',[key,value,'PX',ttlMs]);}throw new Error('Redis client does not support set or sendCommand');}async scanKeys(pattern){if(typeof this.client.scan==='function'){const keys=[];let cursor='0';do{let reply;if(this.kind===REDIS_CLIENT_KIND.NODE_REDIS_V4){reply=await this.client.scan(cursor,{MATCH:pattern,COUNT:100});}else{reply=await invokeClientMethod(this.client,'scan',[cursor,'MATCH',pattern,'COUNT',100]);}if(Array.isArray(reply)){cursor=String(reply[0]);if(Array.isArray(reply[1])){keys.push(...reply[1]);}}else if(reply&&typeof reply==='object'){cursor=String(reply.cursor);if(Array.isArray(reply.keys)){keys.push(...reply.keys);}}else{break;}}while(cursor!=='0');return keys;}if(typeof this.client.sendCommand==='function'||typeof this.client.send_command==='function'){const keys=[];let cursor='0';do{const reply=await this._sendCommand(['SCAN',cursor,'MATCH',pattern,'COUNT','100']);if(Array.isArray(reply)){cursor=String(reply[0]);if(Array.isArray(reply[1])){keys.push(...reply[1]);}}else if(reply&&typeof reply==='object'){cursor=String(reply.cursor);if(Array.isArray(reply.keys)){keys.push(...reply.keys);}}else{break;}}while(cursor!=='0');return keys;}return[];}isReady(rlKey,isReadonly){if(this.kind===REDIS_CLIENT_KIND.IOREDIS){if(this.client.status){return this.client.status==='ready';}return true;}if(this.kind===REDIS_CLIENT_KIND.NODE_REDIS_V4){if(typeof this.client.isOpen==='boolean'&&this.client.isOpen!==true){return false;}if(this.client._slots&&typeof this.client._slots.getClient==='function'){try{const slotClient=this.client._slots.getClient(rlKey,isReadonly);return Boolean(slotClient&&slotClient.isReady===true);}catch{return false;}}return this.client.isReady!==false;}if(this.kind===REDIS_CLIENT_KIND.NODE_REDIS_V3){if(typeof this.client.isReady==='function'){return this.client.isReady();}if(typeof this.client.ready==='boolean'){return this.client.ready;}if(typeof this.client.connected==='boolean'){return this.client.connected;}}return true;}}export function isNoScriptError(err){return Boolean(err&&typeof err.message==='string'&&err.message.includes('NOSCRIPT'));}export default RedisClientAdapter;
|
package/dist/utils/retryAfter.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
export function calculateRetryAfterSeconds(resetTime,now=Date.now()){const resetMs=resetTime instanceof Date?resetTime.getTime():Number(resetTime);const diffMs=resetMs-now;if(diffMs<=0)return 1;return Math.ceil(diffMs/1000);}
|