import { browser } from "$app/environment"; import { untrack } from "svelte"; import { fetchCommitStatuses, type CommitStatuses } from "$lib/api/commitStatuses"; import { pollPipelineCollection } from "$lib/components/repo/pipelines/livePipelines"; import type { RepoInfo } from "$lib/components/repo/types"; interface CommitStatusTarget { repo: RepoInfo; shas: string[]; } const replaceStatuses = (current: CommitStatuses, updated: CommitStatuses) => { for (const sha of Object.keys(current)) { if (!(sha in updated)) delete current[sha]; } Object.assign(current, updated); }; export const liveCommitStatuses = ( target: () => Promise ): Promise => { if (!browser) return Promise.resolve({}); const current = $state({}); $effect(() => { const pendingTarget = target(); let stopped = false; let stopPolling = () => {}; untrack(() => replaceStatuses(current, {})); void pendingTarget .then(async (resolved) => { if (!resolved.repo.spindle || resolved.shas.length === 0) return; const initial = await fetchCommitStatuses( resolved.repo.ownerHandle, resolved.repo.name, resolved.shas ); if (stopped) return; replaceStatuses(current, initial); stopPolling = pollPipelineCollection( Object.values(initial), async (_pipelines, signal) => Object.values( await fetchCommitStatuses( resolved.repo.ownerHandle, resolved.repo.name, resolved.shas, signal ) ), (updated) => replaceStatuses( current, Object.fromEntries(updated.map((pipeline) => [pipeline.sha, pipeline])) ) ); }) .catch(() => {}); return () => { stopped = true; stopPolling(); }; }); return Promise.resolve(current); };