@irvinebroque/http-rfc-utils 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +222 -0
- package/dist/auth.d.ts +139 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +991 -0
- package/dist/auth.js.map +1 -0
- package/dist/cache-status.d.ts +15 -0
- package/dist/cache-status.d.ts.map +1 -0
- package/dist/cache-status.js +152 -0
- package/dist/cache-status.js.map +1 -0
- package/dist/cache.d.ts +94 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +244 -0
- package/dist/cache.js.map +1 -0
- package/dist/client-hints.d.ts +23 -0
- package/dist/client-hints.d.ts.map +1 -0
- package/dist/client-hints.js +81 -0
- package/dist/client-hints.js.map +1 -0
- package/dist/conditional.d.ts +97 -0
- package/dist/conditional.d.ts.map +1 -0
- package/dist/conditional.js +300 -0
- package/dist/conditional.js.map +1 -0
- package/dist/content-disposition.d.ts +23 -0
- package/dist/content-disposition.d.ts.map +1 -0
- package/dist/content-disposition.js +122 -0
- package/dist/content-disposition.js.map +1 -0
- package/dist/cookie.d.ts +43 -0
- package/dist/cookie.d.ts.map +1 -0
- package/dist/cookie.js +472 -0
- package/dist/cookie.js.map +1 -0
- package/dist/cors.d.ts +53 -0
- package/dist/cors.d.ts.map +1 -0
- package/dist/cors.js +170 -0
- package/dist/cors.js.map +1 -0
- package/dist/datetime.d.ts +53 -0
- package/dist/datetime.d.ts.map +1 -0
- package/dist/datetime.js +205 -0
- package/dist/datetime.js.map +1 -0
- package/dist/digest.d.ts +220 -0
- package/dist/digest.d.ts.map +1 -0
- package/dist/digest.js +355 -0
- package/dist/digest.js.map +1 -0
- package/dist/encoding.d.ts +14 -0
- package/dist/encoding.d.ts.map +1 -0
- package/dist/encoding.js +86 -0
- package/dist/encoding.js.map +1 -0
- package/dist/etag.d.ts +55 -0
- package/dist/etag.d.ts.map +1 -0
- package/dist/etag.js +182 -0
- package/dist/etag.js.map +1 -0
- package/dist/ext-value.d.ts +40 -0
- package/dist/ext-value.d.ts.map +1 -0
- package/dist/ext-value.js +119 -0
- package/dist/ext-value.js.map +1 -0
- package/dist/forwarded.d.ts +14 -0
- package/dist/forwarded.d.ts.map +1 -0
- package/dist/forwarded.js +93 -0
- package/dist/forwarded.js.map +1 -0
- package/dist/header-utils.d.ts +71 -0
- package/dist/header-utils.d.ts.map +1 -0
- package/dist/header-utils.js +143 -0
- package/dist/header-utils.js.map +1 -0
- package/dist/headers.d.ts +71 -0
- package/dist/headers.d.ts.map +1 -0
- package/dist/headers.js +134 -0
- package/dist/headers.js.map +1 -0
- package/dist/hsts.d.ts +15 -0
- package/dist/hsts.d.ts.map +1 -0
- package/dist/hsts.js +106 -0
- package/dist/hsts.js.map +1 -0
- package/dist/http-signatures.d.ts +202 -0
- package/dist/http-signatures.d.ts.map +1 -0
- package/dist/http-signatures.js +720 -0
- package/dist/http-signatures.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +125 -0
- package/dist/index.js.map +1 -0
- package/dist/json-pointer.d.ts +97 -0
- package/dist/json-pointer.d.ts.map +1 -0
- package/dist/json-pointer.js +278 -0
- package/dist/json-pointer.js.map +1 -0
- package/dist/jsonpath.d.ts +98 -0
- package/dist/jsonpath.d.ts.map +1 -0
- package/dist/jsonpath.js +1470 -0
- package/dist/jsonpath.js.map +1 -0
- package/dist/language.d.ts +14 -0
- package/dist/language.d.ts.map +1 -0
- package/dist/language.js +95 -0
- package/dist/language.js.map +1 -0
- package/dist/link.d.ts +102 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +437 -0
- package/dist/link.js.map +1 -0
- package/dist/linkset.d.ts +111 -0
- package/dist/linkset.d.ts.map +1 -0
- package/dist/linkset.js +501 -0
- package/dist/linkset.js.map +1 -0
- package/dist/negotiate.d.ts +71 -0
- package/dist/negotiate.d.ts.map +1 -0
- package/dist/negotiate.js +357 -0
- package/dist/negotiate.js.map +1 -0
- package/dist/pagination.d.ts +80 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +188 -0
- package/dist/pagination.js.map +1 -0
- package/dist/prefer.d.ts +18 -0
- package/dist/prefer.d.ts.map +1 -0
- package/dist/prefer.js +93 -0
- package/dist/prefer.js.map +1 -0
- package/dist/problem.d.ts +54 -0
- package/dist/problem.d.ts.map +1 -0
- package/dist/problem.js +104 -0
- package/dist/problem.js.map +1 -0
- package/dist/proxy-status.d.ts +28 -0
- package/dist/proxy-status.d.ts.map +1 -0
- package/dist/proxy-status.js +220 -0
- package/dist/proxy-status.js.map +1 -0
- package/dist/range.d.ts +28 -0
- package/dist/range.d.ts.map +1 -0
- package/dist/range.js +243 -0
- package/dist/range.js.map +1 -0
- package/dist/response.d.ts +101 -0
- package/dist/response.d.ts.map +1 -0
- package/dist/response.js +200 -0
- package/dist/response.js.map +1 -0
- package/dist/sorting.d.ts +66 -0
- package/dist/sorting.d.ts.map +1 -0
- package/dist/sorting.js +168 -0
- package/dist/sorting.js.map +1 -0
- package/dist/structured-fields.d.ts +30 -0
- package/dist/structured-fields.d.ts.map +1 -0
- package/dist/structured-fields.js +468 -0
- package/dist/structured-fields.js.map +1 -0
- package/dist/types.d.ts +772 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/uri-template.d.ts +48 -0
- package/dist/uri-template.d.ts.map +1 -0
- package/dist/uri-template.js +483 -0
- package/dist/uri-template.js.map +1 -0
- package/dist/uri.d.ts +80 -0
- package/dist/uri.d.ts.map +1 -0
- package/dist/uri.js +423 -0
- package/dist/uri.js.map +1 -0
- package/package.json +66 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared HTTP header parsing utilities.
|
|
3
|
+
* Common patterns per RFC 9110 §5.6.2 (tokens), §5.6.4 (quoted-strings).
|
|
4
|
+
* @internal - not exported from the public API
|
|
5
|
+
* @see https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* RFC 9110 §5.6.2: token characters.
|
|
9
|
+
* token = 1*tchar
|
|
10
|
+
* tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
|
|
11
|
+
* "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
|
|
12
|
+
*/
|
|
13
|
+
export const TOKEN_CHARS = /^[!#$%&'*+\-.^_`|~A-Za-z0-9]+$/;
|
|
14
|
+
/**
|
|
15
|
+
* RFC 9110 §12.4.2: q-value grammar.
|
|
16
|
+
* qvalue = ( "0" [ "." 0*3DIGIT ] ) / ( "1" [ "." 0*3("0") ] )
|
|
17
|
+
*/
|
|
18
|
+
export const QVALUE_REGEX = /^(?:0(?:\.\d{0,3})?|1(?:\.0{0,3})?)$/;
|
|
19
|
+
/**
|
|
20
|
+
* Check if a header value is empty or whitespace-only.
|
|
21
|
+
*
|
|
22
|
+
* @param header - The header value to check
|
|
23
|
+
* @returns True if the header is null, undefined, empty, or whitespace-only
|
|
24
|
+
*/
|
|
25
|
+
export function isEmptyHeader(header) {
|
|
26
|
+
return !header || !header.trim();
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Split a header value by delimiter, respecting quoted strings.
|
|
30
|
+
* Handles escape sequences within quoted strings per RFC 9110 §5.6.4.
|
|
31
|
+
*
|
|
32
|
+
* RFC 9110 §5.6.4: quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
|
|
33
|
+
*
|
|
34
|
+
* @param value - The header value to split
|
|
35
|
+
* @param delimiter - The delimiter character (typically ',' or ';')
|
|
36
|
+
* @returns Array of split parts (not trimmed)
|
|
37
|
+
*/
|
|
38
|
+
// RFC 9110 §5.6.4: quoted-string and quoted-pair handling.
|
|
39
|
+
export function splitQuotedValue(value, delimiter) {
|
|
40
|
+
const parts = [];
|
|
41
|
+
let current = '';
|
|
42
|
+
let inQuotes = false;
|
|
43
|
+
let escaped = false;
|
|
44
|
+
for (const char of value) {
|
|
45
|
+
if (escaped) {
|
|
46
|
+
current += char;
|
|
47
|
+
escaped = false;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (char === '\\' && inQuotes) {
|
|
51
|
+
current += char;
|
|
52
|
+
escaped = true;
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (char === '"') {
|
|
56
|
+
inQuotes = !inQuotes;
|
|
57
|
+
current += char;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (char === delimiter && !inQuotes) {
|
|
61
|
+
parts.push(current);
|
|
62
|
+
current = '';
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
current += char;
|
|
66
|
+
}
|
|
67
|
+
parts.push(current);
|
|
68
|
+
return parts;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Simple comma-split for headers that don't contain quoted values.
|
|
72
|
+
* Use this for simple list headers like Accept-Language, Accept-Encoding.
|
|
73
|
+
*
|
|
74
|
+
* @param value - The header value to split
|
|
75
|
+
* @returns Array of trimmed, non-empty parts
|
|
76
|
+
*/
|
|
77
|
+
export function splitListValue(value) {
|
|
78
|
+
return value.split(',').map(part => part.trim()).filter(Boolean);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Unquote a quoted-string value, handling escape sequences.
|
|
82
|
+
* If the value is not quoted, returns it trimmed.
|
|
83
|
+
*
|
|
84
|
+
* RFC 9110 §5.6.4: quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
|
|
85
|
+
*
|
|
86
|
+
* @param value - The potentially quoted value
|
|
87
|
+
* @returns Unquoted value with escapes resolved
|
|
88
|
+
*/
|
|
89
|
+
// RFC 9110 §5.6.4: quoted-string unescaping.
|
|
90
|
+
export function unquote(value) {
|
|
91
|
+
const trimmed = value.trim();
|
|
92
|
+
if (!trimmed.startsWith('"') || !trimmed.endsWith('"') || trimmed.length < 2) {
|
|
93
|
+
return trimmed;
|
|
94
|
+
}
|
|
95
|
+
const inner = trimmed.slice(1, -1);
|
|
96
|
+
let result = '';
|
|
97
|
+
let i = 0;
|
|
98
|
+
while (i < inner.length) {
|
|
99
|
+
if (inner[i] === '\\' && i + 1 < inner.length) {
|
|
100
|
+
result += inner[i + 1];
|
|
101
|
+
i += 2;
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
result += inner[i];
|
|
105
|
+
i++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Quote a value if it contains non-token characters.
|
|
112
|
+
* Escapes backslashes and double quotes within the value.
|
|
113
|
+
*
|
|
114
|
+
* @param value - The value to potentially quote
|
|
115
|
+
* @returns Token value as-is, or quoted-string if needed
|
|
116
|
+
*/
|
|
117
|
+
// RFC 9110 §5.6.2, §5.6.4: token vs quoted-string selection.
|
|
118
|
+
export function quoteIfNeeded(value) {
|
|
119
|
+
if (value === '') {
|
|
120
|
+
return '""';
|
|
121
|
+
}
|
|
122
|
+
if (TOKEN_CHARS.test(value)) {
|
|
123
|
+
return value;
|
|
124
|
+
}
|
|
125
|
+
const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
126
|
+
return `"${escaped}"`;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Parse a q-value (quality value) from a string.
|
|
130
|
+
* Returns null if the value is invalid per RFC 9110 §12.4.2.
|
|
131
|
+
*
|
|
132
|
+
* @param value - The q-value string (e.g., "0.9", "1", "0.123")
|
|
133
|
+
* @returns Parsed number between 0 and 1, or null if invalid
|
|
134
|
+
*/
|
|
135
|
+
// RFC 9110 §12.4.2: qvalue parsing.
|
|
136
|
+
export function parseQValue(value) {
|
|
137
|
+
const trimmed = value.trim();
|
|
138
|
+
if (!QVALUE_REGEX.test(trimmed)) {
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
return Number(trimmed);
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=header-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"header-utils.js","sourceRoot":"","sources":["../src/header-utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Misc header utilities per RFC 9110, RFC 8594.
|
|
3
|
+
* RFC 9110 §10.2.3, §12.5.5.
|
|
4
|
+
* RFC 8594 §3 (Sunset header).
|
|
5
|
+
* @see https://www.rfc-editor.org/rfc/rfc8594.html#section-3
|
|
6
|
+
*/
|
|
7
|
+
import type { RetryAfterValue } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Parse Retry-After header value.
|
|
10
|
+
*/
|
|
11
|
+
export declare function parseRetryAfter(value: string): RetryAfterValue | null;
|
|
12
|
+
/**
|
|
13
|
+
* Format Retry-After header value.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatRetryAfter(value: Date | number): string;
|
|
16
|
+
/**
|
|
17
|
+
* Merge Vary header values, preserving order and handling '*'.
|
|
18
|
+
*/
|
|
19
|
+
export declare function mergeVary(existing: string | null, add: string | string[]): string;
|
|
20
|
+
/**
|
|
21
|
+
* Parse Sunset header value.
|
|
22
|
+
*
|
|
23
|
+
* The Sunset header indicates when a resource is expected to become
|
|
24
|
+
* unresponsive. Returns null for invalid or missing values.
|
|
25
|
+
*
|
|
26
|
+
* Per RFC 8594 Section 3, timestamps in the past mean the resource
|
|
27
|
+
* is expected to become unavailable at any time.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const sunset = parseSunset('Wed, 11 Nov 2026 11:11:11 GMT');
|
|
32
|
+
* if (sunset && sunset < new Date()) {
|
|
33
|
+
* console.warn('Resource sunset has passed');
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseSunset(value: string): Date | null;
|
|
38
|
+
/**
|
|
39
|
+
* Format a Date as a Sunset header value.
|
|
40
|
+
*
|
|
41
|
+
* Uses IMF-fixdate format per RFC 9110 Section 5.6.7.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const header = formatSunset(new Date('2026-12-31T23:59:59Z'));
|
|
46
|
+
* // "Thu, 31 Dec 2026 23:59:59 GMT"
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare function formatSunset(date: Date): string;
|
|
50
|
+
/**
|
|
51
|
+
* Check if a sunset date is imminent (approaching or past).
|
|
52
|
+
*
|
|
53
|
+
* Per RFC 8594 Section 3, timestamps in the past mean the resource
|
|
54
|
+
* is expected to become unavailable at any time. This helper checks
|
|
55
|
+
* if the sunset is within a given threshold of the current time.
|
|
56
|
+
*
|
|
57
|
+
* @param sunset - Parsed sunset date (null if no Sunset header)
|
|
58
|
+
* @param thresholdMs - Threshold in milliseconds (default: 0, meaning only past dates)
|
|
59
|
+
* @returns true if sunset is null-safe past or within threshold
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```ts
|
|
63
|
+
* const sunset = parseSunset(response.headers.get('Sunset') ?? '');
|
|
64
|
+
* // Check if sunset is within 7 days
|
|
65
|
+
* if (isSunsetImminent(sunset, 7 * 24 * 60 * 60 * 1000)) {
|
|
66
|
+
* console.warn('Resource will sunset within 7 days');
|
|
67
|
+
* }
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function isSunsetImminent(sunset: Date | null, thresholdMs?: number): boolean;
|
|
71
|
+
//# sourceMappingURL=headers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headers.d.ts","sourceRoot":"","sources":["../src/headers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD;;GAEG;AAEH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,CAiBrE;AAED;;GAEG;AAEH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAO7D;AAED;;GAEG;AAEH,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,CA8BjF;AAED;;;;;;;;;;;;;;;;GAgBG;AAEH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAMtD;AAED;;;;;;;;;;GAUG;AAEH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE/C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,WAAW,GAAE,MAAU,GAAG,OAAO,CAMtF"}
|
package/dist/headers.js
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Misc header utilities per RFC 9110, RFC 8594.
|
|
3
|
+
* RFC 9110 §10.2.3, §12.5.5.
|
|
4
|
+
* RFC 8594 §3 (Sunset header).
|
|
5
|
+
* @see https://www.rfc-editor.org/rfc/rfc8594.html#section-3
|
|
6
|
+
*/
|
|
7
|
+
import { parseHTTPDate, formatHTTPDate } from './datetime.js';
|
|
8
|
+
/**
|
|
9
|
+
* Parse Retry-After header value.
|
|
10
|
+
*/
|
|
11
|
+
// RFC 9110 §10.2.3: Retry-After parsing.
|
|
12
|
+
export function parseRetryAfter(value) {
|
|
13
|
+
if (!value || !value.trim()) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
const trimmed = value.trim();
|
|
17
|
+
if (/^\d+$/.test(trimmed)) {
|
|
18
|
+
const seconds = parseInt(trimmed, 10);
|
|
19
|
+
return { delaySeconds: Math.max(0, seconds) };
|
|
20
|
+
}
|
|
21
|
+
const date = parseHTTPDate(trimmed);
|
|
22
|
+
if (date) {
|
|
23
|
+
return { date };
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Format Retry-After header value.
|
|
29
|
+
*/
|
|
30
|
+
// RFC 9110 §10.2.3: Retry-After formatting.
|
|
31
|
+
export function formatRetryAfter(value) {
|
|
32
|
+
if (typeof value === 'number') {
|
|
33
|
+
const seconds = Math.max(0, Math.floor(value));
|
|
34
|
+
return String(seconds);
|
|
35
|
+
}
|
|
36
|
+
return formatHTTPDate(value);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Merge Vary header values, preserving order and handling '*'.
|
|
40
|
+
*/
|
|
41
|
+
// RFC 9110 §12.5.5: Vary field-value combination.
|
|
42
|
+
export function mergeVary(existing, add) {
|
|
43
|
+
const existingValues = existing ? existing.split(',').map(v => v.trim()).filter(Boolean) : [];
|
|
44
|
+
const addValues = Array.isArray(add)
|
|
45
|
+
? add
|
|
46
|
+
: add.split(',').map(v => v.trim()).filter(Boolean);
|
|
47
|
+
if (existingValues.includes('*') || addValues.includes('*')) {
|
|
48
|
+
return '*';
|
|
49
|
+
}
|
|
50
|
+
const seen = new Set();
|
|
51
|
+
const combined = [];
|
|
52
|
+
for (const value of existingValues) {
|
|
53
|
+
const key = value.toLowerCase();
|
|
54
|
+
if (!seen.has(key)) {
|
|
55
|
+
seen.add(key);
|
|
56
|
+
combined.push(value);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
for (const value of addValues) {
|
|
60
|
+
const key = value.toLowerCase();
|
|
61
|
+
if (!seen.has(key)) {
|
|
62
|
+
seen.add(key);
|
|
63
|
+
combined.push(value);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return combined.join(', ');
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Parse Sunset header value.
|
|
70
|
+
*
|
|
71
|
+
* The Sunset header indicates when a resource is expected to become
|
|
72
|
+
* unresponsive. Returns null for invalid or missing values.
|
|
73
|
+
*
|
|
74
|
+
* Per RFC 8594 Section 3, timestamps in the past mean the resource
|
|
75
|
+
* is expected to become unavailable at any time.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* const sunset = parseSunset('Wed, 11 Nov 2026 11:11:11 GMT');
|
|
80
|
+
* if (sunset && sunset < new Date()) {
|
|
81
|
+
* console.warn('Resource sunset has passed');
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
// RFC 8594 §3: Sunset = HTTP-date.
|
|
86
|
+
export function parseSunset(value) {
|
|
87
|
+
if (!value || !value.trim()) {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
return parseHTTPDate(value.trim());
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Format a Date as a Sunset header value.
|
|
94
|
+
*
|
|
95
|
+
* Uses IMF-fixdate format per RFC 9110 Section 5.6.7.
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* const header = formatSunset(new Date('2026-12-31T23:59:59Z'));
|
|
100
|
+
* // "Thu, 31 Dec 2026 23:59:59 GMT"
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
// RFC 8594 §3: Sunset header uses HTTP-date format.
|
|
104
|
+
export function formatSunset(date) {
|
|
105
|
+
return formatHTTPDate(date);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Check if a sunset date is imminent (approaching or past).
|
|
109
|
+
*
|
|
110
|
+
* Per RFC 8594 Section 3, timestamps in the past mean the resource
|
|
111
|
+
* is expected to become unavailable at any time. This helper checks
|
|
112
|
+
* if the sunset is within a given threshold of the current time.
|
|
113
|
+
*
|
|
114
|
+
* @param sunset - Parsed sunset date (null if no Sunset header)
|
|
115
|
+
* @param thresholdMs - Threshold in milliseconds (default: 0, meaning only past dates)
|
|
116
|
+
* @returns true if sunset is null-safe past or within threshold
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```ts
|
|
120
|
+
* const sunset = parseSunset(response.headers.get('Sunset') ?? '');
|
|
121
|
+
* // Check if sunset is within 7 days
|
|
122
|
+
* if (isSunsetImminent(sunset, 7 * 24 * 60 * 60 * 1000)) {
|
|
123
|
+
* console.warn('Resource will sunset within 7 days');
|
|
124
|
+
* }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
// RFC 8594 §3: Past timestamps mean "now"; helper for threshold checks.
|
|
128
|
+
export function isSunsetImminent(sunset, thresholdMs = 0) {
|
|
129
|
+
if (!sunset) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
return sunset.getTime() <= Date.now() + thresholdMs;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=headers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"headers.js","sourceRoot":"","sources":["../src/headers.ts"],"names":[],"mappings":"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"}
|
package/dist/hsts.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict-Transport-Security utilities per RFC 6797.
|
|
3
|
+
* RFC 6797 §6.1, §6.1.1, §6.1.2, §8.1.
|
|
4
|
+
* @see https://www.rfc-editor.org/rfc/rfc6797.html#section-6.1
|
|
5
|
+
*/
|
|
6
|
+
import type { StrictTransportSecurityOptions } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* Parse Strict-Transport-Security header value.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseStrictTransportSecurity(header: string): StrictTransportSecurityOptions | null;
|
|
11
|
+
/**
|
|
12
|
+
* Format Strict-Transport-Security header value.
|
|
13
|
+
*/
|
|
14
|
+
export declare function formatStrictTransportSecurity(options: StrictTransportSecurityOptions): string;
|
|
15
|
+
//# sourceMappingURL=hsts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hsts.d.ts","sourceRoot":"","sources":["../src/hsts.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,YAAY,CAAC;AAgCjE;;GAEG;AAEH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,8BAA8B,GAAG,IAAI,CAiElG;AAED;;GAEG;AAEH,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,8BAA8B,GAAG,MAAM,CAO7F"}
|
package/dist/hsts.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strict-Transport-Security utilities per RFC 6797.
|
|
3
|
+
* RFC 6797 §6.1, §6.1.1, §6.1.2, §8.1.
|
|
4
|
+
* @see https://www.rfc-editor.org/rfc/rfc6797.html#section-6.1
|
|
5
|
+
*/
|
|
6
|
+
import { TOKEN_CHARS, isEmptyHeader, splitQuotedValue, unquote } from './header-utils.js';
|
|
7
|
+
/**
|
|
8
|
+
* Unquote strictly - returns null if not a valid quoted string.
|
|
9
|
+
* Used for HSTS directive values where unquoted values are expected.
|
|
10
|
+
*/
|
|
11
|
+
function unquoteStrict(value) {
|
|
12
|
+
const trimmed = value.trim();
|
|
13
|
+
if (!trimmed.startsWith('"') || !trimmed.endsWith('"') || trimmed.length < 2) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return unquote(trimmed);
|
|
17
|
+
}
|
|
18
|
+
function parseDirectiveValue(value) {
|
|
19
|
+
const trimmed = value.trim();
|
|
20
|
+
if (!trimmed) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (trimmed.startsWith('"')) {
|
|
24
|
+
return unquoteStrict(trimmed);
|
|
25
|
+
}
|
|
26
|
+
if (!TOKEN_CHARS.test(trimmed)) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return trimmed;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Parse Strict-Transport-Security header value.
|
|
33
|
+
*/
|
|
34
|
+
// RFC 6797 §6.1, §6.1.1, §6.1.2, §8.1: STS parsing and invalid header handling.
|
|
35
|
+
export function parseStrictTransportSecurity(header) {
|
|
36
|
+
if (isEmptyHeader(header)) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
const directives = splitQuotedValue(header, ';');
|
|
40
|
+
const seen = new Set();
|
|
41
|
+
let maxAge = null;
|
|
42
|
+
let includeSubDomains = false;
|
|
43
|
+
for (const directive of directives) {
|
|
44
|
+
const trimmed = directive.trim();
|
|
45
|
+
if (!trimmed) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const eqIndex = trimmed.indexOf('=');
|
|
49
|
+
const name = (eqIndex === -1 ? trimmed : trimmed.slice(0, eqIndex)).trim();
|
|
50
|
+
if (!name || !TOKEN_CHARS.test(name)) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
const lowerName = name.toLowerCase();
|
|
54
|
+
if (seen.has(lowerName)) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
seen.add(lowerName);
|
|
58
|
+
if (eqIndex === -1) {
|
|
59
|
+
if (lowerName === 'includesubdomains') {
|
|
60
|
+
includeSubDomains = true;
|
|
61
|
+
}
|
|
62
|
+
else if (lowerName === 'max-age') {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
const rawValue = trimmed.slice(eqIndex + 1).trim();
|
|
68
|
+
const value = parseDirectiveValue(rawValue);
|
|
69
|
+
if (value === null) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
if (lowerName === 'max-age') {
|
|
73
|
+
if (!/^\d+$/.test(value)) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
const parsed = Number(value);
|
|
77
|
+
if (!Number.isFinite(parsed)) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
maxAge = parsed;
|
|
81
|
+
}
|
|
82
|
+
else if (lowerName === 'includesubdomains') {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (maxAge === null) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
maxAge,
|
|
91
|
+
includeSubDomains: includeSubDomains ? true : undefined,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Format Strict-Transport-Security header value.
|
|
96
|
+
*/
|
|
97
|
+
// RFC 6797 §6.1: STS field-value formatting.
|
|
98
|
+
export function formatStrictTransportSecurity(options) {
|
|
99
|
+
const maxAge = Math.max(0, Math.floor(options.maxAge));
|
|
100
|
+
const parts = [`max-age=${maxAge}`];
|
|
101
|
+
if (options.includeSubDomains) {
|
|
102
|
+
parts.push('includeSubDomains');
|
|
103
|
+
}
|
|
104
|
+
return parts.join('; ');
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=hsts.js.map
|
package/dist/hsts.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hsts.js","sourceRoot":"","sources":["../src/hsts.ts"],"names":[],"mappings":"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"}
|