| 1 | import { asArray } from "./array"; |
| 2 | import { appendLivePageEntries } from "./transcriptLiveWindow"; |
| 3 | import type { PreparedTranscriptInstall, SessionTranscript, TranscriptProjection } from "./transcriptStoreTypes"; |
| 4 | import type { HistoryEntry, HistorySlice } from "./types"; |
| 5 | |
| 6 | /** Prepare a complete Follow replacement while preserving the resident object |
| 7 | * that owns in-flight content handoffs. Nothing becomes visible until commit. */ |
| 8 | export function prepareTranscriptInstall(previous: SessionTranscript | undefined, session: SessionTranscript, |
| 9 | slice: HistorySlice, windowPageEntries: number, |
| 10 | replaceRecords: (candidate: SessionTranscript, entries: HistoryEntry[]) => void, |
| 11 | projectionOf: (candidate: SessionTranscript) => TranscriptProjection, |
| 12 | install: (candidate: SessionTranscript) => void): PreparedTranscriptInstall { |
| 13 | session.generation = (previous?.generation ?? 0) + 1; |
| 14 | session.bindingKey = previous?.bindingKey; |
| 15 | session.canonicalV2 = true; |
| 16 | session.latestSequence = slice.revision; |
| 17 | const entries = asArray<HistoryEntry>(slice.entries); |
| 18 | replaceRecords(session, entries); |
| 19 | appendLivePageEntries(session.pages, entries.map(entry => entry.entryId), windowPageEntries); |
| 20 | if (session.pages.length) { |
| 21 | session.pages[0].olderCursor = slice.nextCursor ?? ""; |
| 22 | session.pages[session.pages.length - 1].newerCursor = slice.newerCursor ?? ""; |
| 23 | } |
| 24 | Object.assign(session, { |
| 25 | nextCursor: slice.nextCursor ?? "", newerCursor: slice.newerCursor ?? "", |
| 26 | hasOlder: Boolean(slice.hasOlder), hasNewer: Boolean(slice.hasNewer), |
| 27 | totalTurns: slice.totalTurns ?? 0, startTurn: slice.startTurn ?? 0, endTurn: slice.endTurn ?? 0, |
| 28 | revision: slice.revision ?? 0, revisionKnown: true, digest: slice.digest ?? "", |
| 29 | }); |
| 30 | const projection = projectionOf(session); |
| 31 | let committed = false; |
| 32 | return { projection, commit: () => { |
| 33 | if (committed) return; |
| 34 | committed = true; |
| 35 | if (!previous) return install(session); |
| 36 | const retained = { |
| 37 | pendingContent: previous.pendingContent, generationSettlement: previous.generationSettlement, |
| 38 | olderInFlight: previous.olderInFlight, newerInFlight: previous.newerInFlight, |
| 39 | reclaimedOlder: previous.reclaimedOlder, reclaimedNewer: previous.reclaimedNewer, |
| 40 | }; |
| 41 | Object.assign(previous, session, retained); |
| 42 | install(previous); |
| 43 | } }; |
| 44 | } |
| 45 |