Defensive sidecar parse — mirror of databases.ts normalizeSidecar.
(raw: unknown)
| 663 | |
| 664 | /** Defensive sidecar parse — mirror of databases.ts normalizeSidecar. */ |
| 665 | function dbNormalizeSidecar(raw: unknown): DatabaseSidecar | null { |
| 666 | if (!raw || typeof raw !== 'object') return null |
| 667 | const obj = raw as Record<string, unknown> |
| 668 | const fields = Array.isArray(obj.fields) ? (obj.fields as DbField[]) : null |
| 669 | if (!fields || fields.length === 0) return null |
| 670 | if (!fields.every((f) => f && typeof f.id === 'string' && typeof f.name === 'string')) return null |
| 671 | const fieldIds = new Set(fields.map((f) => f.id)) |
| 672 | const idFieldId = |
| 673 | typeof obj.idFieldId === 'string' && fieldIds.has(obj.idFieldId) ? obj.idFieldId : fields[0].id |
| 674 | let views = Array.isArray(obj.views) ? (obj.views as DbView[]) : [] |
| 675 | views = views.filter( |
| 676 | (v) => v && typeof v.id === 'string' && (v.type === 'table' || v.type === 'board') |
| 677 | ) |
| 678 | if (views.length === 0) views = buildDefaultViews(fields).views |
| 679 | const activeViewId = |
| 680 | typeof obj.activeViewId === 'string' && views.some((v) => v.id === obj.activeViewId) |
| 681 | ? obj.activeViewId |
| 682 | : views[0].id |
| 683 | const pages = |
| 684 | obj.pages && typeof obj.pages === 'object' |
| 685 | ? (Object.fromEntries( |
| 686 | Object.entries(obj.pages as Record<string, unknown>).filter( |
| 687 | ([, v]) => typeof v === 'string' |
| 688 | ) |
| 689 | ) as Record<string, string>) |
| 690 | : undefined |
| 691 | return { version: 1, idFieldId, fields, views, activeViewId, ...(pages ? { pages } : {}) } |
| 692 | } |
| 693 | |
| 694 | function dbPagesToFull(csvRel: string, pages: Record<string, string>): Record<string, string> { |
| 695 | const formDir = formDirFromCsvPath(dbToPosix(csvRel)) |
no test coverage detected