at main 3.6 kB view raw
1const API = (() => { 2 const ENDPOINTS = { 3 owner: "sh.tangled.owner", 4 list: "sh.tangled.repo.list", 5 tree: "sh.tangled.repo.tree", 6 blob: "sh.tangled.repo.blob", 7 branches: "sh.tangled.repo.branches", 8 tags: "sh.tangled.repo.tags", 9 defaultBranch: "sh.tangled.repo.getDefaultBranch", 10 archive: "sh.tangled.repo.archive", 11 }; 12 13 let baseUrl = ""; 14 15 function setBaseUrl(url) { 16 baseUrl = url.replace(/\/+$/, ""); 17 } 18 19 function getBaseUrl() { 20 return baseUrl; 21 } 22 23 async function fetchWithRetry(url, options = {}, retries = 3) { 24 let lastError; 25 for (let attempt = 1; attempt <= retries; attempt++) { 26 try { 27 const response = await fetch(url, options); 28 if (response.ok) return response; 29 lastError = new Error(`HTTP ${response.status}`); 30 if (attempt < retries) { 31 await new Promise((resolve) => setTimeout(resolve, 1000)); 32 } 33 } catch (err) { 34 lastError = err; 35 if (attempt < retries) { 36 await new Promise((resolve) => setTimeout(resolve, 1000)); 37 } 38 } 39 } 40 throw lastError; 41 } 42 43 async function getOwner() { 44 const url = `${baseUrl}/xrpc/${ENDPOINTS.owner}`; 45 const response = await fetchWithRetry(url); 46 return response.json(); 47 } 48 49 async function listRepos() { 50 const url = `${baseUrl}/xrpc/${ENDPOINTS.list}`; 51 const response = await fetch(url); 52 if (!response.ok) throw new Error(`HTTP ${response.status}`); 53 return response.json(); 54 } 55 56 async function getDefaultBranch(repo) { 57 const url = `${baseUrl}/xrpc/${ENDPOINTS.defaultBranch}?repo=${encodeURIComponent(repo)}`; 58 const response = await fetch(url); 59 if (!response.ok) throw new Error(`HTTP ${response.status}`); 60 return response.json(); 61 } 62 63 async function getBranches(repo) { 64 const url = `${baseUrl}/xrpc/${ENDPOINTS.branches}?repo=${encodeURIComponent(repo)}`; 65 const response = await fetch(url); 66 if (!response.ok) throw new Error(`HTTP ${response.status}`); 67 return response.json(); 68 } 69 70 async function getTree(repo, branch, path = "") { 71 const url = `${baseUrl}/xrpc/${ENDPOINTS.tree}?repo=${encodeURIComponent(repo)}&branch=${encodeURIComponent(branch)}&path=${encodeURIComponent(path)}`; 72 const response = await fetchWithRetry(url); 73 return response.json(); 74 } 75 76 async function getBlob(repo, branch, path) { 77 const url = `${baseUrl}/xrpc/${ENDPOINTS.blob}?repo=${encodeURIComponent(repo)}&branch=${encodeURIComponent(branch)}&path=${encodeURIComponent(path)}`; 78 const response = await fetchWithRetry(url); 79 return response.json(); 80 } 81 82 function getArchiveUrl(repo, branch) { 83 return `${baseUrl}/xrpc/${ENDPOINTS.archive}?repo=${encodeURIComponent(repo)}&branch=${encodeURIComponent(branch)}`; 84 } 85 86 async function resolveDID(did) { 87 try { 88 const url = `https://plc.directory/${encodeURIComponent(did)}`; 89 const response = await fetch(url); 90 if (!response.ok) return null; 91 92 const didDocument = await response.json(); 93 94 // Extract handle from alsoKnownAs field 95 if (didDocument.alsoKnownAs && didDocument.alsoKnownAs.length > 0) { 96 const handle = didDocument.alsoKnownAs[0].replace(/^at:\/\//, ""); 97 return handle; 98 } 99 100 return null; 101 } catch (error) { 102 console.error("Failed to resolve DID:", error); 103 return null; 104 } 105 } 106 107 return { 108 setBaseUrl, 109 getBaseUrl, 110 getOwner, 111 listRepos, 112 getDefaultBranch, 113 getBranches, 114 getTree, 115 getBlob, 116 getArchiveUrl, 117 resolveDID, 118 }; 119})();