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

http-fields

v0.0.4

Published

Modern JavaScript library for parsing and serializing HTTP Structured Field Values (RFC 8941 & RFC 9651)

Readme

http-fields

A modern JavaScript library for parsing and serializing HTTP Structured Field Values (RFC 8941 & RFC 9651). Provides bidirectional translation between structured header strings and JSON with full TypeScript support.

See also badgateway/structured-headers

Overview

Structured Fields is a specification that defines common data structures for HTTP header and trailer fields. Instead of each HTTP field having its own custom parsing logic, this specification provides a set of well-defined data types and parsing algorithms that can be shared across multiple fields.

This library implements the complete RFC 8941 and RFC 9651 specifications with strict parsing and serialization according to the standards, including support for dates and Unicode display strings.

Features

  • Complete RFC 8941 & RFC 9651 Implementation - All data types and algorithms
  • TypeScript Support - Comprehensive type definitions and type guards
  • Strict Parsing - Follows RFC's error handling requirements
  • Bidirectional Translation - Parse to JSON and serialize back to strings
  • Extended Data Types - Dates (@) and Display Strings (%) from RFC 9651
  • Type Safety - Full TypeScript definitions with runtime validation
  • Zero Dependencies - Pure JavaScript implementation
  • ES Module Support - Modern JavaScript module format

Installation

npm install http-fields

Or include directly in your project:

import * as HTTPFields from "./index.mjs";

Quick Start

JavaScript

import * as HTTPFields from "http-fields";

// Parse a list
const list = HTTPFields.parse("sugar, tea, rum", "list");
console.log(list);
// Output: [
//   { value: { type: 'token', value: 'sugar' }, parameters: {} },
//   { value: { type: 'token', value: 'tea' }, parameters: {} },
//   { value: { type: 'token', value: 'rum' }, parameters: {} }
// ]

// Serialize back to string
const serialized = HTTPFields.serialize(list, "list");
console.log(serialized); // "sugar, tea, rum"

TypeScript

import * as HTTPFields from "http-fields";
import type { List, Dictionary, Item } from "http-fields/types";

// Parse with type safety
const list: List = HTTPFields.parse("sugar, tea, rum", "list");
const dict: Dictionary = HTTPFields.parse("a=1, b=2", "dictionary");
const item: Item = HTTPFields.parse("@1672531200", "item");

// RFC 9651 features
const date = HTTPFields.date(new Date());
const displayStr = HTTPFields.displayString("Hello 世界");

Data Types

Basic Types

| Type | Description | Example | JSON Representation | | ----------------------------- | ------------------------------------------------------------ | ------------------ | --------------------------------------------------------- | | Integer | Signed integer (-999,999,999,999,999 to 999,999,999,999,999) | 42 | 42 | | Decimal | Decimal number (max 12 integer digits, 3 fractional) | 3.14 | 3.14 | | String | Quoted ASCII string | "hello world" | "hello world" | | Token | Unquoted text identifier | application/json | { type: 'token', value: 'application/json' } | | Byte Sequence | Base64-encoded binary data | :SGVsbG8=: | { type: 'binary', value: 'SGVsbG8=', decoded: 'Hello' } | | Boolean | True or false value | ?1 or ?0 | true or false | | Date (RFC 9651) | Unix timestamp with @ prefix | @1672531200 | { type: 'date', value: Date } | | Display String (RFC 9651) | Unicode string with % prefix | %"Hello 世界" | { type: 'displaystring', value: 'Hello 世界' } |

Structured Types

| Type | Description | Example | | -------------- | ----------------------------------- | ---------------------- | | List | Array of items or inner lists | a, b, (c d) | | Dictionary | Key-value pairs | a=1, b=2, c | | Inner List | Parenthesized list of items | (a b c) | | Parameters | Semicolon-separated key-value pairs | ;charset=utf-8;q=0.9 |

API Reference

Core Methods

parse(fieldValue, fieldType)

Parses a structured field string into a JSON representation.

Parameters:

  • fieldValue (string): The HTTP field value to parse
  • fieldType (string): One of 'list', 'dictionary', or 'item'

Returns: Parsed structure as JSON

Example:

const result = HTTPFields.parse("a=1, b=2;x=y", "dictionary");

serialize(data, fieldType)

Serializes a JSON structure back to a structured field string.

Parameters:

  • data (any): The data structure to serialize
  • fieldType (string): One of 'list', 'dictionary', or 'item'

Returns: Serialized field value string

Example:

const fieldValue = HTTPFields.serialize(data, "dictionary");

Helper Methods

token(value)

Creates a token object for use in structured data.

Parameters:

  • value (string): The token string

Returns: Token object { type: 'token', value: string }

Example:

const tokenObj = HTTPFields.token("application/json");

binary(base64Value)

Creates a binary object for use in structured data.

Parameters:

  • base64Value (string): Base64-encoded string

Returns: Binary object { type: 'binary', value: string }

Example:

const binaryObj = HTTPFields.binary("SGVsbG8=");

date(dateValue) - RFC 9651

Creates a date object for use in structured data.

Parameters:

  • dateValue (Date): JavaScript Date object

Returns: Date object { type: 'date', value: Date }

Example:

const dateObj = HTTPFields.date(new Date("2024-01-01"));
// Serializes to: @1704067200

displayString(unicodeValue) - RFC 9651

Creates a display string object for Unicode content.

Parameters:

  • unicodeValue (string): Unicode string

Returns: Display string object { type: 'displaystring', value: string }

Example:

const displayObj = HTTPFields.displayString("Hello 世界");
// Serializes to: %"Hello %e4%b8%96%e7%95%8c"

Examples

Working with Lists

// Parse a simple list
const simpleList = HTTPFields.parse("a, b, c", "list");

// Parse a list with parameters
const listWithParams = HTTPFields.parse("a;x=1, b;y=2", "list");

// Parse a list with inner lists
const complexList = HTTPFields.parse("a, (b c), d", "list");

// Create and serialize a list
const listData = [
  { value: HTTPFields.token("sugar"), parameters: {} },
  { value: HTTPFields.token("tea"), parameters: { quality: 0.8 } },
  {
    value: [
      { value: HTTPFields.token("milk"), parameters: {} },
      { value: HTTPFields.token("honey"), parameters: {} },
    ],
    parameters: { organic: true },
  },
];
const serialized = HTTPFields.serialize(listData, "list");
// Result: "sugar, tea;quality=0.8, (milk honey);organic"

Working with Dictionaries

// Parse a dictionary
const dict = HTTPFields.parse(
  "cache=max-age, public, max-age=3600",
  "dictionary"
);

// Parse dictionary with various types
const complexDict = HTTPFields.parse(
  'a=1, b="hello", c=?1, d=(x y)',
  "dictionary"
);

// Create and serialize a dictionary
const dictData = {
  method: { value: HTTPFields.token("GET"), parameters: {} },
  timeout: { value: 30, parameters: { unit: HTTPFields.token("seconds") } },
  secure: { value: true, parameters: {} },
  headers: {
    value: [
      { value: HTTPFields.token("authorization"), parameters: {} },
      { value: HTTPFields.token("content-type"), parameters: {} },
    ],
    parameters: {},
  },
};
const serialized = HTTPFields.serialize(dictData, "dictionary");

Working with Items

// Parse a simple item
const item = HTTPFields.parse("42", "item");
// Result: { value: 42, parameters: {} }

// Parse an item with parameters
const itemWithParams = HTTPFields.parse(
  '"hello";charset="utf-8";length=5',
  "item"
);
// Result: {
//   value: "hello",
//   parameters: {
//     charset: "utf-8",
//     length: 5
//   }
// }

// Serialize an item
const itemData = {
  value: HTTPFields.token("application/json"),
  parameters: {
    charset: HTTPFields.token("utf-8"),
    boundary: "----WebKitFormBoundary7MA4YWxkTrZu0gW",
  },
};
const serialized = HTTPFields.serialize(itemData, "item");
// Result: 'application/json;charset=utf-8;boundary="----WebKitFormBoundary7MA4YWxkTrZu0gW"'

Working with Binary Data

// Parse binary data
const binary = HTTPFields.parse(":SGVsbG8gV29ybGQ=:", "item");
// Result: {
//   value: {
//     type: 'binary',
//     value: 'SGVsbG8gV29ybGQ=',
//     decoded: 'Hello World'
//   },
//   parameters: {}
// }

// Create binary data
const binaryData = {
  value: HTTPFields.binary(btoa("Hello World")),
  parameters: { encoding: HTTPFields.token("base64") },
};
const serialized = HTTPFields.serialize(binaryData, "item");
// Result: ':SGVsbG8gV29ybGQ=:;encoding=base64'

Working with Dates (RFC 9651)

// Parse date values
const dateItem = HTTPFields.parse("@1672531200", "item");
// Result: {
//   value: {
//     type: 'date',
//     value: Date(2023-01-01T00:00:00.000Z)
//   },
//   parameters: {}
// }

// Create and serialize dates
const expiryData = {
  value: HTTPFields.date(new Date("2025-12-31T23:59:59Z")),
  parameters: { timezone: HTTPFields.token("UTC") },
};
const serialized = HTTPFields.serialize(expiryData, "item");
// Result: '@1767225599;timezone=UTC'

// Dates in dictionaries
const timingDict = {
  start: { value: HTTPFields.date(new Date("2024-01-01")), parameters: {} },
  end: { value: HTTPFields.date(new Date("2024-12-31")), parameters: {} },
};
const serializedDict = HTTPFields.serialize(timingDict, "dictionary");
// Result: 'start=@1704067200, end=@1735689600'

Working with Display Strings (RFC 9651)

// Parse Unicode display strings
const displayItem = HTTPFields.parse('%"Hello 世界"', "item");
// Result: {
//   value: {
//     type: 'displaystring',
//     value: 'Hello 世界'
//   },
//   parameters: {}
// }

// Create and serialize display strings
const messageData = {
  value: HTTPFields.displayString("Welcome to Tokyo 東京へようこそ"),
  parameters: { lang: HTTPFields.token("ja") },
};
const serialized = HTTPFields.serialize(messageData, "item");
// Result: '%"Welcome to Tokyo %e6%9d%b1%e4%ba%ac%e3%81%b8%e3%82%88%e3%81%86%e3%81%93%e3%81%9d";lang=ja'

// Mixed content with dates and display strings
const eventList = [
  { value: HTTPFields.displayString("Conference 会議"), parameters: {} },
  { value: HTTPFields.date(new Date("2024-06-15T09:00:00Z")), parameters: {} },
  { value: HTTPFields.token("tokyo-convention-center"), parameters: {} },
];
const serializedList = HTTPFields.serialize(eventList, "list");
// Result: '%"Conference %e4%bc%9a%e8%ad%b0", @1718442000, tokyo-convention-center'

Working with Standard Headers Object

The library integrates seamlessly with the standard Web API Headers object used in fetch(), service workers, and other web APIs:

Reading Headers from HTTP Requests/Responses

// Working with fetch Response headers
async function handleResponse(response) {
  const headers = response.headers;

  // Parse Cache-Control header (dictionary)
  const cacheControlValue = headers.get("cache-control");
  if (cacheControlValue) {
    try {
      const cacheControl = HTTPFields.parse(cacheControlValue, "dictionary");
      console.log("Max age:", cacheControl["max-age"]?.value);
      console.log("Is private:", cacheControl.private?.value === true);
    } catch (error) {
      console.warn("Invalid Cache-Control header");
    }
  }

  // Parse Accept-Language header (list)
  const acceptLangValue = headers.get("accept-language");
  if (acceptLangValue) {
    try {
      const acceptLang = HTTPFields.parse(acceptLangValue, "list");
      const languages = acceptLang.map((item) => ({
        language: item.value.value || item.value,
        quality: item.parameters.q || 1.0,
      }));
      console.log("Preferred languages:", languages);
    } catch (error) {
      console.warn("Invalid Accept-Language header");
    }
  }

  // Parse custom structured header (item)
  const apiVersionValue = headers.get("x-api-version");
  if (apiVersionValue) {
    try {
      const apiVersion = HTTPFields.parse(apiVersionValue, "item");
      console.log("API Version:", apiVersion.value);
      console.log("Deprecated:", apiVersion.parameters.deprecated);
    } catch (error) {
      console.warn("Invalid X-API-Version header");
    }
  }
}

// Usage with fetch
fetch("/api/data").then(handleResponse).catch(console.error);

Setting Headers for HTTP Requests

// Create structured headers for outgoing requests
function createApiRequest(url, options = {}) {
  const headers = new Headers(options.headers);

  // Set Accept header (list with quality values)
  const acceptData = [
    {
      value: HTTPFields.token("application/json"),
      parameters: { q: 0.9 },
    },
    {
      value: HTTPFields.token("application/xml"),
      parameters: { q: 0.8 },
    },
    {
      value: HTTPFields.token("*/*"),
      parameters: { q: 0.1 },
    },
  ];
  headers.set("accept", HTTPFields.serialize(acceptData, "list"));

  // Set custom API preferences (dictionary)
  const preferencesData = {
    version: { value: 2, parameters: {} },
    format: { value: HTTPFields.token("compact"), parameters: {} },
    "include-metadata": { value: true, parameters: {} },
  };
  headers.set(
    "x-api-preferences",
    HTTPFields.serialize(preferencesData, "dictionary")
  );

  // Set client info (item with parameters)
  const clientInfoData = {
    value: HTTPFields.token("MyApp"),
    parameters: {
      version: "1.2.3",
      platform: HTTPFields.token("web"),
    },
  };
  headers.set("x-client-info", HTTPFields.serialize(clientInfoData, "item"));

  return fetch(url, {
    ...options,
    headers,
  });
}

// Usage
createApiRequest("/api/users", {
  method: "GET",
  headers: {
    authorization: "Bearer token123",
  },
});

Express.js Server Integration

import express from "express";
import * as HTTPFields from "http-fields";

const app = express();

// Middleware to parse structured headers
app.use((req, res, next) => {
  // Parse client capabilities (list)
  const capabilities = req.headers["x-client-capabilities"];
  if (capabilities) {
    try {
      req.clientCapabilities = HTTPFields.parse(capabilities, "list").map(
        (item) => item.value.value || item.value
      );
    } catch (error) {
      req.clientCapabilities = [];
    }
  }

  // Parse feature flags (dictionary)
  const featureFlags = req.headers["x-feature-flags"];
  if (featureFlags) {
    try {
      req.featureFlags = HTTPFields.parse(featureFlags, "dictionary");
    } catch (error) {
      req.featureFlags = {};
    }
  }

  next();
});

// Route handler using parsed structured headers
app.get("/api/data", (req, res) => {
  // Use parsed capabilities
  const supportsCompression = req.clientCapabilities.includes("gzip");
  const experimentalFeatures =
    req.featureFlags["experimental-ui"]?.value === true;

  // Set structured response headers
  const cacheData = {
    "max-age": { value: 3600, parameters: {} },
    public: { value: true, parameters: {} },
  };
  res.set("cache-control", HTTPFields.serialize(cacheData, "dictionary"));

  // Set API metadata (item)
  const metadataData = {
    value: HTTPFields.token("v2"),
    parameters: {
      "rate-limit": 1000,
      experimental: experimentalFeatures,
    },
  };
  res.set("x-api-metadata", HTTPFields.serialize(metadataData, "item"));

  res.json({ data: "response" });
});

Service Worker Example

// In service worker
self.addEventListener("fetch", (event) => {
  const request = event.request;
  const headers = request.headers;

  // Parse client preferences
  const preferencesValue = headers.get("x-preferences");
  let preferences = {};

  if (preferencesValue) {
    try {
      preferences = HTTPFields.parse(preferencesValue, "dictionary");
    } catch (error) {
      console.warn("Invalid preferences header");
    }
  }

  // Modify response based on preferences
  if (preferences.theme?.value === "dark") {
    // Serve dark theme assets
    event.respondWith(fetch("/assets/dark-theme.css"));
  } else {
    event.respondWith(fetch(request));
  }
});

// Setting headers in service worker
self.addEventListener("fetch", (event) => {
  if (event.request.url.includes("/api/")) {
    // Add structured headers to API requests
    const modifiedRequest = new Request(event.request, {
      headers: new Headers([
        ...event.request.headers.entries(),
        [
          "x-service-worker",
          HTTPFields.serialize(
            {
              value: HTTPFields.token("active"),
              parameters: {
                version: "1.0",
                "cache-strategy": HTTPFields.token("stale-while-revalidate"),
              },
            },
            "item"
          ),
        ],
      ]),
    });

    event.respondWith(fetch(modifiedRequest));
  }
});

Headers Utility Class

// Utility class for working with structured headers
import { StructuredHeaders } from "http-fields";

// Usage example
const headers = new StructuredHeaders();

// Set cache control
headers.setStructured(
  "cache-control",
  {
    "max-age": { value: 3600, parameters: {} },
    public: { value: true, parameters: {} },
  },
  "dictionary"
);

// Set accept header
headers.setStructured(
  "accept",
  [
    { value: HTTPFields.token("application/json"), parameters: { q: 0.9 } },
    { value: HTTPFields.token("text/html"), parameters: { q: 0.8 } },
  ],
  "list"
);

// Use with fetch
fetch("/api/endpoint", {
  headers: headers.headers,
});

// Parse response headers
fetch("/api/endpoint").then((response) => {
  const responseHeaders = new StructuredHeaders(response.headers);
  const cacheControl = responseHeaders.getStructured(
    "cache-control",
    "dictionary"
  );
  const rateLimit = responseHeaders.getStructured("x-rate-limit", "item");

  console.log("Cache max-age:", cacheControl?.["max-age"]?.value);
  console.log("Rate limit:", rateLimit?.value);
});

Error Handling

The library follows RFC 8941's strict error handling requirements. Any parsing error causes the entire operation to fail with a descriptive error message:

try {
  const result = HTTPFields.parse("invalid[syntax", "list");
} catch (error) {
  console.error("Parsing failed:", error.message);
  // Handle the error - treat as if field was not present
}

Common error scenarios:

  • Invalid characters in tokens
  • Malformed strings (unterminated quotes)
  • Numbers out of range
  • Invalid base64 encoding
  • Malformed inner lists or parameters

JSON Structure Format

List Structure

[
    {
        value: <item_value> | <inner_list>,
        parameters: { key: value, ... }
    },
    ...
]

Dictionary Structure

{
    "key1": {
        value: <item_value> | <inner_list>,
        parameters: { key: value, ... }
    },
    "key2": { ... },
    ...
}

Item Structure

{
    value: <item_value>,
    parameters: { key: value, ... }
}

Inner List Structure

[
    {
        value: <item_value>,
        parameters: { key: value, ... }
    },
    ...
]

Real-World Use Cases

Cache-Control Headers

// Parse Cache-Control as a dictionary
const cacheControl = HTTPFields.parse(
  "max-age=3600, private, must-revalidate",
  "dictionary"
);

// Create Cache-Control
const cacheData = {
  "max-age": { value: 3600, parameters: {} },
  private: { value: true, parameters: {} },
  "must-revalidate": { value: true, parameters: {} },
};
const header = HTTPFields.serialize(cacheData, "dictionary");

Accept Headers

// Parse Accept header as a list
const accept = HTTPFields.parse(
  "text/html;q=0.9, application/json;q=0.8",
  "list"
);

// Create Accept header
const acceptData = [
  {
    value: HTTPFields.token("text/html"),
    parameters: { q: 0.9 },
  },
  {
    value: HTTPFields.token("application/json"),
    parameters: { q: 0.8 },
  },
];
const header = HTTPFields.serialize(acceptData, "list");

Custom Application Headers

// API versioning header
const version = HTTPFields.parse("v=2;deprecated=?0", "item");

// Feature flags
const features = HTTPFields.parse("feature-a, feature-b;enabled=?1", "list");

// Service metadata
const metadata = HTTPFields.parse(
  'service="api", version=2, region="us-west"',
  "dictionary"
);

Compliance and Testing

This implementation follows RFC 8941 strictly and passes the community test suite available at httpwg/structured-field-tests.

Limitations

  • Follows RFC 8941 size limits (15-digit integers, 3 decimal places, etc.)
  • ASCII-only strings (use Byte Sequences for Unicode)
  • Strict parsing - any malformed input fails completely
  • Parameter keys must be lowercase

Browser Compatibility

This library uses modern JavaScript features:

  • ES6 modules
  • Array methods (splice, shift, etc.)
  • String methods
  • Object methods

Supports all modern browsers and Node.js environments.

Testing

Run the comprehensive test suite:

# Run custom tests (47 tests covering all features)
npm test

# Run official HTTP Working Group tests (168 tests - ALL PASSING! ✅)
npm run test:official

# Run all tests
npm run test:all

# Run tests with coverage
npm run test:coverage

Test Results:

  • Custom tests: 47/47 passing
  • Official HTTP WG tests: 1711/1711 passing
  • Total: 1758 tests passing

Our implementation now passes the same official test suite used by badgateway/structured-headers while maintaining our developer-friendly API.

Contributing

Contributions welcome! Please ensure:

  • All tests pass
  • Code follows existing style
  • RFC 8941 compliance maintained
  • Documentation updated

License

MIT License - see LICENSE file for details.

References