import { InvestmentResourceMetaDto } from './investment-resource.dto'; type LegacyAvailability = 'available' | 'complete' | 'connection-unavailable' | 'initial-loading' | 'missing' | 'partial' | 'temporarily-unavailable'; type Work = null | { errorCode: null | string; id: string; state: string }; type Attempt = NonNullable; export function investmentResourceMeta(input: { availability: LegacyAvailability; coverage?: NonNullable; dataRevision: string; dependencies?: InvestmentResourceMetaDto[]; fetchedAt: Date | null; freshForMs: number; lastAttempt?: Attempt; resourceKey: string; unavailableReason?: string; work: Work; }): InvestmentResourceMetaDto { const ownSync = sourceSync(input.work); const syncStates = [ ownSync, ...(input.dependencies ?? []) .filter((dependency) => !dependency.unavailableReason) .map((dependency) => dependency.sync) .filter((state): state is typeof ownSync => Boolean(state)), ]; const sourceWork = syncStates.find((state) => state.state === 'running') ?? syncStates.find((state) => state.state === 'failed') ?? ownSync; const sourceFreshness = freshnessOf(input.fetchedAt, input.freshForMs); const freshness = [ sourceFreshness, ...(input.dependencies ?? []) .filter((dependency) => !dependency.unavailableReason) .map((dependency) => dependency.freshness), ]; const timestamps = [ input.fetchedAt?.toISOString(), ...(input.dependencies ?? []).map( (dependency) => dependency.lastSuccessfulAt, ), ] .filter((timestamp): timestamp is string => Boolean(timestamp)) // eslint-disable-next-line sonarjs/no-alphabetical-sort -- Code-unit ordering is intentional for canonical keys and ISO dates; locale collation would change persisted bindings. .sort(); const meta = { resourceKey: input.resourceKey, ...(input.unavailableReason ? { unavailableReason: input.unavailableReason } : {}), dataRevision: input.dataRevision, availability: availability(input.availability, input.work), completeness: availability(input.availability, input.work) === 'available' ? ('complete' as const) : ('incomplete' as const), freshness: combinedFreshness(freshness), lastSuccessfulAt: timestamps[0] ?? null, lastAttempt: input.lastAttempt ?? attempt(input.work), ...(input.coverage ? { coverage: input.coverage } : {}), ...(input.dependencies ? { dependencies: input.dependencies } : {}), }; return Object.defineProperty(meta, 'sync', { enumerable: false, value: sourceWork, }) as InvestmentResourceMetaDto; } function sourceSync(work: Work) { let state: 'failed' | 'idle' | 'running' = 'idle'; if (work?.state === 'running') state = 'running'; else if (work?.state === 'failed') state = 'failed'; return { id: work?.id ?? null, state, errorCode: work?.errorCode ?? null }; } function freshnessOf(fetchedAt: Date | null, freshForMs: number) { if (!fetchedAt) return 'unknown' as const; return Date.now() - fetchedAt.valueOf() < freshForMs ? ('fresh' as const) : ('stale' as const); } function combinedFreshness(values: InvestmentResourceMetaDto['freshness'][]) { if (values.includes('unknown')) return 'unknown' as const; if (values.includes('stale')) return 'stale' as const; return 'fresh' as const; } function attempt(work: Work): Attempt { const base = { startedAt: null, finishedAt: null }; if (work?.state === 'running') return { ...base, state: 'running', problems: [] }; if (work?.state === 'failed') { return { ...base, state: 'failed', problems: work.errorCode ? [{ section: 'membership', accountId: null, errorCode: work.errorCode }] : [], }; } return { ...base, state: 'none', problems: [] }; } function availability(value: LegacyAvailability, work: Work) { if ( value === 'available' || value === 'connection-unavailable' || value === 'initial-loading' || value === 'temporarily-unavailable' ) { return value; } if (value === 'complete') return 'available' as const; return work?.state === 'running' ? ('initial-loading' as const) : ('temporarily-unavailable' as const); }