import type { UsageReport } from './product-usage'; export type ExperimentValues = Record; export type ExperimentDocument = { definition_version: string; version: number; at: string; actor: { id: number; name: string }; reason: string; values: ExperimentValues; indicator: { id: string; title: string; unit: string; source: string; limits: string; definition_version: string; } | null; plan_sha256: string; observation_plan_sha256: string | null; registration: { first_at: string | null; original_plan_sha256: string | null; amendments: number; amended_after_observation: boolean; }; operational_report: UsageReport | null; report_matches_observation: boolean | null; report_scope: string; }; export type ExperimentView = { installed: boolean; id: string | null; currentVersion: number; historical: boolean; document: ExperimentDocument | null; values: ExperimentValues; states: Record; changeKinds: Record; decisions: Record; labels: Record; catalogue: { definition_version: string; items: Omit< NonNullable, 'definition_version' >[]; services: { code: string; title: string; version: number }[]; }; history: { version: number; at: string; actor: string; state: string; reason: string; }[]; historyNext: number | null; }; export function experimentDifferences( base: ExperimentValues, mine: ExperimentValues, current: ExperimentValues, editedKeys?: readonly string[], ): string[] { return [ ...new Set([ ...Object.keys(base), ...Object.keys(mine), ...Object.keys(current), ]), ].filter( (key) => (base[key] ?? '') !== (current[key] ?? '') && (editedKeys ? editedKeys.includes(key) : (mine[key] ?? '') !== (base[key] ?? '')) && (mine[key] ?? '') !== (current[key] ?? ''), ); } /** Merge only changes the user did not edit; conflicts remain explicit. */ export function mergeExperimentValues( base: ExperimentValues, mine: ExperimentValues, current: ExperimentValues, editedKeys?: readonly string[], ): ExperimentValues { return Object.fromEntries( [ ...new Set([ ...Object.keys(base), ...Object.keys(mine), ...Object.keys(current), ]), ].map((key) => [ key, ( editedKeys ? !editedKeys.includes(key) : (mine[key] ?? '') === (base[key] ?? '') ) ? (current[key] ?? '') : (mine[key] ?? ''), ]), ); }