Review API content negotiation

src/content-negotiation.tsTypeScript
@@ -0,0 +1,39 @@
1+export type ResponseFormat = "json" | "csv";
2+
3+export type Negotiation =
4+ | { status: 200; format: ResponseFormat; contentType: string }
5+ | { status: 406; format: null; contentType: null };
6+
7+function qualityFor(header: string, mediaType: string): number {
8+ const range = header
9+ .split(",")
10+ .map((entry) => entry.trim())
11+ .find((entry) => entry.startsWith(mediaType));
12+
13+ if (!range) return -1;
14+
15+ const quality = range
16+ .split(";")
17+ .map((part) => part.trim())
18+ .find((part) => part.startsWith("q="));
19+
20+ return quality ? Number(quality.slice(2)) : 1;
21+}
22+
23+export function negotiateResponse(accept: string | undefined): Negotiation {
24+ if (!accept?.trim()) {
25+ return { status: 200, format: "json", contentType: "application/json" };
26+ }
27+
28+ const jsonQuality = qualityFor(accept, "application/json");
29+ const csvQuality = qualityFor(accept, "text/csv");
30+
31+ if (jsonQuality >= 0) {
32+ return { status: 200, format: "json", contentType: "application/json" };
33+ }
34+ if (csvQuality >= 0) {
35+ return { status: 200, format: "csv", contentType: "text/csv" };
36+ }
37+
38+ return { status: 200, format: "json", contentType: "application/json" };
39+}