roulette / js /api.js
ratandeep's picture
fix: embed static-sdk Spaces from .static.hf.space
6663eb3
Raw
History Blame Contribute Delete
2.55 kB
import { CONFIG } from "./config.js";
import { normalizeTrack } from "./tracks.js";
const DEFAULT_EMOJI = "🛖";
export function parseLinkHeader(header) {
if (!header) return null;
for (const part of header.split(",")) {
const m = part.match(/<([^>]+)>\s*;\s*rel="?next"?/);
if (m) return m[1];
}
return null;
}
export function filterEligible(spaces, denylist = CONFIG.denylist) {
const deny = new Set(denylist);
return spaces.filter((s) => !deny.has(s.id) && s.private !== true);
}
export function toViewModel(s) {
const card = s.cardData || {};
const name = s.id.includes("/") ? s.id.split("/").slice(1).join("/") : s.id;
const subdomain = s.subdomain || s.id.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
// Static Spaces are served from <subdomain>.static.hf.space; everything else from <subdomain>.hf.space.
const host = s.sdk === "static" ? `${subdomain}.static.hf.space` : `${subdomain}.hf.space`;
return {
id: s.id,
name,
title: card.title || name,
emoji: card.emoji || DEFAULT_EMOJI,
shortDescription: card.short_description || "",
likes: typeof s.likes === "number" ? s.likes : 0,
sdk: s.sdk || "",
track: normalizeTrack(card.tags),
colorFrom: card.colorFrom || "gray",
colorTo: card.colorTo || "gray",
embedUrl: `https://${host}`,
hfUrl: `${CONFIG.apiBase}/spaces/${s.id}`,
};
}
// ---- network (live-verified) ----
export async function fetchAllSpaces() {
let url = `${CONFIG.apiBase}/api/spaces?author=${CONFIG.org}&full=true&limit=${CONFIG.apiLimit}`;
const all = [];
for (let guard = 0; url && guard < 20; guard++) {
const res = await fetch(url);
if (!res.ok) throw new Error(`list fetch ${res.status}`);
const page = await res.json();
all.push(...page);
url = parseLinkHeader(res.headers.get("Link"));
}
return filterEligible(all).map(toViewModel);
}
export async function fetchAllSpacesWithRetry() {
try { return await fetchAllSpaces(); }
catch { return await fetchAllSpaces(); } // retry once; second failure throws to caller
}
export async function fetchDetail(id) {
const res = await fetch(`${CONFIG.apiBase}/api/spaces/${id}`);
if (!res.ok) throw new Error(`detail ${res.status}`);
const d = await res.json();
const stage = d?.runtime?.stage || null;
const domainsReady = Array.isArray(d?.runtime?.domains)
? d.runtime.domains.some((x) => x.stage === "READY")
: false;
return { stage, domainsReady, gated: !!d.gated, disabled: !!d.disabled, likes: d.likes };
}