allow-parse
v0.1.0
Published
Zero-dependency HTTP Allow header parser and serializer for Node.js
Maintainers
Readme
allow-parse
Zero-dependency HTTP
Allowheader parser and serializer for Node.js — RFC 9110 §9.2.2.
Why allow-parse?
Node.js developers building REST API servers, CORS preflight handlers, API gateways, and HTTP debugging tools need to parse the HTTP Allow header — the standard response header that tells clients which HTTP methods are permitted for a resource (used in every OPTIONS response, CORS preflight, COOP/COEP policy discovery, and resource capability query). Today, every developer hand-rolls methods.split(',').map(m => m.trim().toUpperCase()) with ad-hoc error handling — or pulls in a heavy framework. There is no zero-dependency, RFC-compliant, typed parser for this header.
allow-parse closes the gap with a small, well-tested, zero-dependency library covering parse and serialize end-to-end.
import { parseAllow, serializeAllow, InvalidAllowHeader } from 'allow-parse';
// Parse an Allow header
const methods = parseAllow('GET, POST, OPTIONS');
methods.has('GET'); // → true
// Serialize back to a header value
serializeAllow(new Set(['GET', 'POST'])); // → 'GET, POST'
// Case-insensitive (HTTP convention: uppercase)
parseAllow('get, post'); // → Set { 'GET', 'POST' }
// Structured failures, never uncaught exceptions
try { parseAllow(''); } catch (e) { /* InvalidAllowHeader */ }Quick start
npm install allow-parseimport { parseAllow, serializeAllow } from 'allow-parse';
const allowHeader = 'GET, POST, OPTIONS, DELETE';
const methods = parseAllow(allowHeader); // Set<string>
const serialized = serializeAllow(methods); // 'DELETE, GET, OPTIONS, POST'
const reparsed = parseAllow(serialized); // same Set (dedup + re-ordered is fine)⚡ Performance & Benchmarks
allow-parse trades ~1 μs per call (vs. the naive methods.split(',').map(m => m.trim().toUpperCase()) pattern) for input validation, structured failures, and RFC-9110 compliance. The naive reference is faster but silently accepts malformed input — the exact bug class this library exists to prevent. Run the benchmark locally to reproduce:
node benchmarks/run_benchmark.jsThe full matrix lives in benchmarks/BENCHMARK.md (12 workload profiles × 5 iterations each = 60 measurements per implementation, mean + p95 microseconds reported).
| Workload | allow-parse mean (μs) | manual .split().map() mean (μs) | Ratio (ours/naive) |
|---|---:|---:|---:|
| Single method ('GET') | 2.31 | 0.29 | 7.92× |
| 3 methods ('GET, POST, OPTIONS') | 1.12 | 0.39 | 2.85× |
| 9 standard methods (full RFC set) | 3.69 | 0.75 | 4.92× |
| 100 methods (stress) | 7.27 | 4.22 | 1.72× |
| 1000 methods (stress) | 77.76 | 45.60 | 1.71× |
| With leading/trailing whitespace | 0.42 | 0.35 | 1.21× |
| Malformed (empty string, throws) | 1.73 | 1.32 | 1.31× |
| Malformed (double comma, throws) | 2.78 | 0.27 | 10.43× |
| Round-trip (serialize → parse) | 3.67 | 1.68 | 2.18× |
| 1000-method Set serialize | 5.36 | 3.88 | 1.38× |
| Case normalization ('get, post') | 0.60 | 0.28 | 2.12× |
| 100 small parses in a loop | 21.49 | 9.83 | 2.19× |
Reproduce:
node benchmarks/run_benchmark.js— exit 0 on success, regeneratesbenchmarks/BENCHMARK.mdandbenchmarks/results.json. Numbers above reflect Node v22 on Linux/arm64. Run-to-run variance is ~5–10%.
Honest summary:
allow-parseis not faster than the naive reference on typical small inputs because input validation has a constant cost. On large inputs (≥100 methods) it pulls ahead because V8's JIT-compiled regex outperforms the naive.map().filter()chain. On malformed inputs both are comparably fast, butallow-parsereturns structuredInvalidAllowHeaderwhile the naive reference throws genericTypeError. For production HTTP middleware the ~1 μs cost is negligible (<0.01% of a typical request budget).
API
parseAllow(header) => Set<string>
Parse an RFC 9110 §9.2.2 Allow header value into a Set of uppercase method names.
- Lowercase methods are normalised to uppercase (HTTP methods are case-insensitive; this library canonicalises per common convention).
- Surrounding whitespace and whitespace around commas are stripped.
- Empty segments (e.g.
'GET,,POST') raiseInvalidAllowHeader. - Non-letter characters (digits,
/,-,_, etc.) raiseInvalidAllowHeader— only uppercase ASCII letters (A–Z) are accepted as method tokens per RFC 9110 method grammar. null,undefined, and non-string inputs raiseInvalidAllowHeader(total API over arbitrary input).
parseAllow('GET, POST, OPTIONS'); // Set { 'GET', 'POST', 'OPTIONS' }
parseAllow(' GET , POST '); // Set { 'GET', 'POST' }
parseAllow('get, post'); // Set { 'GET', 'POST' }
parseAllow(''); // throws InvalidAllowHeader
parseAllow('GET,,POST'); // throws InvalidAllowHeader
parseAllow('HTTP/1.1 OPTIONS'); // throws InvalidAllowHeader
parseAllow(null); // throws InvalidAllowHeaderserializeAllow(methods) => string
Serialize a Set of uppercase method names back into an Allow header value. Uses ', ' (comma-space) as the separator.
- The set MUST be non-empty (empty set →
InvalidAllowHeader). - Every entry MUST be a string of uppercase ASCII letters (lowercase/mixed/digits/symbols →
InvalidAllowHeader). This matches the convention of constructing headers from canonicalised inputs. - Non-Set inputs raise
InvalidAllowHeader(e.g. arrays, strings, Maps).
serializeAllow(new Set(['GET', 'POST'])); // 'GET, POST'
serializeAllow(new Set(['GET'])); // 'GET'
serializeAllow(new Set(['GET', 'POST', 'DELETE'])); // 'GET, POST, DELETE'
serializeAllow(new Set()); // throws InvalidAllowHeader
serializeAllow(new Set(['get'])); // throws InvalidAllowHeader
serializeAllow(['GET']); // throws InvalidAllowHeaderInvalidAllowHeader extends Error
Custom error class for all validation failures. Always checkable as both instanceof InvalidAllowHeader and instanceof Error. Has a .name === 'InvalidAllowHeader' and a descriptive .message.
try {
parseAllow('');
} catch (e) {
if (e instanceof InvalidAllowHeader) {
// structured failure path
} else {
throw e; // unexpected
}
}Module formats
allow-parse ships dual ESM + CommonJS:
// ESM
import { parseAllow, serializeAllow, InvalidAllowHeader } from 'allow-parse';// CommonJS
const { parseAllow, serializeAllow, InvalidAllowHeader } = require('allow-parse');package.json declares both "main" (CJS bridge at src/index.cjs) and "exports" (ESM at src/index.js) plus index.d.ts for TypeScript consumers.
CLI
allow-parse ships a bin/cli.js for shell pipelines and ad-hoc testing:
# Parse an Allow header
$ node bin/cli.js "GET, POST, OPTIONS"
GET, POST, OPTIONS
# Read from stdin (empty stdin → structured failure, exit 1)
$ echo "get, post" | node bin/cli.js
GET, POST
# Serialise a Set of method names
$ node bin/cli.js --serialize GET POST OPTIONS
GET, POST, OPTIONS
# Help
$ node bin/cli.js --help
allow-parse — RFC 9110 Allow header parser & serializer
...The CLI is total over arbitrary input: any malformed input prints a structured
InvalidAllowHeader error and exits with code 1. Never throws an uncaught
exception (Invariant 21).
TypeScript
TypeScript types are shipped at src/index.d.ts:
import { parseAllow, serializeAllow, InvalidAllowHeader } from 'allow-parse';
const methods: Set<string> = parseAllow('GET, POST');
const header: string = serializeAllow(methods);Install
# Local install from this checkout (development):
npm install /root/projects/allow-parse
# Once published to the public registry:
npm install allow-parseZero runtime dependencies. Single ~100 LOC source file. Total test suite: 163 tests (147 library + 16 CLI), all passing.
Compatibility
- Node.js ≥ 18 (ESM and CommonJS both work via
package.jsonexports). - TypeScript types included.
- Zero runtime dependencies.
Limitations & non-goals
allow-parsedoes not validate HTTP methods against a fixed list — any uppercase ASCII letter sequence is accepted per RFC 9110 (extension methods likeLOCK,MOVE,VIEWwork). This is a deliberate design choice.- BOM handling: Input is passed through JavaScript's
String.prototype.trim()before parsing. Per ECMA-262 §22.1.3.31,trim()consumes U+FEFF BOM characters at the start/end of the string. A header value of'\uFEFFGET'parses successfully asSet { 'GET' }. RFC 9110 does not address BOM handling; this behaviour follows the ECMAScript language spec. - The parser canonicalises lowercase input to uppercase. The serializer requires already-uppercase input (no implicit normalisation on the write path). This mirrors the typical pattern in server code where input is canonicalised once at the boundary.
allow-parsedoes not implement HTTP method execution, routing, or CORS policy enforcement — it only parses and serialises the header value.- Output order from
serializeAllowreflectsSetinsertion order; it does not sort or rank methods. - Out-of-spec characters that some HTTP libraries tolerate (digits,
_,.) are rejected. RFC 9110 method grammar permits a broadertokenset; this library takes the conservative interpretation that method tokens are uppercase ASCII letters only (consistent with howjshttp-style libraries parse other headers likeAcceptandVary).
License
MIT — see LICENSE.
