/** Internal select value when respondent picks the configurable Other option. Never stored in submissions. */ export const SELECT_OTHER_VALUE = '__other__'; export type SelectOptionItem = string | { value: string; label: string }; export function normalizeSelectOptions(options: SelectOptionItem[] | undefined): { value: string; label: string }[] { return (options ?? []).map((o) => (typeof o === 'string' ? { value: o, label: o } : o)); } export function isCustomSelectValue( value: unknown, optionValues: string[], allowOther?: boolean, ): boolean { if (!allowOther) return false; const stored = String(value ?? '').trim(); if (!stored) return false; return !optionValues.includes(stored); } export function selectControlValue( value: unknown, optionValues: string[], allowOther?: boolean, otherValue = SELECT_OTHER_VALUE, ): string { const stored = String(value ?? ''); if (isCustomSelectValue(stored, optionValues, allowOther)) return otherValue; return stored; } export function otherTextFromValue( value: unknown, optionValues: string[], allowOther?: boolean, ): string { if (!isCustomSelectValue(value, optionValues, allowOther)) return ''; return String(value ?? ''); }