import type { ClassSchema, SchemaFingerprint } from '../../types/auto-group' export function serializeFingerprint(schemas: ClassSchema[]): SchemaFingerprint { return { version: 1, classes: schemas.map(s => ({ kind: s.classKind, subKind: s.subKind, tokenLength: s.tokenLength, columns: s.columns.map(c => ({ name: c.displayName ?? c.name, role: c.role ?? 'factor', sourceIndices: c.sourceIndices, binning: c.binning, ops: c.ops, })), groupBy: s.groupBy, })), } } export function restoreFingerprint( fp: SchemaFingerprint, current: ClassSchema[], ): ClassSchema[] { return fp.classes.map(snap => { // Match on token length too when the snapshot carries one, so length-split // classes sharing a (kind, subKind) restore to the right schema. Snapshots // saved before token-length splitting have no `tokenLength`; for those we // match on (kind, subKind) and prefer the candidate with the same column // count, so an old single-class fingerprint lands on the compatible split // instead of throwing on the first (wrong-arity) hit. const candidates = current.filter( c => c.classKind === snap.kind && c.subKind === snap.subKind && (snap.tokenLength == null || c.tokenLength == null || c.tokenLength === snap.tokenLength), ) const target = candidates.find(c => c.columns.length === snap.columns.length) ?? candidates[0] if (!target) { throw new Error( `Fingerprint class not present in current input: ${snap.kind}${snap.subKind ? '/' + snap.subKind : ''}`, ) } if (target.columns.length !== snap.columns.length) { throw new Error( `Saved schema expects ${snap.columns.length} columns, current data has ${target.columns.length} for class ${snap.kind}${snap.subKind ? '/' + snap.subKind : ''}. column count mismatch`, ) } return { ...target, groupBy: snap.groupBy, columns: target.columns.map((col, i) => ({ ...col, displayName: snap.columns[i].name, role: snap.columns[i].role, binning: snap.columns[i].binning, ops: snap.columns[i].ops, })), } }) }