{"version":3,"file":"Timer.cjs","sourceRoot":"","sources":["../../src/snaps/Timer.ts"],"names":[],"mappings":"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","sourcesContent":["import { assert } from '@metamask/utils';\n\nexport type TimerStatus = 'stopped' | 'paused' | 'running' | 'finished';\n\nexport class Timer {\n  // TODO: Either fix this lint violation or explain why it's necessary to\n  //  ignore.\n  // eslint-disable-next-line no-restricted-syntax\n  private state:\n    | { value: 'stopped'; remaining: number }\n    | {\n        value: 'paused';\n        remaining: number;\n        callback: () => void;\n      }\n    | {\n        value: 'running';\n        remaining: number;\n        callback: () => void;\n        start: number;\n        timeout?: unknown;\n      }\n    | { value: 'finished'; remaining: number };\n\n  /**\n   * If `ms` is smaller or equal to zero (including -Infinity), the callback is added to the event loop and executed async immediately\n   * If `ms` is +Infinity the timer never finishes.\n   *\n   * @throws {@link TypeError}. If `ms` is NaN or negative.\n   * @param ms - The number of milliseconds before the callback is called after started.\n   */\n  constructor(ms: number) {\n    assert(\n      !Number.isNaN(ms),\n      new TypeError(\"Can't start a timer with NaN time\"),\n    );\n    assert(ms >= 0, new TypeError(\"Can't start a timer with negative time\"));\n    this.state = { value: 'stopped', remaining: ms };\n  }\n\n  get status(): TimerStatus {\n    return this.state.value;\n  }\n\n  get remaining(): number {\n    return this.state.remaining;\n  }\n\n  /**\n   * Cancels the currently running timer and marks it finished.\n   *\n   * @throws {@link Error}. If it wasn't running or paused.\n   */\n  cancel() {\n    assert(\n      this.status === 'paused' || this.status === 'running',\n      'Tried to cancel a not running Timer',\n    );\n    this.onFinish(false);\n  }\n\n  /**\n   * Marks the timer as finished prematurely and triggers the callback.\n   *\n   * @throws {@link Error}. If it wasn't running or paused.\n   */\n  finish() {\n    assert(this.status !== 'finished', 'Tried to finish a finished Timer.');\n    this.onFinish(true);\n  }\n\n  /**\n   * Pauses a currently running timer, allowing it to resume later.\n   *\n   * @throws {@link Error}. If it wasn't running.\n   */\n  pause() {\n    assert(\n      this.state.value === 'running',\n      'Tried to pause a not running Timer',\n    );\n\n    const { callback, start, timeout, remaining } = this.state;\n\n    timeout !== undefined && clearTimeout(timeout as any);\n    this.state = {\n      value: 'paused',\n      callback,\n      remaining: remaining - (Date.now() - start),\n    };\n  }\n\n  /**\n   * Starts the timer.\n   *\n   * @param callback - The function that will be called after the timer finishes.\n   * @throws {@link Error}. If it was already started.\n   */\n  start(callback: () => void) {\n    assert(\n      this.state.value === 'stopped',\n      'Tried to start an already running Timer',\n    );\n\n    const { remaining } = this.state;\n    this.state = { value: 'paused', remaining, callback };\n    this.resume();\n  }\n\n  /**\n   * Resumes a currently paused timer.\n   *\n   * @throws {@link Error}. If it wasn't paused.\n   */\n  resume() {\n    assert(this.state.value === 'paused', 'Tried to resume not paused Timer');\n    const { remaining, callback } = this.state;\n    const start = Date.now();\n\n    let timeout: unknown;\n    // setTimeout returns immediately on +Infinity which we use\n    if (remaining !== Number.POSITIVE_INFINITY) {\n      timeout = setTimeout(() => this.onFinish(true), remaining);\n    }\n\n    this.state = { value: 'running', callback, remaining, start, timeout };\n  }\n\n  // TODO: Either fix this lint violation or explain why it's necessary to\n  //  ignore.\n  // eslint-disable-next-line no-restricted-syntax\n  private onFinish(shouldCall: boolean) {\n    assert(this.state.value === 'running' || this.state.value === 'paused');\n\n    if (this.state.value === 'running' && this.state.timeout !== undefined) {\n      clearTimeout(this.state.timeout as any);\n    }\n\n    const { callback, remaining } = this.state;\n    this.state = {\n      value: 'finished',\n      remaining:\n        this.state.value === 'running'\n          ? remaining - (Date.now() - this.state.start)\n          : remaining,\n    };\n\n    if (shouldCall) {\n      callback();\n    }\n  }\n}\n"]}