@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
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
|
package/dist/cors.js.map
ADDED
|
@@ -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"}
|
package/dist/datetime.js
ADDED
|
@@ -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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"}
|
package/dist/digest.d.ts
ADDED
|
@@ -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"}
|