( value: unknown, colType: CsvColumnType )
| 217 | * reject it with context rather than silently inserting `null`. |
| 218 | */ |
| 219 | export function coerceValue( |
| 220 | value: unknown, |
| 221 | colType: CsvColumnType |
| 222 | ): string | number | boolean | null | Record<string, unknown> | unknown[] { |
| 223 | if (value === null || value === undefined || value === '') return null |
| 224 | switch (colType) { |
| 225 | case 'number': { |
| 226 | const n = Number(value) |
| 227 | return Number.isNaN(n) ? null : n |
| 228 | } |
| 229 | case 'boolean': { |
| 230 | const s = String(value).toLowerCase() |
| 231 | if (s === 'true') return true |
| 232 | if (s === 'false') return false |
| 233 | return null |
| 234 | } |
| 235 | case 'date': { |
| 236 | const d = new Date(String(value)) |
| 237 | return Number.isNaN(d.getTime()) ? String(value) : d.toISOString() |
| 238 | } |
| 239 | case 'json': { |
| 240 | if (typeof value === 'object') return value as Record<string, unknown> | unknown[] |
| 241 | try { |
| 242 | return JSON.parse(String(value)) |
| 243 | } catch { |
| 244 | return String(value) |
| 245 | } |
| 246 | } |
| 247 | default: |
| 248 | return String(value) |
| 249 | } |
| 250 | } |
| 251 | |
| 252 | /** |
| 253 | * Mapping from raw CSV header to target column name, with `null` indicating |
no test coverage detected