- Add isBase to page repo baseFields so it is always selected
- Add isBase to sidebar pages query select list
- Add isBase to client IPage type
- In PageContent, render <BaseTable pageId={page.id} /> when page.isBase
is true instead of the TipTap editor path
Postgres has no per-expression stats for `cells->>'uuid' ILIKE '%…%'`,
`search_text ILIKE`, or `search_tsv @@`, so EXPLAIN Plan Rows falls
back to a default selectivity and is off by orders of magnitude — a
contains filter on a 10k-row base was reporting ~150 against thousands
of real matches. Auto-route any request whose filter tree contains a
contains/ncontains/startsWith/endsWith op or a search term to the
capped-exact path, even when the caller asked for an estimate.
Per-cell useResolvedPages([id]) calls each mounted with a unique
React Query key, so a grid with 20 page-typed cells fired 20 requests on
first paint. A shared loader now accumulates incoming ids within a
microtask, fires a single POST for the union, and fans the subset each
caller asked for back to them. Cells keep their own cache entry + null
handling; they just share the underlying network call.
Also renames /bases/pages/resolve → /bases/pages/expand — the old name
collided with other "resolve" semantics in the codebase.
Row-count display on a filtered view shouldn't force a full COUNT(*) on
every list fetch. New endpoint returns either an EXPLAIN-plan estimate
(default, ~1ms, no execution) or a LIMIT-capped exact count that short-
circuits to `{ capped: true }` once the match set passes EXACT_COUNT_CAP.
Clients call it in parallel with the rows query so the grid still paints
at its own pace.
- DTO + repo.countEstimate/countExact reusing the list predicate shape
- service picks the mode; controller mirrors the list Read ability check
- client hook keyed by filter/search/exact so a "show exact" toggle
doesn't clobber the estimate cache
The withProperties subquery hydrating /bases/info was missing a
`deleted_at IS NULL` filter, so after a delete the socket-echo
invalidation refetched and the just-deleted column rehydrated on the
originating client and never dropped on others.