Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596import { isCid, isDid } from "@atcute/lexicons/syntax";
export interface MarkupContext { /** `owner/repo` */ repo: string; ref: string; dir?: string; host?: string; camo?: boolean; sourceLines?: boolean; collapseCode?: boolean;}
const ABSOLUTE = /^[a-z][a-z0-9+.-]*:|^\/\//i;
export const isAbsoluteUrl = (url: string): boolean => ABSOLUTE.test(url);
export const isRepoRelative = (url: string): boolean => url !== "" && !isAbsoluteUrl(url) && !url.startsWith("#");
// `.` and `..` have to collapse here instead of reaching the urlconst normalize = (path: string): string => { const parts: string[] = []; for (const part of path.split("/")) { if (part === "" || part === ".") continue; if (part === "..") parts.pop(); else parts.push(part); } return parts.join("/");};
const withinRepo = (path: string, ctx: MarkupContext): string => normalize(path.startsWith("/") ? path : `${ctx.dir ?? ""}/${path}`);
const splitSuffix = (url: string): [string, string] => { const at = url.search(/[?#]/); return at === -1 ? [url, ""] : [url.slice(0, at), url.slice(at)];};
// markdown-it percent-encodes destinations before we ever see them, so only the// ref still needs encodingconst repoUrl = (kind: string, url: string, ctx: MarkupContext): string => { const [path, suffix] = splitSuffix(url); if (path === "") return url; const ref = encodeURIComponent(ctx.ref); return `/${ctx.repo}/${kind}/${ref}/${withinRepo(path, ctx)}${suffix}`;};
export const treeUrl = (url: string, ctx: MarkupContext): string => repoUrl("tree", url, ctx);
export const rawUrl = (url: string, ctx: MarkupContext): string => repoUrl("raw", url, ctx);
const hostOf = (url: string): string | null => { try { // the base only matters for protocol relative urls, which have a host anyway return new URL(url, "https://invalid.").host; } catch { return null; }};
// camo wants the target hex encodedexport const toHex = (value: string): string => Array.from(new TextEncoder().encode(value), (byte) => byte.toString(16).padStart(2, "0")).join( "" );
// a pds blob written into markdown as blob+at://<did>/<cid>, independent of the// pds hostname. resolving the did needs the network, so the url points at the// route that does itconst BLOB_URI = /^blob\+at:\/\/([^/]+)\/([^/?#]+)$/;
const parseBlobUri = (url: string): { did: string; cid: string } | null => { const match = BLOB_URI.exec(url); if (!match) return null; const [, did, cid] = match; return isDid(did) && isCid(cid) ? { did, cid } : null;};
export const mediaUrl = (url: string, ctx: MarkupContext): string => { const blob = parseBlobUri(url); if (blob) return `/blob/${blob.did}/${blob.cid}`; if (isRepoRelative(url)) return rawUrl(url, ctx); if (!ctx.camo || !isAbsoluteUrl(url) || hostOf(url) === ctx.host) return url; return `/camo/${toHex(url)}`;};
export const mediaSrcset = (srcset: string, ctx: MarkupContext): string => srcset .split(",") .map((candidate) => { const [url, ...descriptors] = candidate.trim().split(/\s+/); return [mediaUrl(url, ctx), ...descriptors].join(" "); }) .join(", ");