hanzio 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.
Files changed (51) hide show
  1. package/README.md +510 -0
  2. package/dist/api-wrapper/index.d.ts +84 -0
  3. package/dist/api-wrapper/index.js +250 -0
  4. package/dist/array/index.d.ts +22 -0
  5. package/dist/cache/index.d.ts +19 -0
  6. package/dist/date/index.d.ts +11 -0
  7. package/dist/gzip/index.d.ts +2 -0
  8. package/dist/gzip/index.js +16 -0
  9. package/dist/index.d.ts +12 -0
  10. package/dist/index.js +1565 -0
  11. package/dist/jwt/index.d.ts +3 -0
  12. package/dist/jwt/index.js +19 -0
  13. package/dist/math/index.d.ts +2 -0
  14. package/dist/math/linearRegressionTrend.d.ts +5 -0
  15. package/dist/math/metricsSeriesColors.d.ts +42 -0
  16. package/dist/p-queue/index.d.ts +95 -0
  17. package/dist/p-queue/index.js +439 -0
  18. package/dist/promise/index.d.ts +21 -0
  19. package/dist/sitemap/index.d.ts +14 -0
  20. package/dist/sitemap/index.js +552 -0
  21. package/dist/state/index.d.ts +101 -0
  22. package/dist/string/index.d.ts +8 -0
  23. package/dist/typedswitch/index.d.ts +124 -0
  24. package/dist/url/index.d.ts +1 -0
  25. package/dist/zod/index.d.ts +8 -0
  26. package/dist/zod/index.js +210 -0
  27. package/dist/zod/jsonToZodSchemaMapper.d.ts +6 -0
  28. package/dist/zod/zodMinMaxFilter.d.ts +14 -0
  29. package/dist/zod/zodToTypeString.d.ts +9 -0
  30. package/package.json +102 -0
  31. package/src/api-wrapper/index.ts +492 -0
  32. package/src/array/index.ts +187 -0
  33. package/src/cache/index.ts +154 -0
  34. package/src/date/index.ts +54 -0
  35. package/src/gzip/index.ts +29 -0
  36. package/src/index.ts +12 -0
  37. package/src/jwt/index.ts +25 -0
  38. package/src/math/index.ts +11 -0
  39. package/src/math/linearRegressionTrend.ts +36 -0
  40. package/src/math/metricsSeriesColors.ts +208 -0
  41. package/src/p-queue/index.ts +690 -0
  42. package/src/promise/index.ts +73 -0
  43. package/src/sitemap/index.ts +195 -0
  44. package/src/state/index.ts +331 -0
  45. package/src/string/index.ts +86 -0
  46. package/src/typedswitch/index.ts +309 -0
  47. package/src/url/index.ts +5 -0
  48. package/src/zod/index.ts +29 -0
  49. package/src/zod/jsonToZodSchemaMapper.ts +44 -0
  50. package/src/zod/zodMinMaxFilter.ts +28 -0
  51. package/src/zod/zodToTypeString.ts +222 -0
@@ -0,0 +1,690 @@
1
+ export type RunFunction = () => Promise<unknown> | unknown
2
+
3
+ export type Queue<Element, Options> = {
4
+ size: number
5
+ filter: (options: Readonly<Partial<Options>>) => Element[]
6
+ dequeue: () => Element | undefined
7
+ enqueue: (run: Element, options?: Partial<Options>) => void
8
+ setPriority: (id: string, priority: number) => void
9
+ remove?: (id: string) => void
10
+ }
11
+
12
+ export type TaskOptions = {
13
+ readonly signal?: AbortSignal | undefined
14
+ }
15
+
16
+ type TimeoutOptions = {
17
+ timeout?: number
18
+ }
19
+
20
+ export type QueueAddOptions = {
21
+ readonly priority?: number
22
+ id?: string
23
+ } & TaskOptions &
24
+ TimeoutOptions
25
+
26
+ export type Options<
27
+ QueueType extends Queue<RunFunction, QueueOptions> = PriorityQueue,
28
+ QueueOptions extends QueueAddOptions = QueueAddOptions
29
+ > = {
30
+ readonly concurrency?: number
31
+ readonly autoStart?: boolean
32
+ readonly queueClass?: new () => QueueType
33
+ readonly intervalCap?: number
34
+ readonly interval?: number
35
+ readonly carryoverIntervalCount?: boolean
36
+ readonly carryoverConcurrencyCount?: boolean
37
+ readonly strict?: boolean
38
+ } & TimeoutOptions
39
+
40
+ type Task<TaskResultType> =
41
+ | ((options: TaskOptions) => PromiseLike<TaskResultType>)
42
+ | ((options: TaskOptions) => TaskResultType)
43
+
44
+ type EventMap = {
45
+ active: []
46
+ idle: []
47
+ empty: []
48
+ add: []
49
+ next: []
50
+ completed: [unknown]
51
+ error: [unknown]
52
+ pendingZero: []
53
+ rateLimit: []
54
+ rateLimitCleared: []
55
+ }
56
+
57
+ type EventName = keyof EventMap
58
+ type Listener<Event extends EventName> = (
59
+ ...arguments_: EventMap[Event]
60
+ ) => void
61
+
62
+ export class TimeoutError extends Error {
63
+ constructor(message: string) {
64
+ super(message)
65
+ this.name = 'TimeoutError'
66
+ }
67
+ }
68
+
69
+ export type PriorityQueueOptions = {
70
+ priority?: number
71
+ } & QueueAddOptions
72
+
73
+ export class PriorityQueue implements Queue<RunFunction, PriorityQueueOptions> {
74
+ readonly #queue: Array<PriorityQueueOptions & { run: RunFunction }> = []
75
+
76
+ enqueue(run: RunFunction, options: Partial<PriorityQueueOptions> = {}): void {
77
+ const element = {
78
+ priority: options.priority ?? 0,
79
+ id: options.id,
80
+ run
81
+ }
82
+
83
+ const index = this.#queue.findIndex(
84
+ (item) => item.priority! < element.priority
85
+ )
86
+
87
+ if (index === -1) {
88
+ this.#queue.push(element)
89
+ return
90
+ }
91
+
92
+ this.#queue.splice(index, 0, element)
93
+ }
94
+
95
+ dequeue(): RunFunction | undefined {
96
+ return this.#queue.shift()?.run
97
+ }
98
+
99
+ filter(options: Readonly<Partial<PriorityQueueOptions>>): RunFunction[] {
100
+ return this.#queue
101
+ .filter((item) =>
102
+ Object.entries(options).every(
103
+ ([key, value]) => item[key as keyof PriorityQueueOptions] === value
104
+ )
105
+ )
106
+ .map((item) => item.run)
107
+ }
108
+
109
+ setPriority(id: string, priority: number): void {
110
+ const index = this.#queue.findIndex((item) => item.id === id)
111
+
112
+ if (index === -1) {
113
+ throw new ReferenceError(
114
+ `No promise function with the id "${id}" exists in the queue.`
115
+ )
116
+ }
117
+
118
+ const [item] = this.#queue.splice(index, 1)
119
+ this.enqueue(item!.run, { ...item, priority })
120
+ }
121
+
122
+ remove(id: string): void {
123
+ const index = this.#queue.findIndex((item) => item.id === id)
124
+
125
+ if (index !== -1) {
126
+ this.#queue.splice(index, 1)
127
+ }
128
+ }
129
+
130
+ get size(): number {
131
+ return this.#queue.length
132
+ }
133
+ }
134
+
135
+ export default class PQueue<
136
+ QueueType extends Queue<RunFunction, EnqueueOptionsType> = PriorityQueue,
137
+ EnqueueOptionsType extends QueueAddOptions = QueueAddOptions
138
+ > {
139
+ readonly #listeners = new Map<EventName, Set<Listener<EventName>>>()
140
+ #queue: QueueType
141
+ readonly #queueClass: new () => QueueType
142
+ #pending = 0
143
+ #concurrency: number
144
+ #isPaused: boolean
145
+ readonly #intervalCap: number
146
+ readonly #interval: number
147
+ readonly #carryoverIntervalCount: boolean
148
+ readonly #strict: boolean
149
+ #intervalCount = 0
150
+ #intervalEnd = 0
151
+ #intervalTimer?: ReturnType<typeof setTimeout>
152
+ #strictTimestamps: number[] = []
153
+ #rateLimited = false
154
+ #idCounter = 1n
155
+ readonly #runningTasks = new Map<
156
+ symbol,
157
+ {
158
+ id?: string
159
+ priority: number
160
+ startTime: number
161
+ timeout?: number
162
+ }
163
+ >()
164
+ timeout?: number
165
+
166
+ constructor(options: Options<QueueType, EnqueueOptionsType> = {}) {
167
+ const {
168
+ concurrency = Number.POSITIVE_INFINITY,
169
+ autoStart = true,
170
+ queueClass = PriorityQueue as unknown as new () => QueueType,
171
+ intervalCap = Number.POSITIVE_INFINITY,
172
+ interval = 0,
173
+ carryoverIntervalCount,
174
+ carryoverConcurrencyCount,
175
+ strict = false,
176
+ timeout
177
+ } = options
178
+
179
+ if (!(typeof concurrency === 'number' && concurrency >= 1)) {
180
+ throw new TypeError('Expected `concurrency` to be a number from 1 and up')
181
+ }
182
+
183
+ if (!(typeof intervalCap === 'number' && intervalCap >= 1)) {
184
+ throw new TypeError('Expected `intervalCap` to be a number from 1 and up')
185
+ }
186
+
187
+ if (
188
+ !(
189
+ typeof interval === 'number' &&
190
+ Number.isFinite(interval) &&
191
+ interval >= 0
192
+ )
193
+ ) {
194
+ throw new TypeError('Expected `interval` to be a finite number >= 0')
195
+ }
196
+
197
+ if (strict && interval === 0) {
198
+ throw new TypeError('The `strict` option requires a non-zero `interval`')
199
+ }
200
+
201
+ if (strict && intervalCap === Number.POSITIVE_INFINITY) {
202
+ throw new TypeError('The `strict` option requires a finite `intervalCap`')
203
+ }
204
+
205
+ if (timeout !== undefined && !(Number.isFinite(timeout) && timeout > 0)) {
206
+ throw new TypeError('Expected `timeout` to be a positive finite number')
207
+ }
208
+
209
+ this.#queueClass = queueClass
210
+ this.#queue = new queueClass()
211
+ this.#concurrency = concurrency
212
+ this.#isPaused = !autoStart
213
+ this.#intervalCap = intervalCap
214
+ this.#interval = interval
215
+ this.#carryoverIntervalCount =
216
+ carryoverIntervalCount ?? carryoverConcurrencyCount ?? false
217
+ this.#strict = strict
218
+ this.timeout = timeout
219
+ }
220
+
221
+ on<Event extends EventName>(event: Event, listener: Listener<Event>): this {
222
+ const listeners = this.#listeners.get(event) ?? new Set()
223
+ listeners.add(listener as Listener<EventName>)
224
+ this.#listeners.set(event, listeners)
225
+ return this
226
+ }
227
+
228
+ off<Event extends EventName>(event: Event, listener: Listener<Event>): this {
229
+ this.#listeners.get(event)?.delete(listener as Listener<EventName>)
230
+ return this
231
+ }
232
+
233
+ emit<Event extends EventName>(
234
+ event: Event,
235
+ ...arguments_: EventMap[Event]
236
+ ): boolean {
237
+ const listeners = this.#listeners.get(event)
238
+
239
+ if (!listeners || listeners.size === 0) {
240
+ return false
241
+ }
242
+
243
+ for (const listener of [...listeners]) {
244
+ listener(...(arguments_ as EventMap[EventName]))
245
+ }
246
+
247
+ return true
248
+ }
249
+
250
+ get concurrency(): number {
251
+ return this.#concurrency
252
+ }
253
+
254
+ set concurrency(newConcurrency: number) {
255
+ if (!(typeof newConcurrency === 'number' && newConcurrency >= 1)) {
256
+ throw new TypeError('Expected `concurrency` to be a number from 1 and up')
257
+ }
258
+
259
+ this.#concurrency = newConcurrency
260
+ this.#processQueue()
261
+ }
262
+
263
+ get size(): number {
264
+ return this.#queue.size
265
+ }
266
+
267
+ sizeBy(options: Readonly<Partial<EnqueueOptionsType>>): number {
268
+ return this.#queue.filter(options).length
269
+ }
270
+
271
+ get pending(): number {
272
+ return this.#pending
273
+ }
274
+
275
+ get isPaused(): boolean {
276
+ return this.#isPaused
277
+ }
278
+
279
+ get isRateLimited(): boolean {
280
+ this.#updateRateLimitState()
281
+ return this.#rateLimited
282
+ }
283
+
284
+ get isSaturated(): boolean {
285
+ return (
286
+ (this.#pending >= this.#concurrency && this.#queue.size > 0) ||
287
+ (this.isRateLimited && this.#queue.size > 0)
288
+ )
289
+ }
290
+
291
+ get runningTasks(): ReadonlyArray<{
292
+ readonly id?: string
293
+ readonly priority: number
294
+ readonly startTime: number
295
+ readonly timeout?: number
296
+ }> {
297
+ return [...this.#runningTasks.values()].map((task) => ({ ...task }))
298
+ }
299
+
300
+ setPriority(id: string, priority: number): void {
301
+ if (typeof priority !== 'number' || !Number.isFinite(priority)) {
302
+ throw new TypeError('Expected `priority` to be a finite number')
303
+ }
304
+
305
+ this.#queue.setPriority(id, priority)
306
+ }
307
+
308
+ async add<TaskResultType>(
309
+ function_: Task<TaskResultType>,
310
+ options: Partial<EnqueueOptionsType> = {}
311
+ ): Promise<TaskResultType> {
312
+ const taskOptions = {
313
+ timeout: this.timeout,
314
+ ...options,
315
+ id: options.id ?? (this.#idCounter++).toString()
316
+ } as Partial<EnqueueOptionsType> & Required<Pick<QueueAddOptions, 'id'>>
317
+
318
+ return new Promise<TaskResultType>((resolve, reject) => {
319
+ const taskSymbol = Symbol(taskOptions.id)
320
+ let didRun = false
321
+ let queuedAbortHandler: (() => void) | undefined
322
+ let runningAbortHandler: (() => void) | undefined
323
+
324
+ const cleanupQueuedAbort = () => {
325
+ if (queuedAbortHandler) {
326
+ taskOptions.signal?.removeEventListener('abort', queuedAbortHandler)
327
+ queuedAbortHandler = undefined
328
+ }
329
+ }
330
+
331
+ const run = async () => {
332
+ didRun = true
333
+ cleanupQueuedAbort()
334
+ this.#pending++
335
+ this.#runningTasks.set(taskSymbol, {
336
+ id: taskOptions.id,
337
+ priority: taskOptions.priority ?? 0,
338
+ startTime: Date.now(),
339
+ timeout: taskOptions.timeout
340
+ })
341
+
342
+ try {
343
+ taskOptions.signal?.throwIfAborted()
344
+ const operation = Promise.resolve(
345
+ function_({ signal: taskOptions.signal })
346
+ )
347
+ const result = await this.#withTimeoutAndAbort(
348
+ operation,
349
+ taskOptions,
350
+ (listener) => {
351
+ runningAbortHandler = listener
352
+ }
353
+ )
354
+ resolve(result)
355
+ this.emit('completed', result)
356
+ } catch (error) {
357
+ reject(error)
358
+ this.emit('error', error)
359
+ } finally {
360
+ if (runningAbortHandler) {
361
+ taskOptions.signal?.removeEventListener(
362
+ 'abort',
363
+ runningAbortHandler
364
+ )
365
+ }
366
+
367
+ this.#runningTasks.delete(taskSymbol)
368
+ queueMicrotask(() => {
369
+ this.#next()
370
+ })
371
+ }
372
+ }
373
+
374
+ if (taskOptions.signal?.aborted) {
375
+ reject(taskOptions.signal.reason)
376
+ return
377
+ }
378
+
379
+ if (taskOptions.signal) {
380
+ queuedAbortHandler = () => {
381
+ if (didRun) {
382
+ return
383
+ }
384
+
385
+ this.#queue.remove?.(taskOptions.id)
386
+ reject(taskOptions.signal?.reason)
387
+ this.emit('next')
388
+ this.#tryToStartAnother()
389
+ }
390
+ taskOptions.signal.addEventListener('abort', queuedAbortHandler, {
391
+ once: true
392
+ })
393
+ }
394
+
395
+ this.#queue.enqueue(run, taskOptions)
396
+ this.emit('add')
397
+ this.#processQueue()
398
+ })
399
+ }
400
+
401
+ async addAll<TaskResultsType>(
402
+ functions: ReadonlyArray<Task<TaskResultsType>>,
403
+ options?: Partial<EnqueueOptionsType>
404
+ ): Promise<TaskResultsType[]> {
405
+ return Promise.all(
406
+ functions.map(async (function_) => this.add(function_, options))
407
+ )
408
+ }
409
+
410
+ start(): this {
411
+ if (!this.#isPaused) {
412
+ return this
413
+ }
414
+
415
+ this.#isPaused = false
416
+ this.#processQueue()
417
+ return this
418
+ }
419
+
420
+ pause(): void {
421
+ this.#isPaused = true
422
+ }
423
+
424
+ clear(): void {
425
+ this.#queue = new this.#queueClass()
426
+ this.#clearIntervalTimer()
427
+ this.#updateRateLimitState()
428
+ this.emit('empty')
429
+
430
+ if (this.#pending === 0) {
431
+ this.emit('idle')
432
+ }
433
+
434
+ this.emit('next')
435
+ }
436
+
437
+ async onEmpty(): Promise<void> {
438
+ if (this.#queue.size === 0) {
439
+ return
440
+ }
441
+
442
+ await this.#onEvent('empty')
443
+ }
444
+
445
+ async onIdle(): Promise<void> {
446
+ if (this.#queue.size === 0 && this.#pending === 0) {
447
+ return
448
+ }
449
+
450
+ await this.#onEvent('idle')
451
+ }
452
+
453
+ async onPendingZero(): Promise<void> {
454
+ if (this.#pending === 0) {
455
+ return
456
+ }
457
+
458
+ await this.#onEvent('pendingZero')
459
+ }
460
+
461
+ async onRateLimit(): Promise<void> {
462
+ if (this.isRateLimited) {
463
+ return
464
+ }
465
+
466
+ await this.#onEvent('rateLimit')
467
+ }
468
+
469
+ async onRateLimitCleared(): Promise<void> {
470
+ if (!this.isRateLimited) {
471
+ return
472
+ }
473
+
474
+ await this.#onEvent('rateLimitCleared')
475
+ }
476
+
477
+ onError(): Promise<never> {
478
+ return new Promise<never>((_resolve, reject) => {
479
+ const listener = (error: unknown) => {
480
+ this.off('error', listener)
481
+ reject(error)
482
+ }
483
+
484
+ this.on('error', listener)
485
+ })
486
+ }
487
+
488
+ async onSizeLessThan(limit: number): Promise<void> {
489
+ if (this.#queue.size < limit) {
490
+ return
491
+ }
492
+
493
+ await this.#onEvent('next', () => this.#queue.size < limit)
494
+ }
495
+
496
+ async #onEvent(event: EventName, filter?: () => boolean): Promise<void> {
497
+ return new Promise((resolve) => {
498
+ const listener = () => {
499
+ if (filter && !filter()) {
500
+ return
501
+ }
502
+
503
+ this.off(event, listener)
504
+ resolve()
505
+ }
506
+
507
+ this.on(event, listener)
508
+ })
509
+ }
510
+
511
+ async #withTimeoutAndAbort<TaskResultType>(
512
+ operation: Promise<TaskResultType>,
513
+ options: Partial<EnqueueOptionsType>,
514
+ setAbortListener: (listener: () => void) => void
515
+ ): Promise<TaskResultType> {
516
+ const races: Array<Promise<TaskResultType>> = [operation]
517
+ let timeoutId: ReturnType<typeof setTimeout> | undefined
518
+
519
+ if (options.timeout) {
520
+ races.push(
521
+ new Promise<never>((_resolve, reject) => {
522
+ timeoutId = setTimeout(() => {
523
+ reject(
524
+ new TimeoutError(`Task timed out after ${options.timeout}ms`)
525
+ )
526
+ }, options.timeout)
527
+ })
528
+ )
529
+ }
530
+
531
+ if (options.signal) {
532
+ races.push(
533
+ new Promise<never>((_resolve, reject) => {
534
+ const listener = () => {
535
+ reject(options.signal?.reason)
536
+ }
537
+
538
+ setAbortListener(listener)
539
+ options.signal?.addEventListener('abort', listener, { once: true })
540
+ })
541
+ )
542
+ }
543
+
544
+ try {
545
+ return await Promise.race(races)
546
+ } finally {
547
+ if (timeoutId) {
548
+ clearTimeout(timeoutId)
549
+ }
550
+ }
551
+ }
552
+
553
+ #next(): void {
554
+ this.#pending--
555
+
556
+ if (this.#pending === 0) {
557
+ this.emit('pendingZero')
558
+ }
559
+
560
+ this.#processQueue()
561
+ this.emit('next')
562
+ }
563
+
564
+ #processQueue(): void {
565
+ while (this.#tryToStartAnother()) {}
566
+ this.#updateRateLimitState()
567
+ }
568
+
569
+ #tryToStartAnother(): boolean {
570
+ if (this.#queue.size === 0) {
571
+ this.#clearIntervalTimer()
572
+ this.emit('empty')
573
+
574
+ if (this.#pending === 0) {
575
+ this.emit('idle')
576
+ }
577
+
578
+ return false
579
+ }
580
+
581
+ if (
582
+ this.#isPaused ||
583
+ this.#pending >= this.#concurrency ||
584
+ !this.#doesIntervalAllowAnother()
585
+ ) {
586
+ return false
587
+ }
588
+
589
+ const job = this.#queue.dequeue()
590
+
591
+ if (!job) {
592
+ return false
593
+ }
594
+
595
+ this.#consumeIntervalSlot()
596
+ this.emit('active')
597
+ job()
598
+ return true
599
+ }
600
+
601
+ #doesIntervalAllowAnother(): boolean {
602
+ if (
603
+ this.#intervalCap === Number.POSITIVE_INFINITY ||
604
+ this.#interval === 0
605
+ ) {
606
+ return true
607
+ }
608
+
609
+ const now = Date.now()
610
+
611
+ if (this.#strict) {
612
+ this.#strictTimestamps = this.#strictTimestamps.filter(
613
+ (timestamp) => now - timestamp < this.#interval
614
+ )
615
+
616
+ if (this.#strictTimestamps.length < this.#intervalCap) {
617
+ return true
618
+ }
619
+
620
+ this.#scheduleIntervalResume(
621
+ this.#interval - (now - this.#strictTimestamps[0]!)
622
+ )
623
+ return false
624
+ }
625
+
626
+ if (this.#intervalEnd === 0 || now >= this.#intervalEnd) {
627
+ this.#intervalCount = this.#carryoverIntervalCount ? this.#pending : 0
628
+ this.#intervalEnd = now + this.#interval
629
+ }
630
+
631
+ if (this.#intervalCount < this.#intervalCap) {
632
+ return true
633
+ }
634
+
635
+ this.#scheduleIntervalResume(this.#intervalEnd - now)
636
+ return false
637
+ }
638
+
639
+ #consumeIntervalSlot(): void {
640
+ if (
641
+ this.#intervalCap === Number.POSITIVE_INFINITY ||
642
+ this.#interval === 0
643
+ ) {
644
+ return
645
+ }
646
+
647
+ if (this.#strict) {
648
+ this.#strictTimestamps.push(Date.now())
649
+ return
650
+ }
651
+
652
+ this.#intervalCount++
653
+ }
654
+
655
+ #scheduleIntervalResume(delay: number): void {
656
+ if (this.#intervalTimer) {
657
+ return
658
+ }
659
+
660
+ this.#intervalTimer = setTimeout(
661
+ () => {
662
+ this.#intervalTimer = undefined
663
+ this.#processQueue()
664
+ },
665
+ Math.max(0, delay)
666
+ )
667
+ }
668
+
669
+ #clearIntervalTimer(): void {
670
+ if (this.#intervalTimer) {
671
+ clearTimeout(this.#intervalTimer)
672
+ this.#intervalTimer = undefined
673
+ }
674
+ }
675
+
676
+ #updateRateLimitState(): void {
677
+ const previous = this.#rateLimited
678
+ const isRateLimited =
679
+ this.#queue.size > 0 &&
680
+ this.#intervalCap !== Number.POSITIVE_INFINITY &&
681
+ this.#interval > 0 &&
682
+ !this.#doesIntervalAllowAnother()
683
+
684
+ this.#rateLimited = isRateLimited
685
+
686
+ if (previous !== isRateLimited) {
687
+ this.emit(isRateLimited ? 'rateLimit' : 'rateLimitCleared')
688
+ }
689
+ }
690
+ }