aimusic / ComfyUI /custom_nodes /koolook /web /sidebar /update_check.js
vidfom's picture
Upload folder using huggingface_hub (part 8)
0c85e62 verified
Raw
History Blame Contribute Delete
3.79 kB
// 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");
}