Source: net/slidingwindow.js

'use strict';

const assert = require('bsert');
const AsyncEmitter = require('bevent');

class SlidingWindow extends AsyncEmitter {
  /**
   * Create a sliding window counter
   * e.g:
   * ```
   * new SlidingWindow({
   *   window: 1000,
   *   limit: 100
   * })
   * ```
   * creates a sliding window which allows 100 requests per second
   * @property {Number} window - window period in milliseconds
   * @property {Number} limit - max requests allowed
   * @property {Timeout} timeout - sliding window timeout
   * @property {Number} current - current window counter
   * @property {Number} previous - previous window counter
   * @property {Number} timestamp - current window start time in milliseconds
   */

  constructor(options) {
    super();

    this.window = options.window || 1000;
    this.limit = options.limit || 100;

    this.timeout = null;
    this.current = 0;
    this.previous = 0;
    this.timestamp = 0;
  }

  start() {
    this.timestamp = Date.now();
    this.timeout = setInterval(() => this.reset(), this.window);
  }

  stop() {
    this.timestamp = 0;
    clearInterval(this.timeout);
  }

  async reset() {
    this.previous = this.current;
    this.current = 0;
    this.timestamp = Date.now();
    this.emit('reset');
  }

  score() {
    const ms = Date.now() - this.timestamp;
    let weight = 1 - (ms / this.window);

    if (weight < 0)
      weight = 0;

    return this.previous * weight + this.current;
  }

  increase(count) {
    assert((count >>> 0) === count);
    this.current += count;
  }

  allow() {
    return this.score() < this.limit;
  }
}

module.exports = SlidingWindow;