table-metadata.ts1.9 KBView on GitHub /**
* Reading `TableDocumentMetadata` off a document row.
*
* `tableStatsHook` writes `{ kind: 'table', schemaVersion, columnCount, rowCount }` into
* `documents.metadata` on every save, precisely so list views, the file tree and
* `list-documents` can show "38 rows × 6 columns" WITHOUT loading content
* (apps/mail/docs/table-documents.md §3.2 step 13). Every client surface types that column
* as `unknown`, so the narrowing lives here rather than being re-guessed per call site.
*/
import type { TableDocumentMetadata } from '@zero/server/table';
export function readTableMetadata(metadata: unknown): TableDocumentMetadata | null {
if (typeof metadata !== 'object' || metadata === null) return null;
const candidate = metadata as { kind?: unknown; rowCount?: unknown; columnCount?: unknown };
if (candidate.kind !== 'table') return null;
if (typeof candidate.rowCount !== 'number' || typeof candidate.columnCount !== 'number') {
return null;
}
return metadata as TableDocumentMetadata;
}
/**
* "38 rows × 6 columns" — the one phrasing for a table's shape.
*
* Split out from `formatTableSummary` because the counts also arrive from places that hold no
* metadata object: `documents.importTable` returns them directly, and a size a user just
* created must read identically to the same size read back off the row later.
*/
export function formatRowsByColumns(rowCount: number, columnCount: number): string {
const rows = `${rowCount} ${rowCount === 1 ? 'row' : 'rows'}`;
const columns = `${columnCount} ${columnCount === 1 ? 'column' : 'columns'}`;
return `${rows} × ${columns}`;
}
/** The same, off a `documents.metadata`. Null when the row carries no table stats yet. */
export function formatTableSummary(metadata: unknown): string | null {
const stats = readTableMetadata(metadata);
if (!stats) return null;
return formatRowsByColumns(stats.rowCount, stats.columnCount);
}