npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

range-request-fetcher

v2.3.0

Published

Reliable file fetcher with pause, resume, and abort controls for big files using chunked range requests, retries, and progress tracking

Readme

Range Request Fetcher

A JavaScript library for reliably fetching large files using chunked range requests, automatic retries, and progress tracking. Specifically designed to handle common network errors like ERR_HTTP2_PROTOCOL_ERROR and ERR_SSL_PROTOCOL_ERROR that frequently occur when downloading large files over unstable connections.

Handling download problems

This library specifically handles common network errors that occur during large file downloads:

ERR_HTTP2_PROTOCOL_ERROR

This error typically occurs when:

  • The HTTP/2 connection is disrupted or closed unexpectedly
  • The server terminates the stream prematurely
  • Network instability causes protocol-level issues

How range-request-fetcher helps:

  • Automatically retries failed requests with exponential backoff
  • Resumes downloads from the last successful byte position

ERR_SSL_PROTOCOL_ERROR

This error happens when:

  • SSL/TLS handshake fails or is interrupted
  • Certificate validation issues occur
  • Secure connection is dropped during transfer

How range-request-fetcher helps:

  • Implements intelligent retry logic for SSL failures
  • Maintains connection state to resume from interruption points
  • Uses chunked requests to minimize data loss on connection drops

These features make the library particularly reliable for downloading large files over unstable connections or from servers with intermittent issues

Features

  • Chunked fetching - Splits large files into chunks for improved reliability
  • Automatic retries - Automatically retries failed chunks
  • Progress tracking - Real-time callbacks for progress updates
  • Pause/Resume/Abort - Full download control with pause, resume, and abort functionality
  • Authentication support - Compatible with Bearer tokens and custom headers
  • Modern API - Uses File System Access API for saving files
  • Error handling - Robust handling of network and protocol errors

Installation

npm install range-request-fetcher

Basic Usage

import { rangeRequestFetcher } from 'range-request-fetcher';

// Basic fetch (Promise-based)
const download = rangeRequestFetcher({
  url: 'https://example.com/large-file.zip',
  fileName: 'my-download.zip'
});

await download.promise;
console.log('Download completed!');

Download Control Examples

Simple Progress Tracking

const download = rangeRequestFetcher({
  url: 'https://example.com/large-file.zip',
  fileName: 'my-download.zip',
  onProgress: (percent) => console.log(`Progress: ${percent}%`),
  onStatus: (status) => console.log(`Status: ${status}`)
});

await download.promise;

Pause and Resume

const download = rangeRequestFetcher({
  url: 'https://example.com/large-file.zip',
  fileName: 'my-download.zip',
  onProgress: (percent) => {
    console.log(`Progress: ${percent}%`);
    
    // Auto-pause at 50%
    if (percent === 50) {
      download.pause();
      console.log('Download paused at 50%');
      
      // Resume after 5 seconds
      setTimeout(() => {
        download.resume();
        console.log('Download resumed');
      }, 5000);
    }
  }
});

await download.promise;

Manual Control with UI

const download = rangeRequestFetcher({
  url: 'https://example.com/ubuntu-22.04.3-desktop-amd64.iso',
  fileName: 'ubuntu-22.04.3-desktop-amd64.iso',
  onProgress: (percent) => {
    document.getElementById('progress').style.width = `${percent}%`;
    document.getElementById('percent').textContent = `${percent}%`;
  },
  onStatus: (status) => {
    document.getElementById('status').textContent = status;
  }
});

// UI Controls
document.getElementById('pauseBtn').onclick = () => {
  download.pause();
  document.getElementById('pauseBtn').disabled = true;
  document.getElementById('resumeBtn').disabled = false;
};

document.getElementById('resumeBtn').onclick = () => {
  download.resume();
  document.getElementById('pauseBtn').disabled = false;
  document.getElementById('resumeBtn').disabled = true;
};

document.getElementById('abortBtn').onclick = () => {
  download.abort();
  console.log('Download cancelled');
};

// Check download state
setInterval(() => {
  console.log(`Paused: ${download.isPaused()}, Aborted: ${download.isAborted()}, Progress: ${download.getProgress()}%`);
}, 1000);

try {
  await download.promise;
  console.log('Download completed!');
} catch (error) {
  if (download.isAborted()) {
    console.log('Download was cancelled by user');
  } else {
    console.error('Download failed:', error.message);
  }
}

With Authentication

const download = rangeRequestFetcher({
  url: 'https://api.example.com/secure-file.zip',
  fileName: 'secure-file.zip',
  token: 'your-bearer-token',
  headers: {
    'X-API-Key': 'your-api-key'
  },
  onProgress: (percent) => console.log(`Progress: ${percent}%`)
});

// You can still control authenticated downloads
setTimeout(() => download.pause(), 5000);
setTimeout(() => download.resume(), 10000);

await download.promise;

API Reference

rangeRequestFetcher(options)

Downloads a file using chunked range requests with automatic retries and full download control.

Parameters

| Parameter | Type | Required | Default | Description | |-----------|------|----------|---------|-------------| | url | string | ✅ | - | URL of the file to download | | fileName | string | ❌ | 'downloaded-file' | Suggested name for the saved file | | token | string | ❌ | - | Bearer token for authentication (automatically added to Authorization header) | | headers | object | ❌ | {} | Custom HTTP headers | | chunkSize | number | ❌ | 104857600 (100MB) | Size of each chunk in bytes | | maxRetries | number | ❌ | 10 | Maximum number of retries per chunk | | onProgress | function | ❌ | () => {} | Callback called with progress percentage (0-100) | | onStatus | function | ❌ | () => {} | Callback called with status updates |

Return Value

The function returns a control object with the following methods and properties:

| Method/Property | Type | Description | |-----------------|------|-------------| | promise | Promise | Main download promise to await | | pause() | function | Pauses the download | | resume() | function | Resumes the download | | abort() | function | Cancels the download completely | | isPaused() | function | Returns true if download is paused | | isAborted() | function | Returns true if download was aborted | | getProgress() | function | Returns current progress percentage (0-100) |

onStatus States

  • 'preparing' - Getting file information
  • 'downloading' - Downloading chunks
  • 'paused' - Download is paused
  • 'aborted' - Download was cancelled
  • 'retrying ${start}-${end}, attempt ${retries}' - Retrying a specific chunk
  • 'finalizing' - Finalizing the download
  • 'done' - Download completed successfully
  • 'error' - Download error

Complete Example with Error Handling

import { rangeRequestFetcher } from 'range-request-fetcher';

const download = rangeRequestFetcher({
  url: 'https://releases.ubuntu.com/22.04/ubuntu-22.04.3-desktop-amd64.iso',
  fileName: 'ubuntu-22.04.3-desktop-amd64.iso',
  chunkSize: 50 * 1024 * 1024, // 50MB chunks
  maxRetries: 5,
  headers: {
    'User-Agent': 'MyApp/1.0'
  },
  onProgress: (percent) => {
    console.log(`Download progress: ${percent}%`);
    
    // Update UI
    const progressBar = document.getElementById('progress-bar');
    const progressText = document.getElementById('progress-text');
    
    if (progressBar) progressBar.style.width = `${percent}%`;
    if (progressText) progressText.textContent = `${percent}%`;
  },
  onStatus: (status) => {
    console.log(`Download status: ${status}`);
    
    const statusElement = document.getElementById('download-status');
    if (statusElement) statusElement.textContent = status;
    
    // Handle different states
    if (status === 'paused') {
      document.getElementById('pause-btn')?.setAttribute('disabled', 'true');
      document.getElementById('resume-btn')?.removeAttribute('disabled');
    } else if (status === 'downloading') {
      document.getElementById('pause-btn')?.removeAttribute('disabled');
      document.getElementById('resume-btn')?.setAttribute('disabled', 'true');
    }
  }
});

// Set up UI controls
document.getElementById('pause-btn')?.addEventListener('click', () => {
  download.pause();
  console.log('Download paused by user');
});

document.getElementById('resume-btn')?.addEventListener('click', () => {
  download.resume();
  console.log('Download resumed by user');
});

document.getElementById('cancel-btn')?.addEventListener('click', () => {
  download.abort();
  console.log('Download cancelled by user');
});

// Monitor download state
const monitor = setInterval(() => {
  const progress = download.getProgress();
  const paused = download.isPaused();
  const aborted = download.isAborted();
  
  console.log(`State - Progress: ${progress}%, Paused: ${paused}, Aborted: ${aborted}`);
  
  if (aborted || progress === 100) {
    clearInterval(monitor);
  }
}, 2000);

// Wait for completion
try {
  await download.promise;
  console.log('Download completed successfully!');
  clearInterval(monitor);
} catch (error) {
  clearInterval(monitor);
  
  if (download.isAborted()) {
    console.log('Download was cancelled by user');
  } else {
    console.error('Download failed:', error.message);
    
    // Show user-friendly error messages
    if (error.message.includes('ERR_HTTP2_PROTOCOL_ERROR')) {
      console.log('Try again - HTTP/2 connection issue detected');
    } else if (error.message.includes('ERR_SSL_PROTOCOL_ERROR')) {
      console.log('Try again - SSL connection issue detected');
    }
  }
}