import { accountHistory } from '../../investment-sync-plan'; import { confirmedOperationRanges, type OperationCoverage, type OperationInterval, operationIntervalsOverlap, operationsSyncPriority, planOperationRead, uncoveredOperationIntervals } from './operations-sync-plan'; const UNKNOWN_AS_OF_MS = 0; type WorkState = { errorCode: null | string; id: string; intervals: OperationInterval[]; priority: number; state: string; }; type CoverageAccount = { accountId: string; openedOn: null | string }; // Pure derivation of the ledger read view: what was requested, what is // confirmed, what is missing, and which sync work is currently relevant. export function deriveReadCoverage(input: { accounts: CoverageAccount[]; connectionMembershipAt: Date | null; coverage: OperationCoverage[]; from: Date; instrumentUid?: string; movingEnd: boolean; now: Date; to: Date; work: WorkState[]; }) { const targets = input.accounts.flatMap((account) => input.from < input.to ? [ { accountId: account.accountId, from: input.from, to: input.to, instrumentUid: input.instrumentUid, }, ] : [], ); const plan = planOperationRead(targets, input.coverage, input.now, { movingEnd: input.movingEnd, }); const confirmed = confirmedOperationRanges(plan.intervals, input.coverage); const missing = uncoveredOperationIntervals( plan.intervals, confirmed.map((range) => ({ ...range, instrumentUid: input.instrumentUid, })), ); const available = Boolean(input.connectionMembershipAt); const complete = available && !missing.length; const relevant = input.work.filter((work) => work.intervals.some((interval) => targets.some((target) => operationIntervalsOverlap(interval, target)), ), ); const work = relevant.find((work) => work.state === 'running') ?? relevant.find((work) => work.state === 'failed') ?? null; const sources = input.coverage.filter((row) => targets.some( (target) => operationIntervalsOverlap(row, target) && (!row.instrumentUid || row.instrumentUid === target.instrumentUid), ), ); const lastSuccessfulAt = sources .map((row) => row.fetchedAt) .sort((a, b) => a.valueOf() - b.valueOf())[0] ?? (complete ? input.connectionMembershipAt : null) ?? null; const history = planOperationRead( accountHistory(input.accounts, input.now), input.coverage, input.now, { movingEnd: true }, ); const knownBoundary = input.accounts.every( (account) => account.openedOn !== null, ); return { targets, plan, confirmed, missing, available, complete, work, lastSuccessfulAt, history, knownBoundary, }; } export function coverageAsOf( plan: ReturnType, accountIds: string[], ): null | string { if ( accountIds.length && accountIds.every((accountId) => plan.intervals.some( (interval) => interval.accountId === accountId && interval.asOf, ), ) ) { return new Date( Math.min( ...accountIds.map((accountId) => Math.max( ...plan.intervals .filter((interval) => interval.accountId === accountId) .map((interval) => interval.asOf?.valueOf() ?? UNKNOWN_AS_OF_MS), ), ), ), ).toISOString(); } return null; } export function historyState( knownBoundary: boolean, history: ReturnType, work: WorkState[], coverageCount: number, ): 'complete' | 'failed' | 'loading' | 'missing' | 'partial' { if ( work.some( (work) => work.state === 'running' && work.priority > operationsSyncPriority.screen, ) ) { return 'loading'; } if ( work.some( (work) => work.state === 'failed' && work.priority > operationsSyncPriority.screen, ) ) { return 'failed'; } if ( knownBoundary && history.intervals.every( (interval) => interval.availability === 'complete', ) ) { return 'complete'; } return coverageCount ? 'partial' : 'missing'; } export function serializeRange(range: { accountId: string; from: Date; to: Date }) { return { accountId: range.accountId, from: range.from.toISOString(), to: range.to.toISOString() }; }