Source: src/sharding/ShardInfo.js

// Copyright 2020 Campbell Crowley. All rights reserved.
// Author: Campbell Crowley (web@campbellcrowley.com)

/**
 * @description Stores information about a single shard necessary for
 * the ShardingMaster to operate.
 * @class
 * @memberof ShardingMaster
 * @inner
 * @static
 */
class ShardInfo {
  /**
   * @description Create new instance.
   * @param {string} id The ID of the shard this status object contains data
   * for.
   */
  constructor(id) {
    if (typeof id !== 'string' || id.length < 3) {
      throw new TypeError('Invalid Shard ID for ShardInfo object: ' + id);
    }
    /**
     * @description The ID of the shard this status object contains data for.
     * @public
     * @type {string}
     */
    this.id = id;
    /**
     * @description The public key identifying this shard, used for signing and
     * verification.
     * @public
     * @type {?string}
     * @default
     */
    this.key = null;
    /**
     * @description Is this shard considered the master shard. There must be
     * exactly one of these configured at all times, and in most cases can run
     * in the same directory as the ShardingMaster. This shard will be told to
     * not connect to Discord, and act as the master node for web requests.
     * @public
     * @type {boolean}
     * @default
     */
    this.isMaster = false;
    /**
     * @description Timestamp of the last time the shard has been seen. This
     * will be 0 if the shard has never attempted to communicate with the
     * master.
     * @public
     * @type {number}
     * @default
     */
    this.lastSeen = 0;
    /**
     * @description The last time at which a valid heartbeat was received from
     * the shard indicating that it is still alive.
     * @public
     * @type {number}
     * @default
     */
    this.lastHeartbeat = 0;
    /**
     * @description The timestamp at which the information for this shard was
     * created, and sent to a sysadmin.
     * @public
     * @type {number}
     * @default
     */
    this.createdTime = Date.now();
    /**
     * @description The most recent timestamp at which the shard was told to
     * boot up.
     * @public
     * @type {number}
     * @default
     */
    this.bootTime = 0;
    /**
     * @description The most recent timestamp at which the shard was told to
     * shut down.
     * @public
     * @type {number}
     * @default
     */
    this.stopTime = 0;
    /**
     * @description The Discord shard ID this shard should be currently. A value
     * less than 0 means the shard should be shutdown.
     * @public
     * @type {number}
     * @default
     */
    this.goalShardId = -1;
    /**
     * @description The Discord shard ID this shard currently is. A value less
     * than 0 means the shard is currently shutdown.
     * @public
     * @type {number}
     * @default
     */
    this.currentShardId = -1;
    /**
     * @description The number of shards intended to be running currently. A
     * value of 1 or less denotes this is the only shard. A default value of -1
     * is used to be similar to the ID values, but any value less than 2 is
     * semantically the identical.
     * @public
     * @type {number}
     * @default
     */
    this.goalShardCount = -1;
    /**
     * @description The number of shards intended to be running according to
     * this shard's current configuration. A value of 1 or less denotes this is
     * the only shard. A default value of -1 is used to be similar to the ID
     * values, but any value less than 2 is semantically the identical.
     * @public
     * @type {number}
     * @default
     */
    this.currentShardCount = -1;
    /**
     * @description ShardStatus instance for this shard if it's been received.
     * @public
     * @type {?ShardingMaster.ShardStatus}
     * @default
     */
    this.stats = null;
  }

  /**
   * @description Get the version of this object that can be converted to a
   * string. Additionally, this will only contain the data intended to be saved
   * to disk. `stats` will be excluded, and is intended to be saved separately.
   * @public
   * @returns {object} Serializable version of ShardInfo.
   */
  get serializable() {
    const all = Object.entries(Object.getOwnPropertyDescriptors(this));
    const output = {};
    for (const one of all) {
      if (typeof one[1].value === 'function' || one[0].startsWith('_')) {
        continue;
      } else if (one[0] === 'stats') {
        continue;
      } else if (one[1].value && one[1].value.serializable) {
        output[one[0]] = one[1].value.serializable;
      } else {
        output[one[0]] = one[1].value;
      }
    }
    return output;
  }

  /**
   * @description Format this object into a request for a shard's state. This
   * does not handle cold stops or full reboots, only modifying running state of
   * the shard.
   * @public
   * @returns {object} Object with minimal data to inform the shard how it
   * should currently be configured.
   */
  request() {
    return {
      id: this.goalShardId,
      count: this.goalShardCount,
      master: this.isMaster,
    };
  }

  /**
   * @description Convert a given object to a ShardInfo object. Values are only
   * copied if their types exactly match.
   * @public
   * @static
   * @param {object} obj The ShardInfo-like object to copy.
   * @param {string} [id] If the given object does not specify the shard's ID,
   * it may be passed here instead.
   * @returns {ShardingMaster.ShardInfo} Created ShardInfo object.
   */
  static from(obj, id) {
    const out = new ShardInfo(id || obj.id);
    for (const prop of Object.keys(out)) {
      if (prop === 'id') continue;
      if (typeof out[prop] === 'function') continue;
      if (out[prop] !== null && typeof out[prop] !== typeof obj[prop]) continue;
      out[prop] = obj[prop];
    }
    return out;
  }
}
module.exports = ShardInfo;