const output = document.querySelector("#run-output"); if (output) { let refreshTimer; const refresh = async () => { const response = await fetch(output.dataset.outputUrl, { cache: "no-store" }); if (response.ok) { const nextOutput = (await response.json()).output; if (nextOutput !== output.textContent) { output.textContent = nextOutput; } } const status = document.querySelector("#run-status"); if (status) { const stateResponse = await fetch(status.dataset.stateUrl, { cache: "no-store" }); if (stateResponse.ok) { const state = await stateResponse.json(); status.textContent = state.status; if (state.status !== "queued" && state.status !== "running") { window.clearInterval(refreshTimer); } } } }; refresh(); refreshTimer = window.setInterval(refresh, 2000); } const refreshArtifacts = async (container) => { const response = await fetch(container.dataset.artifactsUrl, { cache: "no-store" }); if (!response.ok) { return []; } const artifacts = (await response.json()).artifacts; const list = container.querySelector("ul"); list.replaceChildren(); for (const artifact of artifacts) { const item = document.createElement("li"); const link = document.createElement("a"); link.href = artifact.url; link.textContent = artifact.name; item.append(link); list.append(item); } container.hidden = artifacts.length === 0; return artifacts; }; for (const container of document.querySelectorAll("#run-artifacts")) { refreshArtifacts(container); window.setInterval(() => refreshArtifacts(container), 2000); } for (const form of document.querySelectorAll("form[data-wait-for-result]")) { const result = form.querySelector(".task-result"); const controls = [...form.querySelectorAll("input, select, button")]; let waiting = false; const warnBeforeLeaving = (event) => { event.preventDefault(); event.returnValue = ""; }; const setWaiting = (value) => { waiting = value; form.classList.toggle("is-waiting", value); controls.forEach((control) => { control.disabled = value; }); if (value) { window.addEventListener("beforeunload", warnBeforeLeaving); } else { window.removeEventListener("beforeunload", warnBeforeLeaving); } }; const showResult = (message, kind, detailUrl) => { result.replaceChildren(); result.className = `task-result ${kind}`; result.append(message); if (detailUrl) { const link = document.createElement("a"); link.href = detailUrl; link.textContent = "View details"; result.append(" ", link); } }; const downloadArtifacts = async (run) => { const response = await fetch(run.artifacts_url, { cache: "no-store" }); if (!response.ok) { return; } const artifacts = (await response.json()).artifacts; if (!artifacts.length) { return; } const link = document.createElement("a"); link.href = artifacts[0].url; link.download = ""; document.body.append(link); link.click(); link.remove(); }; const waitForRun = async (run) => { while (true) { const response = await fetch(run.state_url, { cache: "no-store" }); if (!response.ok) { throw new Error("Could not read task status."); } const state = await response.json(); if (state.status !== "queued" && state.status !== "running") { if (state.status === "succeeded") { showResult("Completed successfully.", "succeeded", run.detail_url); if (form.dataset.downloadArtifacts !== undefined) { await downloadArtifacts(run); } } else { showResult(`Finished with status: ${state.status}.`, "failed", run.detail_url); } return; } await new Promise((resolve) => window.setTimeout(resolve, 500)); } }; form.addEventListener("submit", async (event) => { if (waiting) { event.preventDefault(); return; } if (!form.reportValidity()) { return; } event.preventDefault(); // Disabled fields are omitted from FormData, so collect values first. const formData = new FormData(form); setWaiting(true); showResult("Running...", "running"); try { const response = await fetch(form.action, { method: "POST", body: formData, headers: { Accept: "application/json" }, }); const data = await response.json(); if (!response.ok) { const messages = data.errors ? Object.values(data.errors).join(" ") : "Could not start task."; throw new Error(messages); } await waitForRun(data); } catch (error) { showResult(error instanceof Error ? error.message : "Could not run task.", "failed"); } finally { setWaiting(false); } }); }