112 lines
3.7 KiB
JavaScript
112 lines
3.7 KiB
JavaScript
// SPDX-License-Identifier: GPL-3.0-only
|
|
|
|
import { noStoreUrl } from "./constants.js";
|
|
|
|
const LOCAL_VERSION_ROUTE = "/koolook/api/version";
|
|
const FALLBACK_LATEST_RELEASE_API =
|
|
"https://api.github.com/repos/malkuthro/ComfyUI-Koolook/releases/latest";
|
|
const RELEASES_URL = "https://github.com/malkuthro/ComfyUI-Koolook/releases";
|
|
|
|
function releaseNotesUrl(version, rawUrl) {
|
|
const expected = `${RELEASES_URL}/tag/v${version}`;
|
|
if (typeof rawUrl !== "string") return expected;
|
|
try {
|
|
const parsed = new URL(rawUrl);
|
|
if (
|
|
parsed.protocol === "https:" &&
|
|
parsed.hostname === "github.com" &&
|
|
parsed.pathname === `/malkuthro/ComfyUI-Koolook/releases/tag/v${version}`
|
|
) {
|
|
return parsed.href;
|
|
}
|
|
} catch (_err) {
|
|
// Fall through to the canonical release URL.
|
|
}
|
|
return expected;
|
|
}
|
|
|
|
function parseVersion(value) {
|
|
if (typeof value !== "string") return null;
|
|
const match = value.trim().match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+.*)?$/);
|
|
if (!match) return null;
|
|
return {
|
|
parts: match.slice(1, 4).map((part) => Number(part)),
|
|
prerelease: match[4] || "",
|
|
};
|
|
}
|
|
|
|
export function parseSemver(value) {
|
|
const parsed = parseVersion(value);
|
|
return parsed ? parsed.parts : null;
|
|
}
|
|
|
|
export function compareSemver(a, b) {
|
|
const av = parseVersion(a);
|
|
const bv = parseVersion(b);
|
|
if (!av || !bv) return 0;
|
|
for (let i = 0; i < 3; i += 1) {
|
|
if (av.parts[i] > bv.parts[i]) return 1;
|
|
if (av.parts[i] < bv.parts[i]) return -1;
|
|
}
|
|
if (av.prerelease && !bv.prerelease) return -1;
|
|
if (!av.prerelease && bv.prerelease) return 1;
|
|
return 0;
|
|
}
|
|
|
|
function normalizeLatestRelease(payload) {
|
|
if (!payload || typeof payload !== "object") return null;
|
|
const version = typeof payload.tag_name === "string"
|
|
? payload.tag_name.replace(/^v/i, "")
|
|
: "";
|
|
if (!parseSemver(version)) return null;
|
|
return {
|
|
version,
|
|
url: releaseNotesUrl(version, payload.html_url),
|
|
};
|
|
}
|
|
|
|
export async function checkForUpdate(fetchImpl = fetch) {
|
|
try {
|
|
const localResponse = await fetchImpl(noStoreUrl(LOCAL_VERSION_ROUTE), {
|
|
cache: "no-store",
|
|
});
|
|
if (!localResponse || !localResponse.ok) return null;
|
|
const local = await localResponse.json();
|
|
const current = typeof local.version === "string" ? local.version : "";
|
|
if (!parseSemver(current)) return null;
|
|
|
|
const latestUrl = typeof local.latestReleaseApiUrl === "string" && local.latestReleaseApiUrl
|
|
? local.latestReleaseApiUrl
|
|
: FALLBACK_LATEST_RELEASE_API;
|
|
const latestResponse = await fetchImpl(latestUrl, { cache: "no-store" });
|
|
if (!latestResponse || !latestResponse.ok) return null;
|
|
const latest = normalizeLatestRelease(await latestResponse.json());
|
|
if (!latest || compareSemver(latest.version, current) <= 0) return null;
|
|
|
|
return {
|
|
current,
|
|
latest: latest.version,
|
|
url: latest.url,
|
|
};
|
|
} catch (_err) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function renderUpdateFooter(footerEl, update) {
|
|
footerEl.textContent = "";
|
|
footerEl.classList.remove("koolook-update-footer-visible");
|
|
if (!update) return;
|
|
|
|
const label = document.createElement("span");
|
|
label.textContent = "Update Available";
|
|
const link = document.createElement("a");
|
|
link.href = update.url;
|
|
link.target = "_blank";
|
|
link.rel = "noopener noreferrer";
|
|
link.textContent = "Release notes";
|
|
link.title = `Kforge Labs ${update.latest} release notes`;
|
|
footerEl.append(label, link);
|
|
footerEl.classList.add("koolook-update-footer-visible");
|
|
}
|