@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.
Files changed (147) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +222 -0
  3. package/dist/auth.d.ts +139 -0
  4. package/dist/auth.d.ts.map +1 -0
  5. package/dist/auth.js +991 -0
  6. package/dist/auth.js.map +1 -0
  7. package/dist/cache-status.d.ts +15 -0
  8. package/dist/cache-status.d.ts.map +1 -0
  9. package/dist/cache-status.js +152 -0
  10. package/dist/cache-status.js.map +1 -0
  11. package/dist/cache.d.ts +94 -0
  12. package/dist/cache.d.ts.map +1 -0
  13. package/dist/cache.js +244 -0
  14. package/dist/cache.js.map +1 -0
  15. package/dist/client-hints.d.ts +23 -0
  16. package/dist/client-hints.d.ts.map +1 -0
  17. package/dist/client-hints.js +81 -0
  18. package/dist/client-hints.js.map +1 -0
  19. package/dist/conditional.d.ts +97 -0
  20. package/dist/conditional.d.ts.map +1 -0
  21. package/dist/conditional.js +300 -0
  22. package/dist/conditional.js.map +1 -0
  23. package/dist/content-disposition.d.ts +23 -0
  24. package/dist/content-disposition.d.ts.map +1 -0
  25. package/dist/content-disposition.js +122 -0
  26. package/dist/content-disposition.js.map +1 -0
  27. package/dist/cookie.d.ts +43 -0
  28. package/dist/cookie.d.ts.map +1 -0
  29. package/dist/cookie.js +472 -0
  30. package/dist/cookie.js.map +1 -0
  31. package/dist/cors.d.ts +53 -0
  32. package/dist/cors.d.ts.map +1 -0
  33. package/dist/cors.js +170 -0
  34. package/dist/cors.js.map +1 -0
  35. package/dist/datetime.d.ts +53 -0
  36. package/dist/datetime.d.ts.map +1 -0
  37. package/dist/datetime.js +205 -0
  38. package/dist/datetime.js.map +1 -0
  39. package/dist/digest.d.ts +220 -0
  40. package/dist/digest.d.ts.map +1 -0
  41. package/dist/digest.js +355 -0
  42. package/dist/digest.js.map +1 -0
  43. package/dist/encoding.d.ts +14 -0
  44. package/dist/encoding.d.ts.map +1 -0
  45. package/dist/encoding.js +86 -0
  46. package/dist/encoding.js.map +1 -0
  47. package/dist/etag.d.ts +55 -0
  48. package/dist/etag.d.ts.map +1 -0
  49. package/dist/etag.js +182 -0
  50. package/dist/etag.js.map +1 -0
  51. package/dist/ext-value.d.ts +40 -0
  52. package/dist/ext-value.d.ts.map +1 -0
  53. package/dist/ext-value.js +119 -0
  54. package/dist/ext-value.js.map +1 -0
  55. package/dist/forwarded.d.ts +14 -0
  56. package/dist/forwarded.d.ts.map +1 -0
  57. package/dist/forwarded.js +93 -0
  58. package/dist/forwarded.js.map +1 -0
  59. package/dist/header-utils.d.ts +71 -0
  60. package/dist/header-utils.d.ts.map +1 -0
  61. package/dist/header-utils.js +143 -0
  62. package/dist/header-utils.js.map +1 -0
  63. package/dist/headers.d.ts +71 -0
  64. package/dist/headers.d.ts.map +1 -0
  65. package/dist/headers.js +134 -0
  66. package/dist/headers.js.map +1 -0
  67. package/dist/hsts.d.ts +15 -0
  68. package/dist/hsts.d.ts.map +1 -0
  69. package/dist/hsts.js +106 -0
  70. package/dist/hsts.js.map +1 -0
  71. package/dist/http-signatures.d.ts +202 -0
  72. package/dist/http-signatures.d.ts.map +1 -0
  73. package/dist/http-signatures.js +720 -0
  74. package/dist/http-signatures.js.map +1 -0
  75. package/dist/index.d.ts +41 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +125 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/json-pointer.d.ts +97 -0
  80. package/dist/json-pointer.d.ts.map +1 -0
  81. package/dist/json-pointer.js +278 -0
  82. package/dist/json-pointer.js.map +1 -0
  83. package/dist/jsonpath.d.ts +98 -0
  84. package/dist/jsonpath.d.ts.map +1 -0
  85. package/dist/jsonpath.js +1470 -0
  86. package/dist/jsonpath.js.map +1 -0
  87. package/dist/language.d.ts +14 -0
  88. package/dist/language.d.ts.map +1 -0
  89. package/dist/language.js +95 -0
  90. package/dist/language.js.map +1 -0
  91. package/dist/link.d.ts +102 -0
  92. package/dist/link.d.ts.map +1 -0
  93. package/dist/link.js +437 -0
  94. package/dist/link.js.map +1 -0
  95. package/dist/linkset.d.ts +111 -0
  96. package/dist/linkset.d.ts.map +1 -0
  97. package/dist/linkset.js +501 -0
  98. package/dist/linkset.js.map +1 -0
  99. package/dist/negotiate.d.ts +71 -0
  100. package/dist/negotiate.d.ts.map +1 -0
  101. package/dist/negotiate.js +357 -0
  102. package/dist/negotiate.js.map +1 -0
  103. package/dist/pagination.d.ts +80 -0
  104. package/dist/pagination.d.ts.map +1 -0
  105. package/dist/pagination.js +188 -0
  106. package/dist/pagination.js.map +1 -0
  107. package/dist/prefer.d.ts +18 -0
  108. package/dist/prefer.d.ts.map +1 -0
  109. package/dist/prefer.js +93 -0
  110. package/dist/prefer.js.map +1 -0
  111. package/dist/problem.d.ts +54 -0
  112. package/dist/problem.d.ts.map +1 -0
  113. package/dist/problem.js +104 -0
  114. package/dist/problem.js.map +1 -0
  115. package/dist/proxy-status.d.ts +28 -0
  116. package/dist/proxy-status.d.ts.map +1 -0
  117. package/dist/proxy-status.js +220 -0
  118. package/dist/proxy-status.js.map +1 -0
  119. package/dist/range.d.ts +28 -0
  120. package/dist/range.d.ts.map +1 -0
  121. package/dist/range.js +243 -0
  122. package/dist/range.js.map +1 -0
  123. package/dist/response.d.ts +101 -0
  124. package/dist/response.d.ts.map +1 -0
  125. package/dist/response.js +200 -0
  126. package/dist/response.js.map +1 -0
  127. package/dist/sorting.d.ts +66 -0
  128. package/dist/sorting.d.ts.map +1 -0
  129. package/dist/sorting.js +168 -0
  130. package/dist/sorting.js.map +1 -0
  131. package/dist/structured-fields.d.ts +30 -0
  132. package/dist/structured-fields.d.ts.map +1 -0
  133. package/dist/structured-fields.js +468 -0
  134. package/dist/structured-fields.js.map +1 -0
  135. package/dist/types.d.ts +772 -0
  136. package/dist/types.d.ts.map +1 -0
  137. package/dist/types.js +8 -0
  138. package/dist/types.js.map +1 -0
  139. package/dist/uri-template.d.ts +48 -0
  140. package/dist/uri-template.d.ts.map +1 -0
  141. package/dist/uri-template.js +483 -0
  142. package/dist/uri-template.js.map +1 -0
  143. package/dist/uri.d.ts +80 -0
  144. package/dist/uri.d.ts.map +1 -0
  145. package/dist/uri.js +423 -0
  146. package/dist/uri.js.map +1 -0
  147. package/package.json +66 -0
package/dist/cors.js ADDED
@@ -0,0 +1,170 @@
1
+ /**
2
+ * CORS helpers aligned with Fetch/CORS specs.
3
+ */
4
+ /**
5
+ * Default CORS headers for permissive API access.
6
+ * Used when no specific options are provided.
7
+ */
8
+ export const defaultCorsHeaders = {
9
+ 'Access-Control-Allow-Origin': '*',
10
+ 'Access-Control-Allow-Methods': 'GET, HEAD, OPTIONS',
11
+ 'Access-Control-Allow-Headers': 'Content-Type, Accept, If-None-Match, If-Modified-Since',
12
+ 'Access-Control-Expose-Headers': 'ETag, Last-Modified, Link, X-Total-Count',
13
+ 'Access-Control-Max-Age': '86400',
14
+ };
15
+ /**
16
+ * Validate CORS options and throw if invalid.
17
+ * Called internally by buildCorsHeaders.
18
+ */
19
+ function validateCorsOptions(options) {
20
+ if (options.credentials && (options.origin === '*' || options.origin === undefined)) {
21
+ throw new Error('CORS error: Cannot use wildcard or undefined origin with credentials. ' +
22
+ 'Specify explicit origin(s) when credentials is true.');
23
+ }
24
+ }
25
+ function validateSingleOrigin(options) {
26
+ if (Array.isArray(options.origin)) {
27
+ throw new Error('CORS error: Multiple origins are not valid in Access-Control-Allow-Origin. ' +
28
+ 'Use buildCorsHeadersForOrigin(requestOrigin, options) with an allowlist.');
29
+ }
30
+ }
31
+ /**
32
+ * Build CORS headers from options.
33
+ *
34
+ * @param options - CORS configuration
35
+ * @returns Headers object
36
+ *
37
+ * Behavior:
38
+ * - origin: '*' | string - sets Access-Control-Allow-Origin
39
+ * - For multiple origins, use buildCorsHeadersForOrigin(requestOrigin, options)
40
+ * - methods: string[] - sets Access-Control-Allow-Methods
41
+ * - allowHeaders: string[] - sets Access-Control-Allow-Headers
42
+ * - exposeHeaders: string[] - sets Access-Control-Expose-Headers
43
+ * - credentials: boolean - if true, sets Access-Control-Allow-Credentials: true
44
+ * - WARNING: Cannot use '*' for origin when credentials is true
45
+ * - maxAge: number - sets Access-Control-Max-Age in seconds
46
+ */
47
+ export function buildCorsHeaders(options) {
48
+ if (!options) {
49
+ return { ...defaultCorsHeaders };
50
+ }
51
+ validateCorsOptions(options);
52
+ validateSingleOrigin(options);
53
+ const headers = {};
54
+ // Handle origin
55
+ if (options.origin !== undefined) {
56
+ headers['Access-Control-Allow-Origin'] = options.origin;
57
+ }
58
+ else {
59
+ headers['Access-Control-Allow-Origin'] = '*';
60
+ }
61
+ // Handle methods
62
+ if (options.methods !== undefined) {
63
+ headers['Access-Control-Allow-Methods'] = options.methods.join(', ');
64
+ }
65
+ else {
66
+ headers['Access-Control-Allow-Methods'] = 'GET, HEAD, OPTIONS';
67
+ }
68
+ // Handle allowHeaders
69
+ if (options.allowHeaders !== undefined) {
70
+ headers['Access-Control-Allow-Headers'] = options.allowHeaders.join(', ');
71
+ }
72
+ else {
73
+ headers['Access-Control-Allow-Headers'] = 'Content-Type, Accept, If-None-Match, If-Modified-Since';
74
+ }
75
+ // Handle exposeHeaders
76
+ if (options.exposeHeaders !== undefined) {
77
+ headers['Access-Control-Expose-Headers'] = options.exposeHeaders.join(', ');
78
+ }
79
+ else {
80
+ headers['Access-Control-Expose-Headers'] = 'ETag, Last-Modified, Link, X-Total-Count';
81
+ }
82
+ // Handle credentials
83
+ if (options.credentials) {
84
+ headers['Access-Control-Allow-Credentials'] = 'true';
85
+ }
86
+ // Handle maxAge
87
+ if (options.maxAge !== undefined) {
88
+ headers['Access-Control-Max-Age'] = String(options.maxAge);
89
+ }
90
+ else {
91
+ headers['Access-Control-Max-Age'] = '86400';
92
+ }
93
+ return headers;
94
+ }
95
+ /**
96
+ * Build CORS headers for a preflight response (OPTIONS request).
97
+ * Same as buildCorsHeaders but always includes max-age.
98
+ */
99
+ export function buildPreflightHeaders(options) {
100
+ const headers = buildCorsHeaders(options);
101
+ // Ensure max-age is always present for preflight
102
+ if (!headers['Access-Control-Max-Age']) {
103
+ headers['Access-Control-Max-Age'] = '86400';
104
+ }
105
+ return headers;
106
+ }
107
+ /**
108
+ * Build CORS headers for a specific request origin.
109
+ *
110
+ * Use this when you support multiple origins. It echoes the request origin
111
+ * and adds Vary: Origin for correct caching behavior.
112
+ */
113
+ export function buildCorsHeadersForOrigin(requestOrigin, options) {
114
+ if (!options) {
115
+ return { ...defaultCorsHeaders };
116
+ }
117
+ validateCorsOptions(options);
118
+ if (options.origin === undefined || options.origin === '*') {
119
+ return buildCorsHeaders(options);
120
+ }
121
+ if (!requestOrigin) {
122
+ return {};
123
+ }
124
+ if (!isOriginAllowed(requestOrigin, options)) {
125
+ return {};
126
+ }
127
+ const headers = buildCorsHeaders({
128
+ ...options,
129
+ origin: requestOrigin,
130
+ });
131
+ if (headers['Vary']) {
132
+ const existing = headers['Vary']
133
+ .split(',')
134
+ .map(value => value.trim())
135
+ .filter(Boolean);
136
+ if (!existing.includes('Origin')) {
137
+ existing.push('Origin');
138
+ }
139
+ headers['Vary'] = existing.join(', ');
140
+ }
141
+ else {
142
+ headers['Vary'] = 'Origin';
143
+ }
144
+ return headers;
145
+ }
146
+ /**
147
+ * Check if an origin is allowed based on CorsOptions.
148
+ * Useful for dynamic origin validation.
149
+ *
150
+ * @param requestOrigin - The Origin header from the request
151
+ * @param options - CORS configuration
152
+ * @returns true if origin is allowed
153
+ */
154
+ export function isOriginAllowed(requestOrigin, options) {
155
+ if (!options || options.origin === undefined || options.origin === '*') {
156
+ return true;
157
+ }
158
+ if (Array.isArray(options.origin)) {
159
+ return options.origin.includes(requestOrigin);
160
+ }
161
+ return options.origin === requestOrigin;
162
+ }
163
+ /**
164
+ * Legacy alias for backward compatibility.
165
+ * Returns defaultCorsHeaders.
166
+ */
167
+ export function corsHeaders() {
168
+ return { ...defaultCorsHeaders };
169
+ }
170
+ //# sourceMappingURL=cors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cors.js","sourceRoot":"","sources":["../src/cors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Date/time utilities for HTTP headers.
3
+ * RFC 3339 §5.6, RFC 9110 §5.6.7, RFC 850 §2.
4
+ */
5
+ /**
6
+ * Format a Date as RFC 3339 timestamp (ISO 8601 compatible)
7
+ * Example output: "2025-02-01T12:30:45.123Z"
8
+ *
9
+ * This is the format used in JSON responses.
10
+ */
11
+ export declare function toRFC3339(date: Date): string;
12
+ /**
13
+ * Parse an RFC 3339 timestamp string to Date
14
+ * Returns null for invalid format
15
+ *
16
+ * Accepts:
17
+ * - "2025-02-01T12:30:45Z"
18
+ * - "2025-02-01T12:30:45.123Z"
19
+ * - "2025-02-01T12:30:45+00:00"
20
+ * - "2025-02-01T12:30:45.123+05:30"
21
+ * - "2025-02-01t12:30:45z" (lowercase per §5.6 NOTE)
22
+ * - "2025-02-01 12:30:45Z" (space separator per §5.6 NOTE)
23
+ */
24
+ export declare function parseRFC3339(str: string): Date | null;
25
+ /**
26
+ * Format a Date as HTTP-date per RFC 9110 Section 5.6.7
27
+ * Used for Last-Modified, Date, Expires headers.
28
+ *
29
+ * Format: "Sat, 01 Feb 2025 12:30:45 GMT"
30
+ * (IMF-fixdate format - the preferred format)
31
+ */
32
+ export declare function formatHTTPDate(date: Date): string;
33
+ /**
34
+ * Parse an HTTP-date string to Date
35
+ *
36
+ * RFC 9110 Section 5.6.7 requires parsing these formats:
37
+ * 1. IMF-fixdate: "Sun, 06 Nov 1994 08:49:37 GMT" (preferred)
38
+ * 2. RFC 850: "Sunday, 06-Nov-94 08:49:37 GMT" (obsolete)
39
+ * 3. ANSI C asctime(): "Sun Nov 6 08:49:37 1994" (obsolete)
40
+ *
41
+ * Returns null for invalid format.
42
+ */
43
+ export declare function parseHTTPDate(str: string): Date | null;
44
+ /**
45
+ * Check if a date is in the past (for cache expiration checks)
46
+ */
47
+ export declare function isExpired(date: Date): boolean;
48
+ /**
49
+ * Calculate seconds until a date (for max-age calculations)
50
+ * Returns 0 if date is in the past
51
+ */
52
+ export declare function secondsUntil(date: Date): number;
53
+ //# sourceMappingURL=datetime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datetime.d.ts","sourceRoot":"","sources":["../src/datetime.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;;;;GAKG;AAEH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAE5C;AAED;;;;;;;;;;;GAWG;AAIH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAiErD;AAoED;;;;;;GAMG;AAEH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAEjD;AAED;;;;;;;;;GASG;AAEH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CA2DtD;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAE7C;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,CAG/C"}
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Date/time utilities for HTTP headers.
3
+ * RFC 3339 §5.6, RFC 9110 §5.6.7, RFC 850 §2.
4
+ */
5
+ const DAY_NAMES = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
6
+ const MONTH_NAMES = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
7
+ /**
8
+ * Format a Date as RFC 3339 timestamp (ISO 8601 compatible)
9
+ * Example output: "2025-02-01T12:30:45.123Z"
10
+ *
11
+ * This is the format used in JSON responses.
12
+ */
13
+ // RFC 3339 §5.6: Internet Date/Time Format.
14
+ export function toRFC3339(date) {
15
+ return date.toISOString();
16
+ }
17
+ /**
18
+ * Parse an RFC 3339 timestamp string to Date
19
+ * Returns null for invalid format
20
+ *
21
+ * Accepts:
22
+ * - "2025-02-01T12:30:45Z"
23
+ * - "2025-02-01T12:30:45.123Z"
24
+ * - "2025-02-01T12:30:45+00:00"
25
+ * - "2025-02-01T12:30:45.123+05:30"
26
+ * - "2025-02-01t12:30:45z" (lowercase per §5.6 NOTE)
27
+ * - "2025-02-01 12:30:45Z" (space separator per §5.6 NOTE)
28
+ */
29
+ // RFC 3339 §5.6: Internet Date/Time Format parsing.
30
+ // RFC 3339 §5.6 NOTE: "T" and "Z" may alternatively be lowercase "t" or "z".
31
+ // RFC 3339 §5.6 NOTE: Applications may use space instead of "T" separator.
32
+ export function parseRFC3339(str) {
33
+ const rfc3339Regex = /^(\d{4})-(\d{2})-(\d{2})[Tt ](\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(?:[Zz]|([+-])(\d{2}):(\d{2}))$/;
34
+ const match = str.match(rfc3339Regex);
35
+ if (!match) {
36
+ return null;
37
+ }
38
+ const year = Number(match[1]);
39
+ const month = Number(match[2]);
40
+ const day = Number(match[3]);
41
+ const hours = Number(match[4]);
42
+ const minutes = Number(match[5]);
43
+ const seconds = Number(match[6]);
44
+ const fraction = match[7];
45
+ const offsetSign = match[8];
46
+ const offsetHour = match[9];
47
+ const offsetMinute = match[10];
48
+ if (!isValidDateParts(year, month, day)) {
49
+ return null;
50
+ }
51
+ if (hours < 0 || hours > 23) {
52
+ return null;
53
+ }
54
+ if (minutes < 0 || minutes > 59) {
55
+ return null;
56
+ }
57
+ if (seconds < 0 || seconds > 59) {
58
+ return null;
59
+ }
60
+ let offsetTotalMinutes = 0;
61
+ if (offsetSign) {
62
+ const parsedOffsetHour = Number(offsetHour);
63
+ const parsedOffsetMinute = Number(offsetMinute);
64
+ if (parsedOffsetHour < 0 || parsedOffsetHour > 23) {
65
+ return null;
66
+ }
67
+ if (parsedOffsetMinute < 0 || parsedOffsetMinute > 59) {
68
+ return null;
69
+ }
70
+ offsetTotalMinutes = parsedOffsetHour * 60 + parsedOffsetMinute;
71
+ if (offsetSign === '-') {
72
+ offsetTotalMinutes *= -1;
73
+ }
74
+ }
75
+ const milliseconds = parseFractionalMilliseconds(fraction);
76
+ if (milliseconds === null) {
77
+ return null;
78
+ }
79
+ const utcMillis = Date.UTC(year, month - 1, day, hours, minutes, seconds, milliseconds);
80
+ if (isNaN(utcMillis)) {
81
+ return null;
82
+ }
83
+ return new Date(utcMillis - offsetTotalMinutes * 60000);
84
+ }
85
+ function isValidDateParts(year, month, day) {
86
+ if (year < 0 || year > 9999) {
87
+ return false;
88
+ }
89
+ if (month < 1 || month > 12) {
90
+ return false;
91
+ }
92
+ const daysInMonth = getDaysInMonth(year, month);
93
+ return day >= 1 && day <= daysInMonth;
94
+ }
95
+ function getDaysInMonth(year, month) {
96
+ switch (month) {
97
+ case 2:
98
+ return isLeapYear(year) ? 29 : 28;
99
+ case 4:
100
+ case 6:
101
+ case 9:
102
+ case 11:
103
+ return 30;
104
+ default:
105
+ return 31;
106
+ }
107
+ }
108
+ function isLeapYear(year) {
109
+ if (year % 4 !== 0) {
110
+ return false;
111
+ }
112
+ if (year % 100 !== 0) {
113
+ return true;
114
+ }
115
+ return year % 400 === 0;
116
+ }
117
+ function parseFractionalMilliseconds(fraction) {
118
+ if (!fraction) {
119
+ return 0;
120
+ }
121
+ if (!/^\d+$/.test(fraction)) {
122
+ return null;
123
+ }
124
+ const padded = fraction.length >= 3
125
+ ? fraction.slice(0, 3)
126
+ : fraction.padEnd(3, '0');
127
+ return Number(padded);
128
+ }
129
+ // RFC 850 §2: Two-digit year handling.
130
+ function resolveRFC850Year(twoDigitYear, now = new Date()) {
131
+ const currentYear = now.getUTCFullYear();
132
+ const currentCentury = Math.floor(currentYear / 100) * 100;
133
+ let year = currentCentury + twoDigitYear;
134
+ if (year > currentYear + 50) {
135
+ year -= 100;
136
+ }
137
+ return year;
138
+ }
139
+ /**
140
+ * Format a Date as HTTP-date per RFC 9110 Section 5.6.7
141
+ * Used for Last-Modified, Date, Expires headers.
142
+ *
143
+ * Format: "Sat, 01 Feb 2025 12:30:45 GMT"
144
+ * (IMF-fixdate format - the preferred format)
145
+ */
146
+ // RFC 9110 §5.6.7: IMF-fixdate formatting.
147
+ export function formatHTTPDate(date) {
148
+ return date.toUTCString();
149
+ }
150
+ /**
151
+ * Parse an HTTP-date string to Date
152
+ *
153
+ * RFC 9110 Section 5.6.7 requires parsing these formats:
154
+ * 1. IMF-fixdate: "Sun, 06 Nov 1994 08:49:37 GMT" (preferred)
155
+ * 2. RFC 850: "Sunday, 06-Nov-94 08:49:37 GMT" (obsolete)
156
+ * 3. ANSI C asctime(): "Sun Nov 6 08:49:37 1994" (obsolete)
157
+ *
158
+ * Returns null for invalid format.
159
+ */
160
+ // RFC 9110 §5.6.7, RFC 850 §2: HTTP-date parsing (including rfc850-date).
161
+ export function parseHTTPDate(str) {
162
+ // Try IMF-fixdate: "Sun, 06 Nov 1994 08:49:37 GMT"
163
+ const imfMatch = str.match(/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (\d{2}) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d{2}):(\d{2}):(\d{2}) GMT$/);
164
+ if (imfMatch) {
165
+ const [, day, month, year, hours, minutes, seconds] = imfMatch;
166
+ const monthIndex = MONTH_NAMES.indexOf(month);
167
+ const date = new Date(Date.UTC(parseInt(year, 10), monthIndex, parseInt(day, 10), parseInt(hours, 10), parseInt(minutes, 10), parseInt(seconds, 10)));
168
+ return isNaN(date.getTime()) ? null : date;
169
+ }
170
+ // Try RFC 850: "Sunday, 06-Nov-94 08:49:37 GMT"
171
+ const rfc850Match = str.match(/^(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday), (\d{2})-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d{2}) (\d{2}):(\d{2}):(\d{2}) GMT$/);
172
+ if (rfc850Match) {
173
+ const [, day, month, year2digit, hours, minutes, seconds] = rfc850Match;
174
+ const monthIndex = MONTH_NAMES.indexOf(month);
175
+ // RFC 850 uses 2-digit years; interpret with sliding 50-year window.
176
+ const year = resolveRFC850Year(parseInt(year2digit, 10));
177
+ const date = new Date(Date.UTC(year, monthIndex, parseInt(day, 10), parseInt(hours, 10), parseInt(minutes, 10), parseInt(seconds, 10)));
178
+ return isNaN(date.getTime()) ? null : date;
179
+ }
180
+ // Try ANSI C asctime(): "Sun Nov 6 08:49:37 1994"
181
+ // Note: day can be space-padded (single digit has leading space)
182
+ const asctimeMatch = str.match(/^(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) ([ \d]\d) (\d{2}):(\d{2}):(\d{2}) (\d{4})$/);
183
+ if (asctimeMatch) {
184
+ const [, month, day, hours, minutes, seconds, year] = asctimeMatch;
185
+ const monthIndex = MONTH_NAMES.indexOf(month);
186
+ const date = new Date(Date.UTC(parseInt(year, 10), monthIndex, parseInt(day, 10), parseInt(hours, 10), parseInt(minutes, 10), parseInt(seconds, 10)));
187
+ return isNaN(date.getTime()) ? null : date;
188
+ }
189
+ return null;
190
+ }
191
+ /**
192
+ * Check if a date is in the past (for cache expiration checks)
193
+ */
194
+ export function isExpired(date) {
195
+ return date.getTime() < Date.now();
196
+ }
197
+ /**
198
+ * Calculate seconds until a date (for max-age calculations)
199
+ * Returns 0 if date is in the past
200
+ */
201
+ export function secondsUntil(date) {
202
+ const diff = date.getTime() - Date.now();
203
+ return diff > 0 ? Math.floor(diff / 1000) : 0;
204
+ }
205
+ //# sourceMappingURL=datetime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"datetime.js","sourceRoot":"","sources":["../src/datetime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,220 @@
1
+ /**
2
+ * Digest Fields per RFC 9530.
3
+ * RFC 9530 §2, §3, §4, §5.
4
+ * @see https://www.rfc-editor.org/rfc/rfc9530.html
5
+ *
6
+ * Provides Content-Digest and Repr-Digest HTTP header field parsing/formatting
7
+ * for content and representation integrity verification. Also provides
8
+ * Want-Content-Digest and Want-Repr-Digest preference fields.
9
+ */
10
+ /**
11
+ * Active digest algorithms suitable for adversarial settings.
12
+ * RFC 9530 §5, §7.2.
13
+ */
14
+ export type DigestAlgorithm = 'sha-256' | 'sha-512';
15
+ /**
16
+ * All recognized algorithms including deprecated ones.
17
+ * RFC 9530 §5, §7.2.
18
+ * Deprecated algorithms MAY be used for backward compatibility but
19
+ * MUST NOT be used in adversarial settings.
20
+ */
21
+ export type DigestAlgorithmAny = DigestAlgorithm | 'md5' | 'sha' | 'unixsum' | 'unixcksum' | 'adler' | 'crc32c';
22
+ /**
23
+ * A parsed digest value from Content-Digest or Repr-Digest fields.
24
+ * RFC 9530 §2, §3.
25
+ */
26
+ export interface Digest {
27
+ /** Algorithm key (lowercase) */
28
+ algorithm: string;
29
+ /** Raw digest bytes */
30
+ value: Uint8Array;
31
+ }
32
+ /**
33
+ * A digest preference from Want-Content-Digest or Want-Repr-Digest fields.
34
+ * RFC 9530 §4.
35
+ */
36
+ export interface DigestPreference {
37
+ /** Algorithm key (lowercase) */
38
+ algorithm: string;
39
+ /**
40
+ * Preference weight (0-10).
41
+ * 0 = not acceptable, 1 = least preferred, 10 = most preferred.
42
+ */
43
+ weight: number;
44
+ }
45
+ /**
46
+ * Algorithm classification constants.
47
+ * RFC 9530 §5, §7.2.
48
+ */
49
+ export declare const DIGEST_ALGORITHMS: {
50
+ /** Algorithms suitable for adversarial settings */
51
+ readonly active: readonly ["sha-256", "sha-512"];
52
+ /** Algorithms that MUST NOT be used in adversarial settings */
53
+ readonly deprecated: readonly ["md5", "sha", "unixsum", "unixcksum", "adler", "crc32c"];
54
+ };
55
+ /**
56
+ * Check if an algorithm is active (suitable for adversarial settings).
57
+ * RFC 9530 §5.
58
+ */
59
+ export declare function isActiveAlgorithm(algorithm: string): algorithm is DigestAlgorithm;
60
+ /**
61
+ * Check if an algorithm is deprecated.
62
+ * RFC 9530 §5.
63
+ */
64
+ export declare function isDeprecatedAlgorithm(algorithm: string): boolean;
65
+ /**
66
+ * Parse Content-Digest header field value.
67
+ * RFC 9530 §2.
68
+ *
69
+ * @param value - The Content-Digest header value
70
+ * @returns Array of digests, or null if malformed
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * const digests = parseContentDigest('sha-256=:abc123...:');
75
+ * ```
76
+ */
77
+ export declare function parseContentDigest(value: string): Digest[] | null;
78
+ /**
79
+ * Parse Repr-Digest header field value.
80
+ * RFC 9530 §3.
81
+ *
82
+ * @param value - The Repr-Digest header value
83
+ * @returns Array of digests, or null if malformed
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const digests = parseReprDigest('sha-512=:xyz789...:');
88
+ * ```
89
+ */
90
+ export declare function parseReprDigest(value: string): Digest[] | null;
91
+ /**
92
+ * Parse Want-Content-Digest header field value.
93
+ * RFC 9530 §4.
94
+ *
95
+ * @param value - The Want-Content-Digest header value
96
+ * @returns Array of preferences, or null if malformed
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * const prefs = parseWantContentDigest('sha-256=10, sha-512=3');
101
+ * ```
102
+ */
103
+ export declare function parseWantContentDigest(value: string): DigestPreference[] | null;
104
+ /**
105
+ * Parse Want-Repr-Digest header field value.
106
+ * RFC 9530 §4.
107
+ *
108
+ * @param value - The Want-Repr-Digest header value
109
+ * @returns Array of preferences, or null if malformed
110
+ *
111
+ * @example
112
+ * ```ts
113
+ * const prefs = parseWantReprDigest('sha-512=3, sha-256=10, unixsum=0');
114
+ * ```
115
+ */
116
+ export declare function parseWantReprDigest(value: string): DigestPreference[] | null;
117
+ /**
118
+ * Format Content-Digest header field value.
119
+ * RFC 9530 §2.
120
+ *
121
+ * @param digests - Array of digests to format
122
+ * @returns Formatted header value
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * const header = formatContentDigest([
127
+ * { algorithm: 'sha-256', value: digestBytes }
128
+ * ]);
129
+ * ```
130
+ */
131
+ export declare function formatContentDigest(digests: Digest[]): string;
132
+ /**
133
+ * Format Repr-Digest header field value.
134
+ * RFC 9530 §3.
135
+ *
136
+ * @param digests - Array of digests to format
137
+ * @returns Formatted header value
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * const header = formatReprDigest([
142
+ * { algorithm: 'sha-512', value: digestBytes }
143
+ * ]);
144
+ * ```
145
+ */
146
+ export declare function formatReprDigest(digests: Digest[]): string;
147
+ /**
148
+ * Format Want-Content-Digest header field value.
149
+ * RFC 9530 §4.
150
+ *
151
+ * @param preferences - Array of preferences to format
152
+ * @returns Formatted header value
153
+ *
154
+ * @example
155
+ * ```ts
156
+ * const header = formatWantContentDigest([
157
+ * { algorithm: 'sha-256', weight: 10 },
158
+ * { algorithm: 'sha-512', weight: 3 }
159
+ * ]);
160
+ * ```
161
+ */
162
+ export declare function formatWantContentDigest(preferences: DigestPreference[]): string;
163
+ /**
164
+ * Format Want-Repr-Digest header field value.
165
+ * RFC 9530 §4.
166
+ *
167
+ * @param preferences - Array of preferences to format
168
+ * @returns Formatted header value
169
+ *
170
+ * @example
171
+ * ```ts
172
+ * const header = formatWantReprDigest([
173
+ * { algorithm: 'sha-512', weight: 3 },
174
+ * { algorithm: 'sha-256', weight: 10 },
175
+ * { algorithm: 'unixsum', weight: 0 }
176
+ * ]);
177
+ * ```
178
+ */
179
+ export declare function formatWantReprDigest(preferences: DigestPreference[]): string;
180
+ /**
181
+ * Generate a digest for the given data.
182
+ * RFC 9530 §2, §3.
183
+ *
184
+ * Uses Web Crypto API for hashing. Only active algorithms are supported
185
+ * for generation per RFC 9530 §5.
186
+ *
187
+ * @param data - Data to hash (string, ArrayBuffer, or ArrayBufferView)
188
+ * @param algorithm - Hash algorithm (default: 'sha-256')
189
+ * @returns Promise resolving to digest
190
+ * @throws Error if algorithm is not supported for generation
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * const digest = await generateDigest('hello world');
195
+ * const headers = { 'Content-Digest': formatContentDigest([digest]) };
196
+ * ```
197
+ */
198
+ export declare function generateDigest(data: string | ArrayBuffer | ArrayBufferView, algorithm?: DigestAlgorithm): Promise<Digest>;
199
+ /**
200
+ * Verify a digest against the given data.
201
+ * RFC 9530 §2, §3.
202
+ *
203
+ * Only active algorithms (sha-256, sha-512) can be verified.
204
+ * Deprecated algorithms will return false.
205
+ *
206
+ * @param data - Data to verify
207
+ * @param digest - Digest to check against
208
+ * @returns Promise resolving to true if digest matches
209
+ *
210
+ * @example
211
+ * ```ts
212
+ * const digests = parseContentDigest(headers.get('Content-Digest'));
213
+ * const sha256 = digests?.find(d => d.algorithm === 'sha-256');
214
+ * if (sha256 && await verifyDigest(content, sha256)) {
215
+ * // Content integrity verified
216
+ * }
217
+ * ```
218
+ */
219
+ export declare function verifyDigest(data: string | ArrayBuffer | ArrayBufferView, digest: Digest): Promise<boolean>;
220
+ //# sourceMappingURL=digest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"digest.d.ts","sourceRoot":"","sources":["../src/digest.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AASH;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG,SAAS,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GACxB,eAAe,GACf,KAAK,GACL,KAAK,GACL,SAAS,GACT,WAAW,GACX,OAAO,GACP,QAAQ,CAAC;AAEf;;;GAGG;AACH,MAAM,WAAW,MAAM;IACnB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,uBAAuB;IACvB,KAAK,EAAE,UAAU,CAAC;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC7B,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;CAClB;AAMD;;;GAGG;AACH,eAAO,MAAM,iBAAiB;IAC1B,mDAAmD;;IAEnD,+DAA+D;;CAEzD,CAAC;AAMX;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,IAAI,eAAe,CAEjF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAEhE;AAwCD;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAEjE;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAE9D;AA0CD;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,EAAE,GAAG,IAAI,CAE/E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,EAAE,GAAG,IAAI,CAE5E;AAqBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAE7D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAE1D;AAmBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAE/E;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,gBAAgB,EAAE,GAAG,MAAM,CAE5E;AAcD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,cAAc,CAChC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,EAC5C,SAAS,GAAE,eAA2B,GACvC,OAAO,CAAC,MAAM,CAAC,CAwBjB;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,YAAY,CAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,eAAe,EAC5C,MAAM,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAsBlB"}