export type ShaIngestError = { sha: string; error: string } export type IngestShasResult = { ingested: string[] alreadyStored: string[] errors: ShaIngestError[] remaining: number } /** * Ingest an explicit SHA list (from a push webhook) without letting one failed * commit fetch fail the whole delivery. Failed SHAs are reported in `errors`; * the `backfill-missing` job picks them up on its next tick. */ export async function ingestShaList( shas: string[], deps: { deadline: number hasCommit: (sha: string) => Promise ingest: (sha: string) => Promise now?: () => number warn?: (message: string) => void }, ): Promise { const now = deps.now ?? Date.now const warn = deps.warn ?? ((message: string) => console.warn(message)) const ingested: string[] = [] const alreadyStored: string[] = [] const errors: ShaIngestError[] = [] for (const sha of shas) { if (now() >= deps.deadline) break if (await deps.hasCommit(sha)) { alreadyStored.push(sha) continue } try { await deps.ingest(sha) ingested.push(sha) } catch (error) { const message = error instanceof Error ? error.message : String(error) errors.push({ sha, error: message }) warn(`lineage: deferred ${sha} to backfill-missing: ${message}`) } } return { ingested, alreadyStored, errors, remaining: shas.length - ingested.length - alreadyStored.length, } }