···11+// Copyright 2018-2026 the Deno authors. MIT license.
22+/*!
33+ * Adapted directly from negotiator at https://github.com/jshttp/negotiator/
44+ * which is licensed as follows:
55+ *
66+ * (The MIT License)
77+ *
88+ * Copyright (c) 2012-2014 Federico Romero
99+ * Copyright (c) 2012-2014 Isaac Z. Schlueter
1010+ * Copyright (c) 2014-2015 Douglas Christopher Wilson
1111+ *
1212+ * Permission is hereby granted, free of charge, to any person obtaining
1313+ * a copy of this software and associated documentation files (the
1414+ * 'Software'), to deal in the Software without restriction, including
1515+ * without limitation the rights to use, copy, modify, merge, publish,
1616+ * distribute, sublicense, and/or sell copies of the Software, and to
1717+ * permit persons to whom the Software is furnished to do so, subject to
1818+ * the following conditions:
1919+ *
2020+ * The above copyright notice and this permission notice shall be
2121+ * included in all copies or substantial portions of the Software.
2222+ *
2323+ * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
2424+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2525+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
2626+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
2727+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
2828+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2929+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3030+ */
3131+3232+export interface Specificity {
3333+ i: number;
3434+ o: number | undefined;
3535+ q: number;
3636+ s: number | undefined;
3737+}
3838+3939+export function compareSpecs(a: Specificity, b: Specificity): number {
4040+ return (
4141+ b.q - a.q ||
4242+ (b.s ?? 0) - (a.s ?? 0) ||
4343+ (a.o ?? 0) - (b.o ?? 0) ||
4444+ a.i - b.i ||
4545+ 0
4646+ );
4747+}
4848+4949+export function isQuality(spec: Specificity): boolean {
5050+ return spec.q > 0;
5151+}
5252+5353+interface LanguageSpecificity extends Specificity {
5454+ prefix: string;
5555+ suffix: string | undefined;
5656+ full: string;
5757+}
5858+5959+const SIMPLE_LANGUAGE_REGEXP = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
6060+6161+function parseLanguage(
6262+ str: string,
6363+ i: number,
6464+): LanguageSpecificity | undefined {
6565+ const match = SIMPLE_LANGUAGE_REGEXP.exec(str);
6666+ if (!match) {
6767+ return undefined;
6868+ }
6969+7070+ const [, prefix, suffix] = match;
7171+ if (!prefix) {
7272+ return undefined;
7373+ }
7474+7575+ const full = suffix !== undefined ? `${prefix}-${suffix}` : prefix;
7676+7777+ let q = 1;
7878+ if (match[3]) {
7979+ const params = match[3].split(";");
8080+ for (const param of params) {
8181+ const [key, value] = param.trim().split("=");
8282+ if (key === "q" && value) {
8383+ q = parseFloat(value);
8484+ break;
8585+ }
8686+ }
8787+ }
8888+8989+ return { prefix, suffix, full, i, o: undefined, q, s: undefined };
9090+}
9191+9292+function parseAcceptLanguage(accept: string): LanguageSpecificity[] {
9393+ const accepts = accept.split(",");
9494+ const result: LanguageSpecificity[] = [];
9595+9696+ for (const [i, accept] of accepts.entries()) {
9797+ const language = parseLanguage(accept.trim(), i);
9898+ if (language) {
9999+ result.push(language);
100100+ }
101101+ }
102102+ return result;
103103+}
104104+105105+function specify(
106106+ language: string,
107107+ spec: LanguageSpecificity,
108108+ i: number,
109109+): Specificity | undefined {
110110+ const p = parseLanguage(language, i);
111111+ if (!p) {
112112+ return undefined;
113113+ }
114114+ let s = 0;
115115+ if (spec.full.toLowerCase() === p.full.toLowerCase()) {
116116+ s |= 4;
117117+ } else if (spec.prefix.toLowerCase() === p.prefix.toLowerCase()) {
118118+ s |= 2;
119119+ } else if (spec.full.toLowerCase() === p.prefix.toLowerCase()) {
120120+ s |= 1;
121121+ } else if (spec.full !== "*") {
122122+ return;
123123+ }
124124+125125+ return { i, o: spec.i, q: spec.q, s };
126126+}
127127+128128+function getLanguagePriority(
129129+ language: string,
130130+ accepted: LanguageSpecificity[],
131131+ index: number,
132132+): Specificity {
133133+ let priority: Specificity = { i: -1, o: -1, q: 0, s: 0 };
134134+ for (const accepts of accepted) {
135135+ const spec = specify(language, accepts, index);
136136+ if (
137137+ spec &&
138138+ ((priority.s ?? 0) - (spec.s ?? 0) || priority.q - spec.q ||
139139+ (priority.o ?? 0) - (spec.o ?? 0)) < 0
140140+ ) {
141141+ priority = spec;
142142+ }
143143+ }
144144+ return priority;
145145+}
146146+147147+export function preferredLanguages(
148148+ accept = "*",
149149+ provided?: string[],
150150+): string[] {
151151+ const accepts = parseAcceptLanguage(accept);
152152+153153+ if (!provided) {
154154+ return accepts
155155+ .filter(isQuality)
156156+ .sort(compareSpecs)
157157+ .map((spec) => spec.full);
158158+ }
159159+160160+ const priorities = provided
161161+ .map((type, index) => getLanguagePriority(type, accepts, index));
162162+163163+ return priorities
164164+ .filter(isQuality)
165165+ .sort(compareSpecs)
166166+ .map((priority) => provided[priorities.indexOf(priority)]!);
167167+}
168168+169169+/**
170170+ * Returns an array of media types accepted by the request, in order of
171171+ * preference. If there are no media types supplied in the request, then any
172172+ * media type selector will be returned.
173173+ *
174174+ * @example Usage
175175+ * ```ts
176176+ * import { accepts } from "@std/http/negotiation";
177177+ * import { assertEquals } from "@std/assert";
178178+ *
179179+ * const request = new Request("https://example.com/", {
180180+ * headers: {
181181+ * accept:
182182+ * "text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, *\/*;q=0.8",
183183+ * },
184184+ * });
185185+ *
186186+ * assertEquals(accepts(request), [
187187+ * "text/html",
188188+ * "application/xhtml+xml",
189189+ * "image/webp",
190190+ * "application/xml",
191191+ * "*\/*",
192192+ * ]);
193193+ * ```
194194+ *
195195+ * @param request The request to get the acceptable media types for.
196196+ * @returns An array of acceptable media types.
197197+ */
198198+export function accepts(request: Pick<Request, "headers">): string[];
199199+/**
200200+ * For a given set of media types, return the best match accepted in the
201201+ * request. If no media type matches, then the function returns `undefined`.
202202+ *
203203+ * @example Usage
204204+ * ```ts
205205+ * import { accepts } from "@std/http/negotiation";
206206+ * import { assertEquals } from "@std/assert";
207207+ *
208208+ * const request = new Request("https://example.com/", {
209209+ * headers: {
210210+ * accept:
211211+ * "text/html, application/xhtml+xml, application/xml;q=0.9, image/webp, *\/*;q=0.8",
212212+ * },
213213+ * });
214214+ *
215215+ * assertEquals(accepts(request, "text/html", "image/webp"), "text/html");
216216+ * ```
217217+ *
218218+ * @typeParam T The type of supported content-types (if provided).
219219+ * @param request The request to get the acceptable media types for.
220220+ * @param types An array of media types to find the best matching one from.
221221+ * @returns The best matching media type, if any match.
222222+ */
223223+export function accepts<T extends string = string>(
224224+ request: Pick<Request, "headers">,
225225+ ...types: T[]
226226+): T | undefined;
227227+export function accepts(
228228+ request: Pick<Request, "headers">,
229229+ ...types: string[]
230230+): string | string[] | undefined {
231231+ const accept = request.headers.get("accept");
232232+ return types.length
233233+ ? accept ? preferredMediaTypes(accept, types)[0] : types[0]
234234+ : accept
235235+ ? preferredMediaTypes(accept)
236236+ : ["*/*"];
237237+}
238238+239239+/**
240240+ * Returns an array of content encodings accepted by the request, in order of
241241+ * preference. If there are no encoding supplied in the request, then `["*"]`
242242+ * is returned, implying any encoding is accepted.
243243+ *
244244+ * @example Usage
245245+ * ```ts
246246+ * import { acceptsEncodings } from "@std/http/negotiation";
247247+ * import { assertEquals } from "@std/assert";
248248+ *
249249+ * const request = new Request("https://example.com/", {
250250+ * headers: { "accept-encoding": "deflate, gzip;q=1.0, *;q=0.5" },
251251+ * });
252252+ *
253253+ * assertEquals(acceptsEncodings(request), ["deflate", "gzip", "*"]);
254254+ * ```
255255+ *
256256+ * @param request The request to get the acceptable content encodings for.
257257+ * @returns An array of content encodings this request accepts.
258258+ */
259259+export function acceptsEncodings(request: Pick<Request, "headers">): string[];
260260+/**
261261+ * For a given set of content encodings, return the best match accepted in the
262262+ * request. If no content encodings match, then the function returns
263263+ * `undefined`.
264264+ *
265265+ * **NOTE:** You should always supply `identity` as one of the encodings
266266+ * to ensure that there is a match when the `Accept-Encoding` header is part
267267+ * of the request.
268268+ *
269269+ * @example Usage
270270+ * ```ts
271271+ * import { acceptsEncodings } from "@std/http/negotiation";
272272+ * import { assertEquals } from "@std/assert";
273273+ *
274274+ * const request = new Request("https://example.com/", {
275275+ * headers: { "accept-encoding": "deflate, gzip;q=1.0, *;q=0.5" },
276276+ * });
277277+ *
278278+ * assertEquals(acceptsEncodings(request, "gzip", "identity"), "gzip");
279279+ * ```
280280+ *
281281+ * @typeParam T The type of supported encodings (if provided).
282282+ * @param request The request to get the acceptable content encodings for.
283283+ * @param encodings An array of encodings to find the best matching one from.
284284+ * @returns The best matching encoding, if any match.
285285+ */
286286+export function acceptsEncodings<T extends string = string>(
287287+ request: Pick<Request, "headers">,
288288+ ...encodings: T[]
289289+): T | undefined;
290290+export function acceptsEncodings(
291291+ request: Pick<Request, "headers">,
292292+ ...encodings: string[]
293293+): string | string[] | undefined {
294294+ const acceptEncoding = request.headers.get("accept-encoding");
295295+ return encodings.length
296296+ ? acceptEncoding
297297+ ? preferredEncodings(acceptEncoding, encodings)[0]
298298+ : encodings[0]
299299+ : acceptEncoding
300300+ ? preferredEncodings(acceptEncoding)
301301+ : ["*"];
302302+}
303303+304304+/**
305305+ * Returns an array of languages accepted by the request, in order of
306306+ * preference. If there are no languages supplied in the request, then `["*"]`
307307+ * is returned, imply any language is accepted.
308308+ *
309309+ * @example Usage
310310+ * ```ts
311311+ * import { acceptsLanguages } from "@std/http/negotiation";
312312+ * import { assertEquals } from "@std/assert";
313313+ *
314314+ * const request = new Request("https://example.com/", {
315315+ * headers: {
316316+ * "accept-language": "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
317317+ * },
318318+ * });
319319+ *
320320+ * assertEquals(acceptsLanguages(request), ["fr-CH", "fr", "en", "de", "*"]);
321321+ * ```
322322+ *
323323+ * @param request The request to get the acceptable languages for.
324324+ * @returns An array of languages this request accepts.
325325+ */
326326+export function acceptsLanguages(request: Pick<Request, "headers">): string[];
327327+/**
328328+ * For a given set of languages, return the best match accepted in the request.
329329+ * If no languages match, then the function returns `undefined`.
330330+ *
331331+ * @example Usage
332332+ * ```ts
333333+ * import { acceptsLanguages } from "@std/http/negotiation";
334334+ * import { assertEquals } from "@std/assert";
335335+ *
336336+ * const request = new Request("https://example.com/", {
337337+ * headers: {
338338+ * "accept-language": "fr-CH, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5",
339339+ * },
340340+ * });
341341+ *
342342+ * assertEquals(acceptsLanguages(request, "en-gb", "en-us", "en"), "en");
343343+ * ```
344344+ *
345345+ * @typeParam T The type of supported languages (if provided).
346346+ * @param request The request to get the acceptable language for.
347347+ * @param langs An array of languages to find the best matching one from.
348348+ * @returns The best matching language, if any match.
349349+ */
350350+export function acceptsLanguages<T extends string = string>(
351351+ request: Pick<Request, "headers">,
352352+ ...langs: T[]
353353+): T | undefined;
354354+export function acceptsLanguages(
355355+ request: Pick<Request, "headers">,
356356+ ...langs: string[]
357357+): string | string[] | undefined {
358358+ const acceptLanguage = request.headers.get("accept-language");
359359+ return langs.length
360360+ ? acceptLanguage ? preferredLanguages(acceptLanguage, langs)[0] : langs[0]
361361+ : acceptLanguage
362362+ ? preferredLanguages(acceptLanguage)
363363+ : ["*"];
364364+}
+7
src/routes/bookmarks/[did=did]/+page.server.ts
···22import { parseBookmark } from "$lib/valibot";
33import * as TID from "@atcute/tid";
44import { type Actions, fail } from "@sveltejs/kit";
55+import type { PageServerLoad } from "./$types";
66+77+export const load: PageServerLoad = async ({ locals }) => {
88+ return {
99+ locale: locals.locale,
1010+ };
1111+};
512613export const actions = {
714 deleteBookmark: async (event) => {