Repository: hydralauncher/hydra
File: src/main/services/download/js-http-downloader.ts (TypeScript)

Code before:
  private bytesAtLastSpeedUpdate = 0;
  private isDownloading = false;

  private retryCount = 0;
  private lastDataReceivedAt = Date.now();
  private stallCheckInterval: NodeJS.Timeout | null = null;
  private isPaused = false;
  private isStallRetry = false;

  async startDownload(options: JsHttpDownloaderOptions): Promise<void> {
    if (this.isDownloading) {
      logger.log(
        "[JsHttpDownloader] Download already in progress, resuming..."
      );
      return this.resumeDownload();
    }

    this.currentOptions = options;
    this.isPaused = false;
    await this.startDownloadWithRetry();
  }

  private async startDownloadWithRetry(): Promise<void> {
    if (!this.currentOptions) return;

    while (this.retryCount <= MAX_RETRY_ATTEMPTS && !this.isPaused) {
      this.abortController = new AbortController();
      this.status = "active";
      this.isDownloading = true;
      this.isStallRetry = false;
      this.lastDataReceivedAt = Date.now();

      const { url, savePath, filename, headers = {} } = this.currentOptions;
      const { filePath, startByte, usedFallback } = this.prepareDownloadPath(
        savePath,
        filename,
        url
      );
      const requestHeaders = this.buildRequestHeaders(headers, startByte);

      this.startStallDetection();

      try {
        await this.executeDownload(
          url,
          requestHeaders,
          filePath,
          startByte,
          savePath,
          usedFallback
        );

Review comment (bug):
possible off-by-one: `while (this.retryCount <= MAX_RETRY_ATTEMPTS)` will allow 11 total attempts if `retryCount` counts retries (0..10). If you intend “max 10 retries”, prefer `< MAX_RETRY_ATTEMPTS` in the loop condition and keep the same meaning between `retryCount` and the constant.

Code after:
  private bytesAtLastSpeedUpdate = 0;
  private isDownloading = false;

  private retryCount = 0;
  private lastDataReceivedAt = Date.now();
  private stallCheckInterval: NodeJS.Timeout | null = null;
  private isPaused = false;
  private isStallRetry = false;

  async startDownload(options: JsHttpDownloaderOptions): Promise<void> {
    if (this.isDownloading) {
      logger.log(
        "[JsHttpDownloader] Download already in progress, resuming..."
      );
      return this.resumeDownload();
    }

    this.currentOptions = options;
    this.isPaused = false;
    this.retryCount = 0;
    this.isStallRetry = false;
    await this.startDownloadWithRetry();
  }

  private async startDownloadWithRetry(): Promise<void> {
    if (!this.currentOptions) return;

    while (!this.isPaused) {
      this.abortController = new AbortController();
      this.status = "active";
      this.isDownloading = true;
      this.isStallRetry = false;
      this.lastDataReceivedAt = Date.now();

      const { url, savePath, filename, headers = {} } = this.currentOptions;
      const { filePath, startByte, usedFallback } = this.prepareDownloadPath(
        savePath,
        filename,
        url
      );
      const requestHeaders = this.buildRequestHeaders(headers, startByte);

      this.startStallDetection();

      try {
        await this.executeDownload(
          url,
          requestHeaders,
          filePath,
          startByte,
          savePath,