A music player that connects to your cloud/distributed storage.
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: also support version ranges

+158 -54
+5 -1
src/_includes/layouts/kitchen.vto
··· 12 12 13 13 scripts: 14 14 - common/pages/ppr.js 15 - - common/pages/version-upgrade.js 16 15 --- 17 16 18 17 <header> ··· 34 33 <main> 35 34 {{ content }} 36 35 </main> 36 + 37 + <script> 38 + import { versionUpgrade } from "./common/pages/version-upgrade.js"; 39 + versionUpgrade(); 40 + </script>
+148 -47
src/common/pages/version-upgrade.js
··· 1 - import { canParse, greaterThan, parse as parseSemver } from "@std/semver"; 1 + import { 2 + canParse, 3 + greaterThan, 4 + parse as parseSemver, 5 + satisfies, 6 + tryParseRange, 7 + } from "@std/semver"; 2 8 3 - // Version upgrade (only works with `diffuse-artifacts` deployments) 4 - if (document.location.hostname.endsWith("diffuse.sh")) { 5 - document.querySelectorAll("#status").forEach(async (status) => { 6 - const versionOrCid = 7 - document.location.pathname.slice(1).split("/")[0]?.toLowerCase() ?? ""; 8 - const usesCid = versionOrCid.startsWith("bafy"); 9 - const { default: artifacts } = await import( 10 - `${document.location.origin}/artifacts.json`, 11 - { with: { type: "json" } } 12 - ).catch(() => ({ default: {} })); 9 + /** 10 + * Given the current URL segment and the latest known artifact, returns whether 11 + * the user is already on the latest version. 12 + * 13 + * @param {string} versionOrCid - The first path segment of the current URL 14 + * @param {{ cid: string, version: string } | null} lastArtifact - The latest artifact 15 + * @returns {boolean} 16 + * 17 + * @example No artifact means always latest 18 + * ```js 19 + * import { checkIsLatest } from "~/common/pages/version-upgrade.js"; 20 + * 21 + * if (!checkIsLatest("4.0.0", null)) throw new Error("no artifact should be latest"); 22 + * ``` 23 + * 24 + * @example CID comparison 25 + * ```js 26 + * import { checkIsLatest } from "~/common/pages/version-upgrade.js"; 27 + * 28 + * const artifact = { cid: "bafyabc", version: "4.0.0" }; 29 + * if (!checkIsLatest("bafyabc", artifact)) throw new Error("matching CID should be latest"); 30 + * if (checkIsLatest("bafyxyz", artifact)) throw new Error("different CID should not be latest"); 31 + * ``` 32 + * 33 + * @example Exact version comparison 34 + * ```js 35 + * import { checkIsLatest } from "~/common/pages/version-upgrade.js"; 36 + * 37 + * const artifact = { cid: "bafyabc", version: "4.0.0" }; 38 + * if (!checkIsLatest("4.0.0", artifact)) throw new Error("matching version should be latest"); 39 + * if (checkIsLatest("3.9.0", artifact)) throw new Error("older version should not be latest"); 40 + * ``` 41 + * 42 + * @example Version range (e.g. 4.0.x) 43 + * ```js 44 + * import { checkIsLatest } from "~/common/pages/version-upgrade.js"; 45 + * 46 + * const artifact = { cid: "bafyabc", version: "4.0.5" }; 47 + * if (!checkIsLatest("4.0.x", artifact)) throw new Error("latest within range should be latest"); 48 + * if (checkIsLatest("3.x", artifact)) throw new Error("latest outside range should not be latest"); 49 + * ``` 50 + * 51 + * @example Caret and tilde ranges 52 + * ```js 53 + * import { checkIsLatest } from "~/common/pages/version-upgrade.js"; 54 + * 55 + * const artifact = { cid: "bafyabc", version: "4.1.0" }; 56 + * if (!checkIsLatest("^4.0.1", artifact)) throw new Error("^4.0.1 should match 4.1.0"); 57 + * if (checkIsLatest("~4.0.1", artifact)) throw new Error("~4.0.1 should not match 4.1.0"); 58 + * ``` 59 + * 60 + * @example Partial versions are filled in with zeros (>=4 is equivalent to >=4.0.0) 61 + * ```js 62 + * import { checkIsLatest } from "~/common/pages/version-upgrade.js"; 63 + * 64 + * const artifact = { cid: "bafyabc", version: "4.1.0" }; 65 + * if (!checkIsLatest(">=4", artifact)) throw new Error(">=4 should match 4.1.0"); 66 + * if (checkIsLatest(">=5", artifact)) throw new Error(">=5 should not match 4.1.0"); 67 + * ``` 68 + * 69 + * @example Non-semver, non-range slugs are always latest 70 + * ```js 71 + * import { checkIsLatest } from "~/common/pages/version-upgrade.js"; 72 + * 73 + * const artifact = { cid: "bafyabc", version: "4.0.0" }; 74 + * if (!checkIsLatest("some-branch", artifact)) throw new Error("non-semver slug should be latest"); 75 + * ``` 76 + */ 77 + export function checkIsLatest(versionOrCid, lastArtifact) { 78 + if (!lastArtifact) return true; 79 + const usesCid = versionOrCid.startsWith("bafy"); 80 + if (usesCid) return versionOrCid === lastArtifact.cid; 81 + if (canParse(versionOrCid)) return versionOrCid === lastArtifact.version; 82 + const versionRange = tryParseRange(versionOrCid); 83 + if (versionRange) { 84 + return satisfies(parseSemver(lastArtifact.version), versionRange); 85 + } 86 + return true; 87 + } 13 88 14 - // Latest by semver (ignore non-semver versions) 15 - const lastArtifact = Object.values(artifacts).reduce((max, artifact) => { 89 + /** 90 + * @param {{ version: string, cid: string }[]} artifacts 91 + * @returns {{ version: string, cid: string } | null} 92 + */ 93 + function getLatestArtifact(artifacts) { 94 + return Object.values(artifacts).reduce( 95 + /** @param {{ version: string, cid: string } | null} max */ 96 + (max, artifact) => { 16 97 if (!canParse(artifact.version)) return max; 17 98 if (!max) return artifact; 18 - return greaterThan( 19 - parseSemver(artifact.version), 20 - parseSemver(max.version), 21 - ) 99 + return greaterThan(parseSemver(artifact.version), parseSemver(max.version)) 22 100 ? artifact 23 101 : max; 24 - }, null); 102 + }, 103 + /** @type {{ version: string, cid: string } | null} */ (null), 104 + ); 105 + } 106 + 107 + /** @param {Element} status */ 108 + function removeLoadingAnimation(status) { 109 + status.querySelectorAll(".ph-spinner").forEach((icon) => { 110 + icon.parentElement?.classList.add("hidden"); 111 + 112 + setTimeout(() => { 113 + icon.parentElement?.classList.remove("animate-spin"); 114 + icon.classList.remove("ph-spinner"); 115 + icon.classList.add("ph-arrow-fat-lines-up"); 116 + }, 500); 117 + }); 118 + } 25 119 26 - // Check if using latest 27 - const isLatest = !lastArtifact 28 - ? true 29 - : usesCid 30 - ? versionOrCid === lastArtifact.cid 31 - : !canParse(versionOrCid) 32 - ? true 33 - : versionOrCid === lastArtifact.version; 120 + /** 121 + * @param {Element} status 122 + * @param {{ usesCid: boolean, isLatest: boolean }} options 123 + */ 124 + function updateUpgradeLink(status, { usesCid, isLatest }) { 125 + status.querySelectorAll(`[href="/latest/"]`).forEach((a) => { 126 + if (usesCid) a.setAttribute("href", "/latest/hash/"); 127 + if (!isLatest) setTimeout(() => a.classList.remove("hidden"), 750); 128 + }); 129 + } 34 130 35 - // Remove loading animation 36 - status.querySelectorAll(".ph-spinner").forEach((icon) => { 37 - icon.parentElement?.classList.add("hidden"); 131 + /** 132 + * Setup version upgrade (only works with `diffuse-artifacts` deployments) 133 + */ 134 + export async function versionUpgrade() { 135 + const isDiffuseDomain = document.location.hostname.endsWith("diffuse.sh"); 38 136 39 - setTimeout(() => { 40 - icon.parentElement?.classList.remove("animate-spin"); 41 - icon.classList.remove("ph-spinner"); 42 - icon.classList.add("ph-arrow-fat-lines-up"); 43 - }, 500); 137 + if (!isDiffuseDomain) { 138 + document.querySelectorAll("#status a").forEach((el) => { 139 + el.classList.add("hidden"); 44 140 }); 45 141 46 - // If using CID, append `/hash/` to href 47 - status.querySelectorAll(`[href="/latest/"]`).forEach((a) => { 48 - if (usesCid) a.setAttribute("href", "/latest/hash/"); 49 - if (!isLatest) { 50 - setTimeout(() => { 51 - a.classList.remove("hidden"); 52 - }, 750); 53 - } 54 - }); 55 - }); 56 - } else { 57 - document.querySelectorAll("#status a").forEach((el) => { 58 - el.classList.add("hidden"); 142 + return; 143 + } 144 + 145 + const versionOrCid = 146 + document.location.pathname.slice(1).split("/")[0]?.toLowerCase() ?? ""; 147 + const usesCid = versionOrCid.startsWith("bafy"); 148 + 149 + const { default: artifacts } = await import( 150 + `${document.location.origin}/artifacts.json`, 151 + { with: { type: "json" } } 152 + ).catch(() => ({ default: {} })); 153 + 154 + const lastArtifact = getLatestArtifact(artifacts); 155 + const isLatest = checkIsLatest(versionOrCid, lastArtifact); 156 + 157 + document.querySelectorAll("#status").forEach((status) => { 158 + removeLoadingAnimation(status); 159 + updateUpgradeLink(status, { usesCid, isLatest }); 59 160 }); 60 161 }
-3
src/elements.vto
··· 9 9 - vendor/@phosphor-icons/web/bold/style.css 10 10 - vendor/@phosphor-icons/web/fill/style.css 11 11 12 - scripts: 13 - - common/pages/version-upgrade.js 14 - 15 12 # ELEMENTS 16 13 17 14 artwork:
+5 -3
src/index.vto
··· 6 6 - styles/diffuse/page.css 7 7 - vendor/@phosphor-icons/web/bold/style.css 8 8 - vendor/@phosphor-icons/web/fill/style.css 9 - 10 - scripts: 11 - - common/pages/version-upgrade.js 12 9 --- 13 10 14 11 <style> ··· 98 95 </section> 99 96 </div> 100 97 </main> 98 + 99 + <script> 100 + import { versionUpgrade } from "./common/pages/version-upgrade.js"; 101 + versionUpgrade(); 102 + </script>