@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,357 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Content negotiation utilities per RFC 7231.
|
|
3
|
+
* RFC 7231 §5.3.1, §5.3.2.
|
|
4
|
+
*/
|
|
5
|
+
import { isEmptyHeader, splitQuotedValue, unquote, parseQValue } from './header-utils.js';
|
|
6
|
+
/**
|
|
7
|
+
* Media type constants mapping format names to MIME types.
|
|
8
|
+
*/
|
|
9
|
+
export const MEDIA_TYPES = {
|
|
10
|
+
json: 'application/json',
|
|
11
|
+
csv: 'text/csv',
|
|
12
|
+
html: 'text/html',
|
|
13
|
+
text: 'text/plain',
|
|
14
|
+
xml: 'application/xml',
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Reverse mapping from MIME type to format name.
|
|
18
|
+
*/
|
|
19
|
+
export const MIME_TO_FORMAT = Object.fromEntries(Object.entries(MEDIA_TYPES).map(([format, mime]) => [mime, format]));
|
|
20
|
+
/**
|
|
21
|
+
* Parse an Accept header into a list of entries sorted by preference.
|
|
22
|
+
*
|
|
23
|
+
* RFC 7231 Section 5.3.2 defines Accept header format.
|
|
24
|
+
*
|
|
25
|
+
* @param header - The Accept header value
|
|
26
|
+
* @returns Sorted array of AcceptEntry (highest preference first)
|
|
27
|
+
*
|
|
28
|
+
* Sorting rules:
|
|
29
|
+
* 1. Higher q value first
|
|
30
|
+
* 2. More specific type beats wildcard (text/html > text/star > star/star)
|
|
31
|
+
* 3. More parameters beats fewer
|
|
32
|
+
*
|
|
33
|
+
* Invalid q-values are rejected and the entry is skipped.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* parseAccept("text/html, application/json;q=0.9, text/star;q=0.8")
|
|
37
|
+
* // Returns: [text/html q=1], [application/json q=0.9], [text/star q=0.8]
|
|
38
|
+
*/
|
|
39
|
+
// RFC 7231 §5.3.2: Accept header parsing and sorting.
|
|
40
|
+
export function parseAccept(header) {
|
|
41
|
+
if (isEmptyHeader(header)) {
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
const entries = [];
|
|
45
|
+
const parts = splitQuotedValue(header, ',');
|
|
46
|
+
for (const part of parts) {
|
|
47
|
+
const trimmed = part.trim();
|
|
48
|
+
if (!trimmed)
|
|
49
|
+
continue;
|
|
50
|
+
const entry = parseMediaRange(trimmed);
|
|
51
|
+
if (entry) {
|
|
52
|
+
entries.push(entry);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Sort by preference
|
|
56
|
+
entries.sort((a, b) => {
|
|
57
|
+
// 1. Higher q value first
|
|
58
|
+
if (a.q !== b.q) {
|
|
59
|
+
return b.q - a.q;
|
|
60
|
+
}
|
|
61
|
+
// 2. More specific type beats wildcard
|
|
62
|
+
const specA = getSpecificity(a);
|
|
63
|
+
const specB = getSpecificity(b);
|
|
64
|
+
if (specA !== specB) {
|
|
65
|
+
return specB - specA;
|
|
66
|
+
}
|
|
67
|
+
// 3. More parameters beats fewer
|
|
68
|
+
return b.params.size - a.params.size;
|
|
69
|
+
});
|
|
70
|
+
return entries;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Parse a single media range with optional parameters.
|
|
74
|
+
*/
|
|
75
|
+
function parseMediaRange(range) {
|
|
76
|
+
const parts = splitQuotedValue(range, ';').map(p => p.trim());
|
|
77
|
+
const mediaType = parts[0];
|
|
78
|
+
if (!mediaType || !mediaType.includes('/')) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const [type, subtype] = mediaType.split('/');
|
|
82
|
+
if (!type || !subtype) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
let q = 1.0;
|
|
86
|
+
const params = new Map();
|
|
87
|
+
let seenQ = false;
|
|
88
|
+
for (let i = 1; i < parts.length; i++) {
|
|
89
|
+
const param = parts[i];
|
|
90
|
+
if (!param)
|
|
91
|
+
continue;
|
|
92
|
+
const eqIndex = param.indexOf('=');
|
|
93
|
+
if (eqIndex === -1)
|
|
94
|
+
continue;
|
|
95
|
+
const key = param.slice(0, eqIndex).trim().toLowerCase();
|
|
96
|
+
const value = unquote(param.slice(eqIndex + 1).trim());
|
|
97
|
+
if (key === 'q') {
|
|
98
|
+
const parsed = parseQValue(value);
|
|
99
|
+
if (parsed === null) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
q = parsed;
|
|
103
|
+
seenQ = true;
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
if (!seenQ) {
|
|
107
|
+
params.set(key, value);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
type: type.toLowerCase(),
|
|
113
|
+
subtype: subtype.toLowerCase(),
|
|
114
|
+
q,
|
|
115
|
+
params,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Get specificity score for sorting.
|
|
120
|
+
* - exact match with params: 4
|
|
121
|
+
* - exact match no params: 3
|
|
122
|
+
* - type wildcard (text/star): 2
|
|
123
|
+
* - full wildcard (star/star): 1
|
|
124
|
+
*/
|
|
125
|
+
function getSpecificity(entry) {
|
|
126
|
+
if (entry.type === '*' && entry.subtype === '*') {
|
|
127
|
+
return 1;
|
|
128
|
+
}
|
|
129
|
+
if (entry.subtype === '*') {
|
|
130
|
+
return 2;
|
|
131
|
+
}
|
|
132
|
+
// Exact match
|
|
133
|
+
return entry.params.size > 0 ? 4 : 3;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Check if an Accept entry matches a media type.
|
|
137
|
+
* Returns specificity score if match, 0 if no match.
|
|
138
|
+
*/
|
|
139
|
+
function matchesMediaType(entry, mediaType) {
|
|
140
|
+
const mimeType = MEDIA_TYPES[mediaType];
|
|
141
|
+
return matchesMimeType(entry, mimeType);
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Check if an Accept entry matches a MIME type string.
|
|
145
|
+
* Returns specificity score if match, 0 if no match.
|
|
146
|
+
*/
|
|
147
|
+
function matchesMimeType(entry, mimeType) {
|
|
148
|
+
const parsed = parseMimeTypeWithParams(mimeType);
|
|
149
|
+
if (!parsed) {
|
|
150
|
+
return 0;
|
|
151
|
+
}
|
|
152
|
+
const { type, subtype, params } = parsed;
|
|
153
|
+
if (!paramsMatch(entry.params, params)) {
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
if (!type || !subtype) {
|
|
157
|
+
return 0;
|
|
158
|
+
}
|
|
159
|
+
// Full wildcard matches anything
|
|
160
|
+
if (entry.type === '*' && entry.subtype === '*') {
|
|
161
|
+
return 1;
|
|
162
|
+
}
|
|
163
|
+
// Type must match for type wildcard
|
|
164
|
+
if (entry.type === type && entry.subtype === '*') {
|
|
165
|
+
return 2;
|
|
166
|
+
}
|
|
167
|
+
// Exact match
|
|
168
|
+
if (entry.type === type && entry.subtype === subtype) {
|
|
169
|
+
return entry.params.size > 0 ? 4 : 3;
|
|
170
|
+
}
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
function parseMimeTypeWithParams(mimeType) {
|
|
174
|
+
const parts = splitQuotedValue(mimeType, ';').map(p => p.trim());
|
|
175
|
+
const mediaType = parts[0];
|
|
176
|
+
if (!mediaType || !mediaType.includes('/')) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
const [type, subtype] = mediaType.split('/');
|
|
180
|
+
if (!type || !subtype) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
const params = new Map();
|
|
184
|
+
for (let i = 1; i < parts.length; i++) {
|
|
185
|
+
const param = parts[i];
|
|
186
|
+
if (!param)
|
|
187
|
+
continue;
|
|
188
|
+
const eqIndex = param.indexOf('=');
|
|
189
|
+
if (eqIndex === -1)
|
|
190
|
+
continue;
|
|
191
|
+
const key = param.slice(0, eqIndex).trim().toLowerCase();
|
|
192
|
+
const value = unquote(param.slice(eqIndex + 1).trim());
|
|
193
|
+
if (key) {
|
|
194
|
+
params.set(key, value);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
type: type.toLowerCase(),
|
|
199
|
+
subtype: subtype.toLowerCase(),
|
|
200
|
+
params,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function paramsMatch(required, candidate) {
|
|
204
|
+
if (required.size === 0) {
|
|
205
|
+
return true;
|
|
206
|
+
}
|
|
207
|
+
for (const [key, value] of required.entries()) {
|
|
208
|
+
if (!candidate.has(key)) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
if (candidate.get(key) !== value) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Negotiate the best media type from supported options.
|
|
219
|
+
*
|
|
220
|
+
* @param input - The Request object, Accept header string, or null/undefined
|
|
221
|
+
* @param supported - Array of supported media types (MIME strings like 'application/json')
|
|
222
|
+
* @returns Best matching media type or null if none acceptable
|
|
223
|
+
*
|
|
224
|
+
* Matching rules:
|
|
225
|
+
* - Exact match: "application/json" matches 'application/json'
|
|
226
|
+
* - Type wildcard: "text/\*" matches 'text/html', 'text/csv'
|
|
227
|
+
* - Full wildcard: "\*\/\*" matches anything
|
|
228
|
+
* - q=0 means explicitly not acceptable
|
|
229
|
+
*/
|
|
230
|
+
// RFC 7231 §5.3.2: Media type selection based on Accept.
|
|
231
|
+
export function negotiate(input, supported) {
|
|
232
|
+
if (supported.length === 0) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
let acceptHeader;
|
|
236
|
+
if (input instanceof Request) {
|
|
237
|
+
acceptHeader = input.headers.get('Accept');
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
acceptHeader = input ?? null;
|
|
241
|
+
}
|
|
242
|
+
// Empty or missing Accept header means accept anything
|
|
243
|
+
if (!acceptHeader || !acceptHeader.trim()) {
|
|
244
|
+
return supported[0] ?? null;
|
|
245
|
+
}
|
|
246
|
+
const entries = parseAccept(acceptHeader);
|
|
247
|
+
// No valid entries means accept anything
|
|
248
|
+
if (entries.length === 0) {
|
|
249
|
+
return supported[0] ?? null;
|
|
250
|
+
}
|
|
251
|
+
// Find best match
|
|
252
|
+
let bestMatch = null;
|
|
253
|
+
let bestScore = 0;
|
|
254
|
+
let bestQ = 0;
|
|
255
|
+
for (const entry of entries) {
|
|
256
|
+
// Skip explicitly not acceptable
|
|
257
|
+
if (entry.q === 0) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
for (const mimeType of supported) {
|
|
261
|
+
const score = matchesMimeType(entry, mimeType);
|
|
262
|
+
if (score > 0) {
|
|
263
|
+
// Prefer higher q, then higher specificity
|
|
264
|
+
if (entry.q > bestQ || (entry.q === bestQ && score > bestScore)) {
|
|
265
|
+
bestMatch = mimeType;
|
|
266
|
+
bestScore = score;
|
|
267
|
+
bestQ = entry.q;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return bestMatch;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Get response format from request or Accept header string.
|
|
276
|
+
* Only distinguishes between 'json' and 'csv'.
|
|
277
|
+
* Defaults to 'json' if no preference or Accept missing.
|
|
278
|
+
*
|
|
279
|
+
* @param input - The Request object or Accept header string
|
|
280
|
+
* @returns 'json', 'csv', or null when neither is acceptable
|
|
281
|
+
*/
|
|
282
|
+
// RFC 7231 §5.3.2: Response format selection from Accept.
|
|
283
|
+
export function getResponseFormat(input) {
|
|
284
|
+
let acceptHeader;
|
|
285
|
+
if (input instanceof Request) {
|
|
286
|
+
acceptHeader = input.headers.get('Accept');
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
acceptHeader = input ?? null;
|
|
290
|
+
}
|
|
291
|
+
if (!acceptHeader) {
|
|
292
|
+
return 'json';
|
|
293
|
+
}
|
|
294
|
+
if (!acceptHeader.trim()) {
|
|
295
|
+
return 'json';
|
|
296
|
+
}
|
|
297
|
+
const entries = parseAccept(acceptHeader);
|
|
298
|
+
if (entries.length === 0) {
|
|
299
|
+
return 'json';
|
|
300
|
+
}
|
|
301
|
+
const best = negotiate(acceptHeader, ['application/json', 'text/csv']);
|
|
302
|
+
if (!best) {
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
|
+
return best === 'text/csv' ? 'csv' : 'json';
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Escape a value for CSV output.
|
|
309
|
+
*/
|
|
310
|
+
function escapeCSVValue(value) {
|
|
311
|
+
if (value === null || value === undefined) {
|
|
312
|
+
return '';
|
|
313
|
+
}
|
|
314
|
+
let str;
|
|
315
|
+
if (typeof value === 'object') {
|
|
316
|
+
str = JSON.stringify(value);
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
str = String(value);
|
|
320
|
+
}
|
|
321
|
+
// Check if quoting is needed
|
|
322
|
+
if (str.includes(',') || str.includes('"') || str.includes('\n') || str.includes('\r')) {
|
|
323
|
+
// Escape quotes by doubling them
|
|
324
|
+
return '"' + str.replace(/"/g, '""') + '"';
|
|
325
|
+
}
|
|
326
|
+
return str;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Convert an array of objects to CSV format.
|
|
330
|
+
*
|
|
331
|
+
* @param data - Array of objects (all should have same keys)
|
|
332
|
+
* @returns CSV string with headers
|
|
333
|
+
*
|
|
334
|
+
* Rules:
|
|
335
|
+
* - First row is headers (keys from first object)
|
|
336
|
+
* - Values containing commas, quotes, or newlines are quoted
|
|
337
|
+
* - Quotes in values are escaped as ""
|
|
338
|
+
* - null/undefined become empty string
|
|
339
|
+
* - Objects/arrays are JSON stringified
|
|
340
|
+
*/
|
|
341
|
+
// Non-RFC: CSV formatting helper.
|
|
342
|
+
export function toCSV(data) {
|
|
343
|
+
if (data.length === 0) {
|
|
344
|
+
return '';
|
|
345
|
+
}
|
|
346
|
+
// Get headers from first object
|
|
347
|
+
const firstRow = data[0];
|
|
348
|
+
const headers = Object.keys(firstRow);
|
|
349
|
+
// Build header row
|
|
350
|
+
const headerRow = headers.map(h => escapeCSVValue(h)).join(',');
|
|
351
|
+
// Build data rows
|
|
352
|
+
const dataRows = data.map(row => {
|
|
353
|
+
return headers.map(header => escapeCSVValue(row[header])).join(',');
|
|
354
|
+
});
|
|
355
|
+
return [headerRow, ...dataRows].join('\n');
|
|
356
|
+
}
|
|
357
|
+
//# sourceMappingURL=negotiate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"negotiate.js","sourceRoot":"","sources":["../src/negotiate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pagination helpers with RFC 8288 Link header usage.
|
|
3
|
+
* RFC 8288 §3, §3.3.
|
|
4
|
+
*/
|
|
5
|
+
import type { PaginationResult, DecodedCursor, PaginationLinks } from './types.js';
|
|
6
|
+
/** Default number of items per page */
|
|
7
|
+
export declare const DEFAULT_LIMIT = 50;
|
|
8
|
+
/** Maximum allowed items per page */
|
|
9
|
+
export declare const MAX_LIMIT = 100;
|
|
10
|
+
/**
|
|
11
|
+
* Decode a cursor string to get the offset.
|
|
12
|
+
* Cursor format: base64(JSON({ offset: number }))
|
|
13
|
+
*
|
|
14
|
+
* @param cursor - The cursor string
|
|
15
|
+
* @returns DecodedCursor or null if invalid
|
|
16
|
+
*/
|
|
17
|
+
export declare function decodeCursor(cursor: string): DecodedCursor | null;
|
|
18
|
+
/**
|
|
19
|
+
* Encode an offset into a cursor string.
|
|
20
|
+
*
|
|
21
|
+
* @param offset - The offset to encode
|
|
22
|
+
* @returns Base64-encoded cursor
|
|
23
|
+
*/
|
|
24
|
+
export declare function encodeCursor(offset: number): string;
|
|
25
|
+
/**
|
|
26
|
+
* Parse pagination parameters from URL query string.
|
|
27
|
+
*
|
|
28
|
+
* Supports two modes:
|
|
29
|
+
* 1. Cursor-based: ?cursor=<base64>
|
|
30
|
+
* 2. Offset-based: ?limit=N&offset=N
|
|
31
|
+
*
|
|
32
|
+
* Also extracts sort parameter if present.
|
|
33
|
+
*
|
|
34
|
+
* @param url - The URL to parse
|
|
35
|
+
* @param options - Configuration options
|
|
36
|
+
* @returns PaginationParams on success, PaginationError on failure
|
|
37
|
+
*
|
|
38
|
+
* Error cases:
|
|
39
|
+
* - Invalid cursor format
|
|
40
|
+
* - Invalid cursor payload (not JSON, missing offset, negative offset)
|
|
41
|
+
* - Non-numeric limit/offset
|
|
42
|
+
* - Negative offset
|
|
43
|
+
*/
|
|
44
|
+
export declare function parsePaginationParams(url: URL, options?: {
|
|
45
|
+
defaultLimit?: number;
|
|
46
|
+
maxLimit?: number;
|
|
47
|
+
}): PaginationResult;
|
|
48
|
+
/**
|
|
49
|
+
* Calculate the last page offset given total count and limit.
|
|
50
|
+
*/
|
|
51
|
+
export declare function lastPageOffset(totalCount: number, limit: number): number;
|
|
52
|
+
/**
|
|
53
|
+
* Check if we're on the first page.
|
|
54
|
+
*/
|
|
55
|
+
export declare function isFirstPage(offset: number): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Check if we're on the last page.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isLastPage(offset: number, limit: number, totalCount: number): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Build pagination links for a result set.
|
|
62
|
+
*
|
|
63
|
+
* @param baseUrl - The base URL (without pagination params)
|
|
64
|
+
* @param totalCount - Total number of items
|
|
65
|
+
* @param limit - Items per page
|
|
66
|
+
* @param currentOffset - Current offset
|
|
67
|
+
* @param extraParams - Additional URL params to preserve
|
|
68
|
+
* @returns PaginationLinks object
|
|
69
|
+
*
|
|
70
|
+
* Links:
|
|
71
|
+
* - self: Current page
|
|
72
|
+
* - first: First page (offset=0)
|
|
73
|
+
* - last: Last page
|
|
74
|
+
* - next: Next page (omitted on last page)
|
|
75
|
+
* - prev: Previous page (omitted on first page)
|
|
76
|
+
*
|
|
77
|
+
* All links use cursor-based pagination for consistency.
|
|
78
|
+
*/
|
|
79
|
+
export declare function buildPaginationLinks(baseUrl: string, totalCount: number, limit: number, currentOffset: number, extraParams?: URLSearchParams): PaginationLinks;
|
|
80
|
+
//# sourceMappingURL=pagination.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAGR,gBAAgB,EAChB,aAAa,EACb,eAAe,EAClB,MAAM,YAAY,CAAC;AAGpB,uCAAuC;AACvC,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,qCAAqC;AACrC,eAAO,MAAM,SAAS,MAAM,CAAC;AAE7B;;;;;;GAMG;AAEH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAqBjE;AAED;;;;;GAKG;AAEH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;;;;;;;;;;;;;;GAkBG;AAEH,wBAAgB,qBAAqB,CACjC,GAAG,EAAE,GAAG,EACR,OAAO,CAAC,EAAE;IACN,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB,GACF,gBAAgB,CAiDlB;AAED;;GAEG;AAEH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAKxE;AAED;;GAEG;AAEH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED;;GAEG;AAEH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAKrF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AAEH,wBAAgB,oBAAoB,CAChC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,KAAK,EAAE,MAAM,EACb,aAAa,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,eAAe,GAC9B,eAAe,CAmCjB"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pagination helpers with RFC 8288 Link header usage.
|
|
3
|
+
* RFC 8288 §3, §3.3.
|
|
4
|
+
*/
|
|
5
|
+
/** Default number of items per page */
|
|
6
|
+
export const DEFAULT_LIMIT = 50;
|
|
7
|
+
/** Maximum allowed items per page */
|
|
8
|
+
export const MAX_LIMIT = 100;
|
|
9
|
+
/**
|
|
10
|
+
* Decode a cursor string to get the offset.
|
|
11
|
+
* Cursor format: base64(JSON({ offset: number }))
|
|
12
|
+
*
|
|
13
|
+
* @param cursor - The cursor string
|
|
14
|
+
* @returns DecodedCursor or null if invalid
|
|
15
|
+
*/
|
|
16
|
+
// Non-RFC: Cursor decoding helper.
|
|
17
|
+
export function decodeCursor(cursor) {
|
|
18
|
+
try {
|
|
19
|
+
const decoded = atob(cursor);
|
|
20
|
+
const parsed = JSON.parse(decoded);
|
|
21
|
+
if (typeof parsed.offset !== 'number') {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if (parsed.offset < 0) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (!Number.isInteger(parsed.offset)) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return { offset: parsed.offset };
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Encode an offset into a cursor string.
|
|
38
|
+
*
|
|
39
|
+
* @param offset - The offset to encode
|
|
40
|
+
* @returns Base64-encoded cursor
|
|
41
|
+
*/
|
|
42
|
+
// Non-RFC: Cursor encoding helper.
|
|
43
|
+
export function encodeCursor(offset) {
|
|
44
|
+
return btoa(JSON.stringify({ offset }));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Parse pagination parameters from URL query string.
|
|
48
|
+
*
|
|
49
|
+
* Supports two modes:
|
|
50
|
+
* 1. Cursor-based: ?cursor=<base64>
|
|
51
|
+
* 2. Offset-based: ?limit=N&offset=N
|
|
52
|
+
*
|
|
53
|
+
* Also extracts sort parameter if present.
|
|
54
|
+
*
|
|
55
|
+
* @param url - The URL to parse
|
|
56
|
+
* @param options - Configuration options
|
|
57
|
+
* @returns PaginationParams on success, PaginationError on failure
|
|
58
|
+
*
|
|
59
|
+
* Error cases:
|
|
60
|
+
* - Invalid cursor format
|
|
61
|
+
* - Invalid cursor payload (not JSON, missing offset, negative offset)
|
|
62
|
+
* - Non-numeric limit/offset
|
|
63
|
+
* - Negative offset
|
|
64
|
+
*/
|
|
65
|
+
// Non-RFC: Pagination query parsing.
|
|
66
|
+
export function parsePaginationParams(url, options) {
|
|
67
|
+
const defaultLimit = options?.defaultLimit ?? DEFAULT_LIMIT;
|
|
68
|
+
const maxLimit = options?.maxLimit ?? MAX_LIMIT;
|
|
69
|
+
const cursorParam = url.searchParams.get('cursor');
|
|
70
|
+
const limitParam = url.searchParams.get('limit');
|
|
71
|
+
const offsetParam = url.searchParams.get('offset');
|
|
72
|
+
const sortParam = url.searchParams.get('sort');
|
|
73
|
+
let offset;
|
|
74
|
+
let limit;
|
|
75
|
+
// Cursor takes precedence over limit/offset
|
|
76
|
+
if (cursorParam !== null) {
|
|
77
|
+
const decoded = decodeCursor(cursorParam);
|
|
78
|
+
if (decoded === null) {
|
|
79
|
+
return { error: 'invalid_cursor' };
|
|
80
|
+
}
|
|
81
|
+
offset = decoded.offset;
|
|
82
|
+
}
|
|
83
|
+
else if (offsetParam !== null) {
|
|
84
|
+
const parsedOffset = Number(offsetParam);
|
|
85
|
+
if (Number.isNaN(parsedOffset)) {
|
|
86
|
+
return { error: 'invalid_offset' };
|
|
87
|
+
}
|
|
88
|
+
if (parsedOffset < 0) {
|
|
89
|
+
return { error: 'invalid_offset' };
|
|
90
|
+
}
|
|
91
|
+
offset = Math.floor(parsedOffset);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
offset = 0;
|
|
95
|
+
}
|
|
96
|
+
if (limitParam !== null) {
|
|
97
|
+
const parsedLimit = Number(limitParam);
|
|
98
|
+
if (Number.isNaN(parsedLimit)) {
|
|
99
|
+
return { error: 'invalid_limit' };
|
|
100
|
+
}
|
|
101
|
+
limit = Math.min(Math.max(1, Math.floor(parsedLimit)), maxLimit);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
limit = defaultLimit;
|
|
105
|
+
}
|
|
106
|
+
const result = { limit, offset };
|
|
107
|
+
if (sortParam !== null) {
|
|
108
|
+
result.sort = sortParam;
|
|
109
|
+
}
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Calculate the last page offset given total count and limit.
|
|
114
|
+
*/
|
|
115
|
+
// Non-RFC: Pagination helper.
|
|
116
|
+
export function lastPageOffset(totalCount, limit) {
|
|
117
|
+
if (totalCount <= 0) {
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
return Math.floor((totalCount - 1) / limit) * limit;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Check if we're on the first page.
|
|
124
|
+
*/
|
|
125
|
+
// Non-RFC: Pagination helper.
|
|
126
|
+
export function isFirstPage(offset) {
|
|
127
|
+
return offset === 0;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check if we're on the last page.
|
|
131
|
+
*/
|
|
132
|
+
// Non-RFC: Pagination helper.
|
|
133
|
+
export function isLastPage(offset, limit, totalCount) {
|
|
134
|
+
if (totalCount === 0) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
return offset + limit >= totalCount;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Build pagination links for a result set.
|
|
141
|
+
*
|
|
142
|
+
* @param baseUrl - The base URL (without pagination params)
|
|
143
|
+
* @param totalCount - Total number of items
|
|
144
|
+
* @param limit - Items per page
|
|
145
|
+
* @param currentOffset - Current offset
|
|
146
|
+
* @param extraParams - Additional URL params to preserve
|
|
147
|
+
* @returns PaginationLinks object
|
|
148
|
+
*
|
|
149
|
+
* Links:
|
|
150
|
+
* - self: Current page
|
|
151
|
+
* - first: First page (offset=0)
|
|
152
|
+
* - last: Last page
|
|
153
|
+
* - next: Next page (omitted on last page)
|
|
154
|
+
* - prev: Previous page (omitted on first page)
|
|
155
|
+
*
|
|
156
|
+
* All links use cursor-based pagination for consistency.
|
|
157
|
+
*/
|
|
158
|
+
// RFC 8288 §3, §3.3: Link relations for pagination.
|
|
159
|
+
export function buildPaginationLinks(baseUrl, totalCount, limit, currentOffset, extraParams) {
|
|
160
|
+
const buildUrl = (offset) => {
|
|
161
|
+
const url = new URL(baseUrl);
|
|
162
|
+
url.searchParams.set('cursor', encodeCursor(offset));
|
|
163
|
+
url.searchParams.set('limit', String(limit));
|
|
164
|
+
if (extraParams) {
|
|
165
|
+
extraParams.forEach((value, key) => {
|
|
166
|
+
if (key !== 'cursor' && key !== 'limit' && key !== 'offset') {
|
|
167
|
+
url.searchParams.set(key, value);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
return url.toString();
|
|
172
|
+
};
|
|
173
|
+
const lastOffset = lastPageOffset(totalCount, limit);
|
|
174
|
+
const links = {
|
|
175
|
+
self: buildUrl(currentOffset),
|
|
176
|
+
first: buildUrl(0),
|
|
177
|
+
last: buildUrl(lastOffset)
|
|
178
|
+
};
|
|
179
|
+
if (!isLastPage(currentOffset, limit, totalCount)) {
|
|
180
|
+
links.next = buildUrl(currentOffset + limit);
|
|
181
|
+
}
|
|
182
|
+
if (!isFirstPage(currentOffset)) {
|
|
183
|
+
const prevOffset = Math.max(0, currentOffset - limit);
|
|
184
|
+
links.prev = buildUrl(prevOffset);
|
|
185
|
+
}
|
|
186
|
+
return links;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=pagination.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pagination.js","sourceRoot":"","sources":["../src/pagination.ts"],"names":[],"mappings":"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"}
|
package/dist/prefer.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prefer / Preference-Applied utilities per RFC 7240.
|
|
3
|
+
* RFC 7240 §2, §3.
|
|
4
|
+
*/
|
|
5
|
+
import type { PreferMap, PreferToken } from './types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Parse a Prefer header into a map of preference tokens.
|
|
8
|
+
*/
|
|
9
|
+
export declare function parsePrefer(header: string): PreferMap;
|
|
10
|
+
/**
|
|
11
|
+
* Format Prefer header from tokens.
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatPrefer(preferences: PreferMap | PreferToken[]): string;
|
|
14
|
+
/**
|
|
15
|
+
* Format Preference-Applied header.
|
|
16
|
+
*/
|
|
17
|
+
export declare function formatPreferenceApplied(preferences: PreferMap | string[]): string;
|
|
18
|
+
//# sourceMappingURL=prefer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prefer.d.ts","sourceRoot":"","sources":["../src/prefer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAe,MAAM,YAAY,CAAC;AAGtE;;GAEG;AAEH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAkDrD;AAED;;GAEG;AAEH,wBAAgB,YAAY,CAAC,WAAW,EAAE,SAAS,GAAG,WAAW,EAAE,GAAG,MAAM,CAiB3E;AAED;;GAEG;AAEH,wBAAgB,uBAAuB,CAAC,WAAW,EAAE,SAAS,GAAG,MAAM,EAAE,GAAG,MAAM,CAYjF"}
|