export type SourceCredit = { title: string; license: string; uri?: string; attribution?: string; licenseUrl?: string; retrievedAt?: string; originSha?: string; modificationNotice?: string; }; const LICENSE_URLS: Record = { "CC0-1.0": "https://creativecommons.org/publicdomain/zero/1.0/", "CC-BY-4.0": "https://creativecommons.org/licenses/by/4.0/", "CC-BY-NC-4.0": "https://creativecommons.org/licenses/by-nc/4.0/", MIT: "https://opensource.org/license/mit", }; const ATTRIBUTION_LICENSES = new Set(["CC-BY-4.0", "CC-BY-NC-4.0", "MIT"]); export function uniqueOriginNotices(sources: SourceCredit[]): SourceCredit[] { const seen = new Set(); const notices: SourceCredit[] = []; for (const source of sources) { if (!source.attribution?.trim()) continue; const key = [source.attribution, source.license, source.licenseUrl ?? "", source.originSha ?? "", source.modificationNotice ?? ""].join("\n"); if (seen.has(key)) continue; seen.add(key); notices.push(source); } return notices; } export function attributionGaps(source: SourceCredit): string[] { const gaps: string[] = []; if (!source.license) gaps.push("license"); if (!source.title.trim()) gaps.push("title"); if (!source.uri?.trim()) gaps.push("uri"); if (ATTRIBUTION_LICENSES.has(source.license) && !source.attribution?.trim()) gaps.push("attribution"); if (source.license === "CC-BY-4.0" || source.license === "CC-BY-NC-4.0") { if (source.licenseUrl !== LICENSE_URLS[source.license]) gaps.push("licenseUrl"); if (!source.retrievedAt?.trim()) gaps.push("retrievedAt"); if (!source.originSha?.trim()) gaps.push("originSha"); if (!source.modificationNotice?.trim()) gaps.push("modificationNotice"); } return gaps; }