Détail du package

@apeleghq/http-media-type-negotiator

ApelegHQ850ISC1.0.8

RFC-aware HTTP media type negotiator - parses and normalises Accept headers and media types (RFC 9110), supports q-value ranking, wildcard matching, and a permissive mode for real-world headers.

http, content-negotiation, accept-header, media-type

readme

HTTP Media Type Negotiator

NPM Downloads


🚀 Features

  • No runtime dependencies.
  • Runtime and framework agnostic.
  • Negotiates HTTP media types (Accept header) against server-supported types following RFC 9110 §5.6.
  • Parses and normalises media types and Accept headers with optional permissive mode to tolerate common real-world deviations.
  • Supports q-value parsing and ranking.
  • Tie-breaking by specificity, shared parameters, then server order for deterministic results.
  • Exposes both a reusable factory for repeated negotiation and a one-shot convenience function.
  • It also exposes convenience functions (also used internally) for:
    • Parsing media types (parseMediaType)
    • Normalising said media types (normaliseMediaType)
    • Parsing Accept heades (parseAcceptHeader)

💻 Installation

Install from npm or yarn:

npm install @apeleghq/http-media-type-negotiator

or

yarn add @apeleghq/http-media-type-negotiator

📚 Usage

One-shot negotiation

Use the convenience function when you just need to negotiate once:

import { negotiateMediaType } from '@apeleghq/http-media-type-negotiator';

const available = [
  'text/plain; charset=utf-8',
  'application/json',
];

const best = negotiateMediaType(available, 'text/*;q=0.9, application/json;q=0.8');
// best -> 'text/plain; charset=utf-8'

Reusable negotiator (recommended for repeated calls)

Create a negotiator once for a fixed set of server-supported media types to avoid reparsing:

import { negotiateMediaTypeFactory } from '@apeleghq/http-media-type-negotiator';

const available = [
  'text/plain; charset=utf-8',
  'application/json',
];

const negotiate = negotiateMediaTypeFactory(available);

// Later, for each request:
const best1 = negotiate('application/json');
const best2 = negotiate('text/*;q=0.9, application/json;q=0.8');

Permissive mode

Pass the optional permissive flag to tolerate non-RFC-compliant inputs (extra whitespace, empty parameter values, flag parameters like ;foo, and truncated quoted values at EOF):

const best = negotiate(acceptHeader, true); // permissive parsing

Compatibility with negotiator

An API that emulates the negotiator package is available via the @apeleghq/http-media-type-negotiator/compat/Negotiator export.

The mediaType and mediaTypes interfaces are exposed, although they don't behave identically to those in negotiator:

  • mediaTypes will return at most a single media type if the availableMediaTypes parameter is provided.
  • The negotiation algorithm may produce different results.

⚙️ Behaviour notes

  • Returned strings are the original server-provided strings from the availableMediaTypes array.
  • Q-values are parsed and converted to integer weights between 0 and 1000 (1.0 → 1000). Entries with q=0 are ignored.
  • Matching supports exact type/subtype, type with wildcard subtype (e.g. text/*), and */*.
  • When multiple acceptable ranges tie on q, the negotiator prefers:
    1. More specific type/subtype (non-*),
    2. Media-range that shares the most non-q parameters with the available type,
    3. Server order (the order of available types provided).
  • The parser returns substrings for Accept entries and reparses them into structured media types internally; this keeps the implementation allocation-light.
  • Parameter values are not normalised and evaluated as exact matches (meaning case-sensitively).

✅ Recommended usage pattern

  • If negotiating repeatedly for the same server-supported types, use negotiateMediaTypeFactory once and reuse the returned function to avoid reparsing available types.
  • Normalise case if you need canonical string comparisons beyond what the negotiator provides.

🤝 Contributing

Contributions welcome. Please open issues or pull requests on the repository. Consider adding unit tests for edge cases and performance benchmarks if you change parsing behaviour.

📜 License

This project is released under the ISC license. See the LICENSE file for details.