mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
* feat(tables): add export, import column creation, infinite row pagination - Add `/api/table/[tableId]/export` route streaming CSV/JSON downloads - Rename `/import-csv` route to `/import` and extend to auto-create new columns from unmapped CSV headers via `createColumns` form field - Switch table view to `useInfiniteQuery` so tables larger than 1000 rows fully load; reconcile created rows into the paginated cache so "New row" past 1000 no longer reverts on invalidate - Wire scroll-driven prefetch (600px from bottom) and pre-drain pages before append to keep new-row position consistent - Polish import-csv dialog flow and add Export action to header Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * fix(table): route boundary validation through Zod contracts Switch the table query hooks and the import/export routes to the codebase's contract-based request pattern so the API validation audit and boundary policy ratchet pass. - `hooks/queries/tables.ts` now calls every endpoint via `requestJson(contract, ...)`; the only remaining raw `fetch` calls are the streaming export download and the multipart CSV upload, both annotated `boundary-raw-fetch:`. - The import and export routes parse `params`, the `format` query, and the multipart form fields with shared schemas from `@/lib/api/contracts/tables`. Two new contract schemas (`csvImportCreateColumnsSchema`, `tableExportFormatSchema`) cover the fields specific to these routes. - Bumps the audit baseline by one route to account for the new export endpoint. * fix(table): address PR bot review - Move the `isAppendingRowRef` reset into the create-row mutation's `onSettled` callback. The previous `try/finally` cleared the guard immediately after `mutate()` returned, before the request completed, so a rapid second click on "New row" could fire a duplicate create. - Drop the unused `addTableColumns` wrapper from `lib/table/service`. The CSV import flow only ever uses the transaction-bound `addTableColumnsWithTx`; the standalone wrapper was dead code. * fix(table): re-throw on infinite-query fetch error in append-row drain `useInfiniteQuery.fetchNextPage()` resolves (rather than rejects) when a page request fails — the resolved value carries `status: 'error'` while `hasNextPage` still reflects the last successful page. The drain loop in `handleAppendRow` relied on a thrown error to bail, so a failed mid-drain fetch could spin indefinitely and leave the append guard stuck on. Re-throw inside `fetchNextPageWrapped` when the result is an error so the caller's `try/catch` runs as intended. * fix(table): import TABLE_LIMITS from constants to keep server code out of client bundle The client hook `use-table-data.ts` was importing `TABLE_LIMITS` as a value from the `@/lib/table` barrel, which transitively pulls in `service.ts` and the `postgres` driver. Turbopack then tried to bundle `fs`, `net`, `tls`, and `perf_hooks` into the client component graph and the production build failed. Import `TABLE_LIMITS` directly from `@/lib/table/constants` (a pure constants module) and keep the type imports against the barrel. * fix(table): run batch unique check inside import transaction `checkBatchUniqueConstraintsDb` queried the global `db` connection, so inside a single import transaction (one tx wrapping all batches) the constraint lookup couldn't see uncommitted rows from prior batches — duplicates that crossed `CSV_MAX_BATCH_SIZE` boundaries slipped through. Accept an optional executor and pass `trx` from `batchInsertRowsWithTx` so the lookup observes the in-flight transaction state. --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>