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

if-range-parse

v0.1.0

Published

Zero-dependency Node.js HTTP If-Range header parser with dual-format support (ETag + HTTP-date, per RFC 7233)

Downloads

164

Readme

if-range-parse

Zero-dependency Node.js parser, serializer, and comparator for the HTTP If-Range request header (RFC 7233 §3.2).

The If-Range request header lets a client conditionally request either a byte range or the full representation, by specifying a precondition tied to either an ETag or an HTTP-date:

If-Range: "abc123"
If-Range: W/"abc123"
If-Range: Wed, 21 Oct 2015 07:28:00 GMT

This package distinguishes both forms at parse time and returns a typed, discriminated union — so the caller never has to hand-roll the ETag-vs-date split that has caused countless bugs in range-request handlers, resumable download libraries, and CDN edge workers.

Highlights

  • Dual-format support — ETag ("…", W/"…", "*") and HTTP-date (IMF-fixdate, RFC 850, asctime) in one parser.
  • Zero runtime dependencies — pure Node.js, no npm bloat.
  • Full TypeScript declarations in index.d.ts, passes tsc --noEmit --strict.
  • Strict-validity — throws InvalidIfRangeError on malformed input; TypeError on non-string input.
  • Round-trip serializationparse → serialize → parse is stable for all supported forms.
  • RFC 7232 §2.3.2 strong comparisonetagMatches(a, b) with wildcard ("*") support.

Install

npm install if-range-parse

Requires Node.js ≥ 18.

Usage

import { parseIfRange, serializeIfRange, etagMatches } from 'if-range-parse';

// ETag form — strong ETag
parseIfRange('"abc123"');
// → { kind: 'etag',
//     etag: { strong: true, tag: 'abc123' },
//     raw: '"abc123"' }

// ETag form — weak ETag
parseIfRange('W/"abc123"');
// → { kind: 'etag',
//     etag: { strong: false, tag: 'abc123' },
//     raw: 'W/"abc123"' }

// HTTP-date form — IMF-fixdate (preferred)
parseIfRange('Wed, 21 Oct 2015 07:28:00 GMT');
// → { kind: 'date',
//     date: 2015-10-21T07:28:00.000Z,
//     format: 'IMF-fixdate',
//     raw: 'Wed, 21 Oct 2015 07:28:00 GMT' }

// HTTP-date form — RFC 850 (legacy)
parseIfRange('Sunday, 06-Nov-94 08:49:37 GMT');
// → { kind: 'date',
//     date: 1994-11-06T08:49:37.000Z,
//     format: 'RFC-850',
//     raw: 'Sunday, 06-Nov-94 08:49:37 GMT' }

// HTTP-date form — asctime (legacy)
parseIfRange('Thu Nov  6 08:49:37 2014');
// → { kind: 'date',
//     date: 2014-11-06T08:49:37.000Z,
//     format: 'asctime',
//     raw: 'Thu Nov  6 08:49:37 2014' }

// Serialize back to header form
serializeIfRange({ kind: 'etag', tag: 'abc123', strong: false });
// → 'W/"abc123"'

serializeIfRange({
  kind: 'date',
  date: new Date('2015-10-21T07:28:00Z'),
  format: 'IMF-fixdate',
});
// → 'Wed, 21 Oct 2015 07:28:00 GMT'

// RFC 7232 §2.3.2 strong comparison
etagMatches('"abc"', '"abc"');         // true  (strong == strong, same tag)
etagMatches('W/"abc"', 'W/"abc"');     // true  (weak == weak, same tag)
etagMatches('W/"abc"', '"abc"');       // false (weak never matches strong)
etagMatches('"abc"', 'W/"abc"');       // false
etagMatches('"abc"', '"def"');         // false (different tags)
etagMatches('"*"', '"anything"');      // true  (wildcard)

API

parseIfRange(header: string): IfRangeResult

Parses an If-Range header value into a discriminated union.

| Field | Type | Notes | |---|---|---| | kind | 'etag' or 'date' | Discriminator. | | etag | { strong, tag, wildcard? } | Present iff kind === 'etag'. | | date | Date | Present iff kind === 'date'. UTC-anchored, always valid (no NaN). | | format | 'IMF-fixdate' \| 'RFC-850' \| 'asctime' | Present iff kind === 'date'. | | raw | string | The exact input string (untrimmed). |

Throws:

  • InvalidIfRangeError on malformed header values.
  • TypeError on non-string input (null, undefined, numbers, objects, …).

serializeIfRange(value): string

Serializes a parsed (or compatible) value back into the header form. The input shape mirrors the parsed result:

{ kind: 'etag', tag: string, strong?: boolean }
{ kind: 'etag', etag: { strong: boolean, tag: string } }
{ kind: 'date', date: Date, format: 'IMF-fixdate' | 'RFC-850' | 'asctime' }

Throws InvalidIfRangeError on invalid input.

etagMatches(a: string, b: string): boolean

RFC 7232 §2.3.2 comparison of two ETag header strings. Semantics:

| a | b | result | reason | |---|---|---|---| | "x" | "x" | true | strong == strong, same tag | | W/"x" | W/"x" | true | weak == weak, same tag | | "x" | W/"x" | false | weak never matches strong | | W/"x" | "x" | false | weak never matches strong | | "x" | "y" | false | different tags | | "*" | "anything" | true | wildcard matches anything | | "*" | "*" | true | wildcard matches wildcard |

Throws InvalidIfRangeError on malformed ETag input.

InvalidIfRangeError extends Error

Thrown by all three functions on malformed input. The name field is 'InvalidIfRangeError' for instanceof checks.

HTTP-date format detection order

Three HTTP-date formats are accepted (per RFC 7231 §7.1.1.1). They are tried in this priority order:

  1. IMF-fixdate (preferred) — Sun, 06 Nov 1994 08:49:37 GMT
  2. RFC 850 (legacy) — Sunday, 06-Nov-94 08:49:37 GMT
  3. asctime (legacy) — Sun Nov 6 08:49:37 1994

IMF-fixdate takes priority because some inputs match both the IMF-fixdate and RFC 850 patterns (e.g. Thu, 01-Jan-00 00:00:00 GMT could be parsed either way). When both match, IMF-fixdate wins.

RFC 850 year-window rule

RFC 7231 specifies a sliding window for the 2-digit year:

| Two-digit year | Full year | |---|---| | 00–68 | 2000–2068 | | 69–99 | 1969–1999 |

94 → 1994, 00 → 2000, 99 → 1999. This matches common practice for HTTP date parsing.

Out of scope

This package parses the If-Range request header only. It does not:

  • Parse the Content-Range response header (use content-range-parse for that).
  • Parse If-Match or If-None-Match (use etag-parse).
  • Validate byte-range satisfaction logic — that's the caller's job.
  • Read from raw HTTP request objects — pass the header string directly.

Limitations

  • asctime timezone handling — RFC 7231 §7.1.1.1 says asctime times are "interpreted as UTC". This implementation follows that. Servers that emit asctime in a local timezone will produce incorrect dates.
  • Day-of-week mismatch is tolerated — If the day-of-week name doesn't match the date (e.g. Sun, 06 Nov 1994 … where Nov 6 1994 was actually a Sunday — fine, but mismatches are NOT rejected). This matches common practice; tighten if you need strict validation.
  • No validation that the date is in the past — This is a parser, not a validator.

Project facts

  • Test count: 132 tests across tests/parse.test.js. Run with npm test.
  • Source LOC: under 500 (per spec budget).
  • Zero runtime dependencies.
  • License: MIT.
  • Engines: Node.js ≥ 18.

License

MIT — see LICENSE.