import { SampleGroup } from './components'; import { TreeNode } from './componentLabTypes'; export type OutlierAction = 'include' | 'exclude' | 'qc'; export type InputMode = 'paste' | 'csv' | 'experiment'; export interface OutlierInfo { sample: string; index: number; fieldCount: number; action: OutlierAction; } export interface MetadataRow { sampleName: string; fields: Record; group: string; } export interface ParsedCsvData { columns: string[]; rows: Record[]; sampleColumn: string; delimiter: string; } export type SampleClassKind = 'biological' | 'iqc' | 'eqc' | 'standard' | 'spike-in' | 'stock' | 'blank' | 'unknown'; export type ClassDisposition = 'group' | 'overlay' | 'exclude'; export interface SampleClass { kind: SampleClassKind; label: string; subKind?: string; members: number[]; classTagPositions: number[]; disposition: ClassDisposition; /** * Token count shared by every member of this class. Set by `splitByTokenLength` * after type classification so that samples with different field counts never * share a schema — column index N then means the same field for every member. */ tokenLength?: number; } export type ColumnRole = 'constant' | 'factor' | 'replicate' | 'run-order' | 'id' | 'numeric' | 'class-tag' | 'ignore'; export interface NumericParse { value: number; unit: string; raw: string; } export interface NumericBinning { mode: 'discrete' | 'binned'; edges?: number[]; labels?: string[]; } export interface ValueOps { alias?: Record; exclude?: string[]; } export interface ColumnInfo { index: number; name: string; sourceIndices: number[]; uniqueValues: string[]; cardinality: number; /** Legacy field, retained alongside `role` during the shim window. */ type?: 'prefix' | 'suffix'; role?: ColumnRole; numeric?: { byValue: Record; }; binning?: NumericBinning; ops?: ValueOps; /** Original CSV column header — used for row lookups when name is renamed */ originalName?: string; displayName?: string; } export interface ClassSchema { classKind: SampleClassKind; subKind?: string; /** Token count of the class this schema describes. Distinguishes schemas that * share a (kind, subKind) but were split apart by token-length. */ tokenLength?: number; columns: ColumnInfo[]; groupBy: number[]; replicateColumn?: number; } export interface MergeSuggestion { classKind: SampleClassKind; subKind?: string; columnIndices: number[]; proposedName: string; confidence: number; } export interface AutoGroupResult { /** All emitted groups, experimental + overlay concatenated for source compat. */ groups: SampleGroup[]; /** Class disposition === 'group' only. */ experimentalGroups?: SampleGroup[]; /** Class disposition === 'overlay' only. */ qcGroups?: SampleGroup[]; metadata: MetadataRow[]; excludedSamples: string[]; schemas?: ClassSchema[]; fingerprint?: SchemaFingerprint; /** * Experimental (disposition === 'group') classes as a nested hierarchy: * one root per class, then one level per enabled groupBy column, then the * sample leaves. Parent nodes carry a sample-count `badge`; leaves carry the * sample name plus the detected injection number (`metadata.injection` and as * the leaf badge). Renders directly in `SampleHierarchyTree`. */ groupTree?: TreeNode[]; } export interface SchemaFingerprint { version: 1; classes: Array<{ kind: SampleClassKind; subKind?: string; tokenLength?: number; columns: Array<{ name: string; role: ColumnRole; sourceIndices: number[]; binning?: NumericBinning; ops?: ValueOps; }>; groupBy: number[]; }>; }