···1+// Copyright 2018-2026 the Deno authors. MIT license.
2+/*!
3+ * Adapted directly from negotiator at https://github.com/jshttp/negotiator/
4+ * which is licensed as follows:
5+ *
6+ * (The MIT License)
7+ *
8+ * Copyright (c) 2012-2014 Federico Romero
9+ * Copyright (c) 2012-2014 Isaac Z. Schlueter
10+ * Copyright (c) 2014-2015 Douglas Christopher Wilson
11+ *
12+ * Permission is hereby granted, free of charge, to any person obtaining
13+ * a copy of this software and associated documentation files (the
14+ * 'Software'), to deal in the Software without restriction, including
15+ * without limitation the rights to use, copy, modify, merge, publish,
16+ * distribute, sublicense, and/or sell copies of the Software, and to
17+ * permit persons to whom the Software is furnished to do so, subject to
18+ * the following conditions:
19+ *
20+ * The above copyright notice and this permission notice shall be
21+ * included in all copies or substantial portions of the Software.
22+ *
23+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
24+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
26+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
27+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
28+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30+ */
31+32+export interface Specificity {
33+ i: number;
34+ o: number | undefined;
35+ q: number;
36+ s: number | undefined;
37+}
38+39+export function compareSpecs(a: Specificity, b: Specificity): number {
40+ return (
41+ b.q - a.q ||
42+ (b.s ?? 0) - (a.s ?? 0) ||
43+ (a.o ?? 0) - (b.o ?? 0) ||
44+ a.i - b.i ||
45+ 0
46+ );
47+}
48+49+export function isQuality(spec: Specificity): boolean {
50+ return spec.q > 0;
51+}
52+53+interface LanguageSpecificity extends Specificity {
54+ prefix: string;
55+ suffix: string | undefined;
56+ full: string;
57+}
58+59+const SIMPLE_LANGUAGE_REGEXP = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
60+61+function parseLanguage(
62+ str: string,
63+ i: number,
64+): LanguageSpecificity | undefined {
65+ const match = SIMPLE_LANGUAGE_REGEXP.exec(str);
66+ if (!match) {
67+ return undefined;
68+ }
69+70+ const [, prefix, suffix] = match;
71+ if (!prefix) {
72+ return undefined;
73+ }
74+75+ const full = suffix !== undefined ? `${prefix}-${suffix}` : prefix;
76+77+ let q = 1;
78+ if (match[3]) {
79+ const params = match[3].split(";");
80+ for (const param of params) {
81+ const [key, value] = param.trim().split("=");
82+ if (key === "q" && value) {
83+ q = parseFloat(value);
84+ break;
85+ }
86+ }
87+ }
88+89+ return { prefix, suffix, full, i, o: undefined, q, s: undefined };
90+}
91+92+function parseAcceptLanguage(accept: string): LanguageSpecificity[] {
93+ const accepts = accept.split(",");
94+ const result: LanguageSpecificity[] = [];
95+96+ for (const [i, accept] of accepts.entries()) {
97+ const language = parseLanguage(accept.trim(), i);
98+ if (language) {
99+ result.push(language);
100+ }
101+ }
102+ return result;
103+}
104+105+function specify(
106+ language: string,
107+ spec: LanguageSpecificity,
108+ i: number,
109+): Specificity | undefined {
110+ const p = parseLanguage(language, i);
111+ if (!p) {
112+ return undefined;
113+ }
114+ let s = 0;
115+ if (spec.full.toLowerCase() === p.full.toLowerCase()) {
116+ s |= 4;
117+ } else if (spec.prefix.toLowerCase() === p.prefix.toLowerCase()) {
118+ s |= 2;
119+ } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {
120+ s |= 1;
121+ } else if (spec.full !== "*") {
122+ return;
123+ }
124+125+ return { i, o: spec.i, q: spec.q, s };
126+}
127+128+function getLanguagePriority(
129+ language: string,
130+ accepted: LanguageSpecificity[],
131+ index: number,
132+): Specificity {
133+ let priority: Specificity = { i: -1, o: -1, q: 0, s: 0 };
134+ for (const accepts of accepted) {
135+ const spec = specify(language, accepts, index);
136+ if (
137+ spec &&
138+ ((priority.s ?? 0) - (spec.s ?? 0) || priority.q - spec.q ||
139+ (priority.o ?? 0) - (spec.o ?? 0)) < 0
140+ ) {
141+ priority = spec;
142+ }
143+ }
144+ return priority;
145+}
146+147+export function preferredLanguages(
148+ accept = "*",
149+ provided?: string[],
150+): string[] {
151+ const accepts = parseAcceptLanguage(accept);
152+153+ if (!provided) {
154+ return accepts
155+ .filter(isQuality)
156+ .sort(compareSpecs)
157+ .map((spec) => spec.full);
158+ }
159+160+ const priorities = provided
161+ .map((type, index) => getLanguagePriority(type, accepts, index));
162+163+ return priorities
164+ .filter(isQuality)
165+ .sort(compareSpecs)
166+ .map((priority) => provided[priorities.indexOf(priority)]!);
167+}
168+169+/**
170+ * Returns an array of media types accepted by the request, in order of
171+ * preference. If there are no media types supplied in the request, then any
172+ * media type selector will be returned.
173+ *
174+ * @example Usage
175+ * ```ts
176+ * import { accepts } from "@std/http/negotiation";
177+ * import { assertEquals } from "@std/assert";
178+ *
179+ * const request = new Request("https://example.com/", {
180+ * headers: {
181+ * accept:
182+ * "text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, *\/*;q=0.8",
183+ * },
184+ * });
185+ *
186+ * assertEquals(accepts(request), [
187+ * "text/html",
188+ * "application/xhtml+xml",
189+ * "image/webp",
190+ * "application/xml",
191+ * "*\/*",
192+ * ]);
193+ * ```
194+ *
195+ * @param request The request to get the acceptable media types for.
196+ * @returns An array of acceptable media types.
197+ */
198+export function accepts(request: Pick<Request, "headers">): string[];
199+/**
200+ * For a given set of media types, return the best match accepted in the
201+ * request. If no media type matches, then the function returns `undefined`.
202+ *
203+ * @example Usage
204+ * ```ts
205+ * import { accepts } from "@std/http/negotiation";
206+ * import { assertEquals } from "@std/assert";
207+ *
208+ * const request = new Request("https://example.com/", {
209+ * headers: {
210+ * accept:
211+ * "text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, *\/*;q=0.8",
212+ * },
213+ * });
214+ *
215+ * assertEquals(accepts(request, "text/html", "image/webp"), "text/html");
216+ * ```
217+ *
218+ * @typeParam T The type of supported content-types (if provided).
219+ * @param request The request to get the acceptable media types for.
220+ * @param types An array of media types to find the best matching one from.
221+ * @returns The best matching media type, if any match.
222+ */
223+export function accepts<T extends string = string>(
224+ request: Pick<Request, "headers">,
225+ ...types: T[]
226+): T | undefined;
227+export function accepts(
228+ request: Pick<Request, "headers">,
229+ ...types: string[]
230+): string | string[] | undefined {
231+ const accept = request.headers.get("accept");
232+ return types.length
233+ ? accept ? preferredMediaTypes(accept, types)[0] : types[0]
234+ : accept
235+ ? preferredMediaTypes(accept)
236+ : ["*/*"];
237+}
238+239+/**
240+ * Returns an array of content encodings accepted by the request, in order of
241+ * preference. If there are no encoding supplied in the request, then `["*"]`
242+ * is returned, implying any encoding is accepted.
243+ *
244+ * @example Usage
245+ * ```ts
246+ * import { acceptsEncodings } from "@std/http/negotiation";
247+ * import { assertEquals } from "@std/assert";
248+ *
249+ * const request = new Request("https://example.com/", {
250+ * headers: { "accept-encoding": "deflate, gzip;q=1.0, *;q=0.5" },
251+ * });
252+ *
253+ * assertEquals(acceptsEncodings(request), ["deflate", "gzip", "*"]);
254+ * ```
255+ *
256+ * @param request The request to get the acceptable content encodings for.
257+ * @returns An array of content encodings this request accepts.
258+ */
259+export function acceptsEncodings(request: Pick<Request, "headers">): string[];
260+/**
261+ * For a given set of content encodings, return the best match accepted in the
262+ * request. If no content encodings match, then the function returns
263+ * `undefined`.
264+ *
265+ * **NOTE:** You should always supply `identity` as one of the encodings
266+ * to ensure that there is a match when the `Accept-Encoding` header is part
267+ * of the request.
268+ *
269+ * @example Usage
270+ * ```ts
271+ * import { acceptsEncodings } from "@std/http/negotiation";
272+ * import { assertEquals } from "@std/assert";
273+ *
274+ * const request = new Request("https://example.com/", {
275+ * headers: { "accept-encoding": "deflate, gzip;q=1.0, *;q=0.5" },
276+ * });
277+ *
278+ * assertEquals(acceptsEncodings(request, "gzip", "identity"), "gzip");
279+ * ```
280+ *
281+ * @typeParam T The type of supported encodings (if provided).
282+ * @param request The request to get the acceptable content encodings for.
283+ * @param encodings An array of encodings to find the best matching one from.
284+ * @returns The best matching encoding, if any match.
285+ */
286+export function acceptsEncodings<T extends string = string>(
287+ request: Pick<Request, "headers">,
288+ ...encodings: T[]
289+): T | undefined;
290+export function acceptsEncodings(
291+ request: Pick<Request, "headers">,
292+ ...encodings: string[]
293+): string | string[] | undefined {
294+ const acceptEncoding = request.headers.get("accept-encoding");
295+ return encodings.length
296+ ? acceptEncoding
297+ ? preferredEncodings(acceptEncoding, encodings)[0]
298+ : encodings[0]
299+ : acceptEncoding
300+ ? preferredEncodings(acceptEncoding)
301+ : ["*"];
302+}
303+304+/**
305+ * Returns an array of languages accepted by the request, in order of
306+ * preference. If there are no languages supplied in the request, then `["*"]`
307+ * is returned, imply any language is accepted.
308+ *
309+ * @example Usage
310+ * ```ts
311+ * import { acceptsLanguages } from "@std/http/negotiation";
312+ * import { assertEquals } from "@std/assert";
313+ *
314+ * const request = new Request("https://example.com/", {
315+ * headers: {
316+ * "accept-language": "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
317+ * },
318+ * });
319+ *
320+ * assertEquals(acceptsLanguages(request), ["fr-CH", "fr", "en", "de", "*"]);
321+ * ```
322+ *
323+ * @param request The request to get the acceptable languages for.
324+ * @returns An array of languages this request accepts.
325+ */
326+export function acceptsLanguages(request: Pick<Request, "headers">): string[];
327+/**
328+ * For a given set of languages, return the best match accepted in the request.
329+ * If no languages match, then the function returns `undefined`.
330+ *
331+ * @example Usage
332+ * ```ts
333+ * import { acceptsLanguages } from "@std/http/negotiation";
334+ * import { assertEquals } from "@std/assert";
335+ *
336+ * const request = new Request("https://example.com/", {
337+ * headers: {
338+ * "accept-language": "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
339+ * },
340+ * });
341+ *
342+ * assertEquals(acceptsLanguages(request, "en-gb", "en-us", "en"), "en");
343+ * ```
344+ *
345+ * @typeParam T The type of supported languages (if provided).
346+ * @param request The request to get the acceptable language for.
347+ * @param langs An array of languages to find the best matching one from.
348+ * @returns The best matching language, if any match.
349+ */
350+export function acceptsLanguages<T extends string = string>(
351+ request: Pick<Request, "headers">,
352+ ...langs: T[]
353+): T | undefined;
354+export function acceptsLanguages(
355+ request: Pick<Request, "headers">,
356+ ...langs: string[]
357+): string | string[] | undefined {
358+ const acceptLanguage = request.headers.get("accept-language");
359+ return langs.length
360+ ? acceptLanguage ? preferredLanguages(acceptLanguage, langs)[0] : langs[0]
361+ : acceptLanguage
362+ ? preferredLanguages(acceptLanguage)
363+ : ["*"];
364+}
+7
src/routes/bookmarks/[did=did]/+page.server.ts
···2import { parseBookmark } from "$lib/valibot";
3import * as TID from "@atcute/tid";
4import { type Actions, fail } from "@sveltejs/kit";
000000056export const actions = {
7 deleteBookmark: async (event) => {
···2import { parseBookmark } from "$lib/valibot";
3import * as TID from "@atcute/tid";
4import { type Actions, fail } from "@sveltejs/kit";
5+import type { PageServerLoad } from "./$types";
6+7+export const load: PageServerLoad = async ({ locals }) => {
8+ return {
9+ locale: locals.locale,
10+ };
11+};
1213export const actions = {
14 deleteBookmark: async (event) => {