import type { Bundle, BundleEntry, Resource } from 'fhir/r4'; export type VersionSelection = | { kind: 'selected'; resource: T; versionId?: string; lastUpdated?: string } | { kind: 'omit'; reason: 'not-existent-at-t' | 'deleted-at-t' } | { kind: 'truncated' }; // Timestamp of a history entry: a normal version carries meta.lastUpdated on its resource; // a deletion tombstone has no resource - fall back to the response.lastModified. function entryInstant(entry: BundleEntry): number | undefined { const iso = entry.resource?.meta?.lastUpdated ?? entry.response?.lastModified; if (!iso) return undefined; const t = Date.parse(iso); return Number.isNaN(t) ? undefined : t; } function isDeletion(entry: BundleEntry): boolean { return entry.request?.method === 'DELETE'; } /** Same relation getMergedHistory adds when it strips unfollowable `next` at the 50-page cap. */ const HISTORY_TRUNCATED_RELATION = 'x-akashic-history-truncated'; /** Same relation getMergedHistory adds when the listing failed and versions were read one by one from v1 upward. */ const HISTORY_REBUILT_RELATION = 'x-akashic-history-rebuilt-by-vread'; function hasOlderHistoryPages(history: Bundle): boolean { return (history.link ?? []).some( (l) => l.relation === 'next' || l.relation === HISTORY_TRUNCATED_RELATION, ); } /** * Select the version of a resource that was live at `asOfIso`, from its FHIR `_history` Bundle. * Rule: among entries with instant <= asOf, pick the one with the greatest instant. * - none <= asOf and an older page exists (link[next] or the merged-history cap relation) -> 'truncated' * - none <= asOf and no more pages -> omit 'not-existent-at-t' * - the chosen entry is a deletion tombstone -> omit 'deleted-at-t' * - a history rebuilt version by version, an entry with no time, or one with neither a body nor a deletion cannot * prove which version stood at T -> 'truncated' */ export function selectVersionAsOf(history: Bundle, asOfIso: string): VersionSelection { const asOf = Date.parse(asOfIso); const entries = history.entry ?? []; // A rebuilt history is the oldest versions that happened to be readable, so a newer one may be missing. if ((history.link ?? []).some((l) => l.relation === HISTORY_REBUILT_RELATION)) return { kind: 'truncated' }; let best: BundleEntry | undefined; let bestInstant = -Infinity; for (const entry of entries) { const instant = entryInstant(entry); if (instant === undefined || (!entry.resource && !isDeletion(entry))) return { kind: 'truncated' }; if (instant > asOf) continue; if (instant > bestInstant) { best = entry; bestInstant = instant; } } if (!best) { return hasOlderHistoryPages(history) ? { kind: 'truncated' } : { kind: 'omit', reason: 'not-existent-at-t' }; } if (isDeletion(best)) { return { kind: 'omit', reason: 'deleted-at-t' }; } return { kind: 'selected', resource: best.resource as T, versionId: best.resource?.meta?.versionId, lastUpdated: best.resource?.meta?.lastUpdated, }; }