mirror of
https://github.com/firestar5683/StarPilot.git
synced 2026-11-08 13:33:42 +08:00
55 lines
3.0 KiB
JavaScript
55 lines
3.0 KiB
JavaScript
export async function requestJson(url, { fetcher = (...args) => fetch(...args), timeout = 8000,
|
|
later = (fn, ms) => setTimeout(fn, ms), cancel = id => clearTimeout(id), request = {}, withResponse = false } = {}) {
|
|
if (globalThis.StarPilotApp?.bluetooth?.()) timeout = Math.max(timeout, 120000)
|
|
const controller = new AbortController()
|
|
let timer
|
|
let rejectCanceled
|
|
const abort = () => { controller.abort(); rejectCanceled?.(Object.assign(new Error("Request canceled"), { name: "AbortError" })) }
|
|
request.signal?.addEventListener("abort", abort, { once: true })
|
|
try {
|
|
return await Promise.race([
|
|
new Promise((_, reject) => { rejectCanceled = reject; if (request.signal?.aborted) abort() }),
|
|
(async () => {
|
|
const response = await fetcher(url, { ...request, cache: "no-store", credentials: "same-origin", signal: controller.signal })
|
|
if (!response.ok && !withResponse) {
|
|
const body = await response.json?.().catch(() => null)
|
|
const error = new Error(typeof body?.error === "string" ? body.error : "Galaxy could not be reached")
|
|
error.status = response.status
|
|
error.code = body?.code
|
|
throw error
|
|
}
|
|
const data = await response.json()
|
|
return withResponse ? { response, data } : data
|
|
})(),
|
|
new Promise((_, reject) => {
|
|
timer = later(() => { controller.abort(); reject(Object.assign(new Error("Galaxy took too long to respond"), { name: "TimeoutError" })) }, timeout)
|
|
}),
|
|
])
|
|
} finally {
|
|
request.signal?.removeEventListener("abort", abort)
|
|
cancel(timer)
|
|
controller.abort()
|
|
}
|
|
}
|
|
|
|
const validTool = (tool, root = false) => typeof tool?.path === "string" && tool.path.startsWith("/") &&
|
|
typeof tool.name === "string" && typeof tool.icon === "string" && typeof tool.description === "string" &&
|
|
(root ? ["always", "partial-preview", "local-only", "unavailable"].includes(tool.availability) :
|
|
tool.availability === undefined || ["always", "partial-preview", "local-only", "unavailable"].includes(tool.availability)) &&
|
|
(tool.visibility === undefined || tool.visibility === "authenticated") &&
|
|
(tool.tab === undefined || typeof tool.tab === "string") &&
|
|
(tool.sampleDescription === undefined || typeof tool.sampleDescription === "string") &&
|
|
(tool.children === undefined || Array.isArray(tool.children) && tool.children.every(child => validTool(child)))
|
|
|
|
export async function loadCatalog(options) {
|
|
const [catalog, runtime] = await Promise.all([
|
|
requestJson("./data/catalog.json", options), requestJson("./data/runtime.json", options),
|
|
])
|
|
if (runtime?.schemaVersion !== 1 || !["sample", "local"].includes(runtime.monitor)) throw new Error("Invalid Galaxy runtime")
|
|
if (catalog?.mode !== "offline-preview" || !Array.isArray(catalog.tools) || !catalog.tools.every(tool => validTool(tool, true))) {
|
|
throw new Error("Invalid Galaxy catalog")
|
|
}
|
|
return { tools: catalog.tools.slice().sort((a, b) => a.name.localeCompare(b.name)), mode: runtime.monitor,
|
|
instance: typeof runtime.instance === "string" ? runtime.instance : null }
|
|
}
|