Knot server viewer.
knotview.srv.rbrt.fr
knot
tangled
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})();