feat: Export Configuration Overhaul & Build Stability Fixes#21
feat: Export Configuration Overhaul & Build Stability Fixes#21jacksonkasi1 merged 12 commits intomainfrom
Conversation
…imized cross-page export
|
Deployment failed with the following error: Learn More: https://vercel.com/jacksonkasi1s-projects?upgradeToPro=build-rate-limit |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdapters and types extended to support sorted bulk-ID queries (sortBy, sortOrder). TableCraft adapter gains idField and a queryByIds method. DataTable now uses adapter.queryByIds for cross-page selection retrieval. ExportConfig typing and defineExportConfig helper added, with docs and example. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as DataTable (UI)
participant Adapter as DataAdapter (TableCraft / REST)
participant API as Remote API
UI->>Adapter: queryByIds(selectedIds, { sortBy, sortOrder })
Adapter->>API: GET /items?filter=id_in(selectedIds)&pageSize=ids.length&sort=...
API-->>Adapter: { data: [...] }
Adapter-->>UI: return data[] (ordered per sort)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Reviewer's GuideAdds a sortable queryByIds API to table adapters and updates DataTable’s cross-page export to use it so that exported rows match the current table sort order, along with minor typing and formatting tweaks. Sequence diagram for cross page export using sortable queryByIdssequenceDiagram
actor User
participant DataTable
participant DataAdapter
participant TableCraftAdapter
participant Backend
User->>DataTable: triggerExport()
DataTable->>DataTable: computeSelectedIdsAndSort(sortBy, sortOrder)
DataTable->>DataAdapter: queryByIds(selectedIds, { sortBy, sortOrder })
activate DataAdapter
DataAdapter->>TableCraftAdapter: queryByIds(selectedIds, { sortBy, sortOrder })
activate TableCraftAdapter
TableCraftAdapter->>TableCraftAdapter: buildQueryParams(ids, sortBy, sortOrder, idField)
TableCraftAdapter->>Backend: GET /table?filters[id].operator=in&filters[id].value=ids&sort=sortBy&sortOrder=sortOrder
activate Backend
Backend-->>TableCraftAdapter: sortedRowsById
deactivate Backend
TableCraftAdapter-->>DataAdapter: sortedRowsById
deactivate TableCraftAdapter
DataAdapter-->>DataTable: sortedRowsById
deactivate DataAdapter
DataTable->>DataTable: useFetchedRowsForExport(sortedRowsById)
DataTable-->>User: downloadExportedFile
Class diagram for updated table adapters and DataAdapter interfaceclassDiagram
class QueryParams {
}
class QueryResult_T_ {
}
class TableMetadata {
}
class DataAdapter_T_ {
<<interface>>
+query(params QueryParams) Promise~QueryResult_T_~
+queryByIds(ids Array~string_or_number~, options QueryByIdsOptions) Promise~Array~T~~
+meta() Promise~TableMetadata~
+export(format string, params QueryParams) Promise~string~
}
class QueryByIdsOptions {
+sortBy string
+sortOrder string
}
class TableCraftAdapterOptions_TFilters_ {
+baseUrl string
+table string
+idField string
+headers Record~string_string~
+fetch(url string, options RequestInit) Promise~Response~
+axios any
}
class RestAdapterOptions_T_ {
+queryFn(params QueryParams) Promise~QueryResult_T_~
+queryByIdsFn(ids Array~string_or_number~, options QueryByIdsOptions) Promise~Array~T~~
+metaFn() Promise~TableMetadata~
}
class TableCraftAdapter_T_ {
+query(params QueryParams) Promise~QueryResult_T_~
+queryByIds(ids Array~string_or_number~, options QueryByIdsOptions) Promise~Array~T~~
+meta() Promise~TableMetadata~
+export(format string, params QueryParams) Promise~string~
}
class DataTable_T_ {
+getSelectedItemsForExport() Array~T~
}
DataAdapter_T_ <|.. TableCraftAdapter_T_
TableCraftAdapterOptions_TFilters_ --> TableCraftAdapter_T_ : configures
RestAdapterOptions_T_ --> DataAdapter_T_ : used_to_build
QueryByIdsOptions --> DataAdapter_T_ : parameter
QueryByIdsOptions --> TableCraftAdapter_T_ : parameter
DataTable_T_ --> DataAdapter_T_ : uses_queryByIds_for_export
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've left some high level feedback:
- In
TableCraftAdapter.queryByIds, consider omitting unused query params (emptysearchand blankdateRange) rather than sending empty strings, to avoid subtle differences in backend behavior compared to the normalquerypath. - The
queryByIdsimplementation currently setspageSizetoids.length; if very large selections are expected, it may be safer to either chunk requests or enforce a reasonable upper bound to avoid oversized single queries.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `TableCraftAdapter.queryByIds`, consider omitting unused query params (empty `search` and blank `dateRange`) rather than sending empty strings, to avoid subtle differences in backend behavior compared to the normal `query` path.
- The `queryByIds` implementation currently sets `pageSize` to `ids.length`; if very large selections are expected, it may be safer to either chunk requests or enforce a reasonable upper bound to avoid oversized single queries.Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/table/src/auto/tablecraft-adapter.ts`:
- Around line 282-312: queryByIds currently sets pageSize = ids.length and
builds a GET URL with all IDs which can exceed backend/URL limits; change
queryByIds (and use getMetadataWithFallback) to read
metadata.capabilities.pagination.maxPageSize (use a sensible fallback if
missing), split the ids array into chunks of at most that maxPageSize, perform a
request per chunk (reusing applyCustomFilters/buildQueryUrl and the existing
request call with filters: { [idKey]: { operator: "in", value: chunk } }),
concatenate all returned result.data arrays, and if sortOptions?.sortBy is
provided perform a client-side sort on the combined results using
sortOptions.sortBy and sortOptions.sortOrder before returning. Ensure the idKey,
getMetadataWithFallback, applyCustomFilters, buildQueryUrl, and request usage
remain consistent.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
packages/table/src/auto/rest-adapter.tspackages/table/src/auto/tablecraft-adapter.tspackages/table/src/data-table.tsxpackages/table/src/types.ts
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/table/src/export.tsx (1)
84-100:⚠️ Potential issue | 🟡 MinorMerge partial
columnMappingwith defaults to avoid blank headers.
columnMappingis nowPartial<Record<...>>, but the current logic treats it as complete; missing keys will produce undefined headers. Suggest generating a default mapping and layering user overrides on top.✅ Suggested fix
- const exportColumnMapping: Record<string, string> = - (exportConfig?.columnMapping as Record<string, string>) || - (() => { - const mapping: Record<string, string> = {}; - orderedColumns.forEach((col) => { - const headerText = col.columnDef.header as string; - if (headerText && typeof headerText === "string") { - mapping[col.id] = headerText; - } else { - mapping[col.id] = col.id - .split(/(?=[A-Z])|_/) - .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()) - .join(" "); - } - }); - return mapping; - })(); + const baseMapping = (() => { + const mapping: Record<string, string> = {}; + orderedColumns.forEach((col) => { + const headerText = col.columnDef.header as string; + if (headerText && typeof headerText === "string") { + mapping[col.id] = headerText; + } else { + mapping[col.id] = col.id + .split(/(?=[A-Z])|_/) + .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()) + .join(" "); + } + }); + return mapping; + })(); + const exportColumnMapping: Record<string, string> = { + ...baseMapping, + ...(exportConfig?.columnMapping ?? {}), + };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/table/src/export.tsx` around lines 84 - 100, The exportColumnMapping logic treats exportConfig.columnMapping as complete and can leave headers undefined; change it to first build the default mapping from orderedColumns (the existing mapping logic in the IIFE that uses orderedColumns, col.columnDef.header and fallback formatting) and then merge the user-provided mapping (exportConfig.columnMapping typed as Partial<Record<string,string>>) on top so user values override defaults (use Object.assign or spread to combine defaultMapping with exportConfig.columnMapping when constructing exportColumnMapping). Ensure exportColumnMapping remains a Record<string,string> by filling any missing keys with the defaults from orderedColumns.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@packages/table/src/export.tsx`:
- Around line 84-100: The exportColumnMapping logic treats
exportConfig.columnMapping as complete and can leave headers undefined; change
it to first build the default mapping from orderedColumns (the existing mapping
logic in the IIFE that uses orderedColumns, col.columnDef.header and fallback
formatting) and then merge the user-provided mapping (exportConfig.columnMapping
typed as Partial<Record<string,string>>) on top so user values override defaults
(use Object.assign or spread to combine defaultMapping with
exportConfig.columnMapping when constructing exportColumnMapping). Ensure
exportColumnMapping remains a Record<string,string> by filling any missing keys
with the defaults from orderedColumns.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
apps/vite-web-example/src/pages/orders-page.tsxdocs/SUMMARY.mddocs/export-config.mdpackages/table/src/data-table.tsxpackages/table/src/export.tsxpackages/table/src/index.tspackages/table/src/toolbar.tsxpackages/table/src/types.ts
✅ Files skipped from review due to trivial changes (1)
- docs/export-config.md
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/table/src/data-table.tsx
|
Deployment failed with the following error: Learn More: https://vercel.com/jacksonkasi1s-projects?upgradeToPro=build-rate-limit |
…nd `package.json`.
…ig inference chore(release): publish @tablecraft/table@0.2.12
…ng Record<string, unknown> chore(release): publish @tablecraft/table@0.2.14
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/vite-web-example/package.json`:
- Line 16: The dependency version for `@tablecraft/table` in the app is stale (app
lists ^0.2.11 while the workspace package is 0.2.14); regenerate the Bun
lockfile and align the dependency: run bun install (or bun generate-lockfile) at
repo root to refresh bun.lock so it resolves to 0.2.14, and if desired update
the app's package.json entry for `@tablecraft/table` to ^0.2.14 so sources and
lockfile match consistently.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (3)
apps/vite-web-example/package.jsonpackages/table/package.jsonpackages/table/src/types.ts
✅ Files skipped from review due to trivial changes (1)
- packages/table/package.json
chore(release): publish @tablecraft/table@0.2.15
|
Deployment failed with the following error: Learn More: https://vercel.com/jacksonkasi1s-projects?upgradeToPro=build-rate-limit |
…e builds chore(release): publish @tablecraft/table@0.2.16
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/table/src/export.tsx (1)
62-78:⚠️ Potential issue | 🟠 Major
columnMappingnow completely replaces auto-generated header names — likely a regression.When
exportConfig.columnMappingis provided (even a single-entry object), the||short-circuit skips the IIFE that generates human-readable headers fromcol.columnDef.header. Columns not incolumnMappingwill export with their raw accessor IDs (e.g.statusLabel,vatAmount) instead of the formatted names that TanStack Table column definitions carry.The example in
orders-page.tsxmaps only 3 of ~10 exported columns; all others would silently export with raw IDs.The fix is to seed from auto-detected headers and then overlay user overrides:
🐛 Proposed fix
- const exportColumnMapping: Record<string, string> = - (exportConfig?.columnMapping as Record<string, string>) || - (() => { - const mapping: Record<string, string> = {}; - orderedColumns.forEach((col) => { - const headerText = col.columnDef.header as string; - if (headerText && typeof headerText === "string") { - mapping[col.id] = headerText; - } else { - mapping[col.id] = col.id - .split(/(?=[A-Z])|_/) - .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()) - .join(" "); - } - }); - return mapping; - })(); + const autoMapping: Record<string, string> = {}; + orderedColumns.forEach((col) => { + const headerText = col.columnDef.header as string; + if (headerText && typeof headerText === "string") { + autoMapping[col.id] = headerText; + } else { + autoMapping[col.id] = col.id + .split(/(?=[A-Z])|_/) + .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()) + .join(" "); + } + }); + const exportColumnMapping: Record<string, string> = { + ...autoMapping, + ...(exportConfig?.columnMapping as Record<string, string> ?? {}), + };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/table/src/export.tsx` around lines 62 - 78, The current logic sets exportColumnMapping to exportConfig.columnMapping or else an auto-generated mapping, which causes a regression where a partial user-provided mapping replaces all auto-detected headers; update exportColumnMapping so you first build the default mapping from orderedColumns (using col.columnDef.header and col.id fallback logic) and then shallow-merge/overlay exportConfig.columnMapping on top (so keys in exportConfig override defaults but unspecified columns keep the generated friendly names); refer to exportColumnMapping, exportConfig.columnMapping, orderedColumns, col.columnDef.header and col.id to locate and implement the merge.
🧹 Nitpick comments (4)
packages/table/src/data-table.tsx (1)
648-648: Consider a top-level import alias instead of inlineimport()in JSX.
import("./types").ExportConfig<ExportableData>is valid TypeScript but the inline dynamic-import-style type path is unusual in JSX attribute positions and can trip up some editors and linters. SinceExportConfigis already imported at Line 24 (viaDataTableProps), you can pull it in explicitly and alias it:♻️ Proposed refactor
At the top of the file, add
ExportConfigto the existing type import:-import type { DataTableProps, ExportableData, TableContext } from "./types"; +import type { DataTableProps, ExportableData, ExportConfig, TableContext } from "./types";Then replace the inline cast:
- exportConfig={exportConfig as unknown as import("./types").ExportConfig<ExportableData>} + exportConfig={exportConfig as unknown as ExportConfig<ExportableData>}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/table/src/data-table.tsx` at line 648, Replace the inline dynamic type cast used in the JSX prop (exportConfig={exportConfig as unknown as import("./types").ExportConfig<ExportableData>}) with a top-level named type import: add ExportConfig to the existing type import where DataTableProps is imported (line with DataTableProps) and then cast exportConfig to that imported ExportConfig<ExportableData> type in the JSX; update the JSX attribute to use the named ExportConfig type instead of the inline import() expression so editors and linters handle it cleanly.apps/vite-web-example/src/pages/orders-page.tsx (1)
17-23:Number(row.total)is redundant —totalis already typed asnumber.
OrdersRow.total: number, so the explicitNumber()conversion is unnecessary.♻️ Proposed fix
- total: `₹${Number(row.total).toFixed(2)}`, + total: `₹${row.total.toFixed(2)}`,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/vite-web-example/src/pages/orders-page.tsx` around lines 17 - 23, The transformFunction is unnecessarily wrapping row.total with Number(...) even though OrdersRow.total is already a number; update the transformFunction to format total using row.total.toFixed(2) (inside the template string `₹${...}`) and remove the Number(...) conversion so the code reads `total: \`₹${row.total.toFixed(2)}\``; keep the existing createdAt handling and ensure you reference transformFunction and OrdersRow.total when making the change.packages/table/package.json (1)
21-22:typecheckscript is inconsistent with the updatedbuild/devscripts.
buildanddevnow use plaintsc, buttypecheckat Line 24 still usesbun run --bun tsc --noEmit. Align for consistency:♻️ Proposed fix
- "typecheck": "bun run --bun tsc --noEmit" + "typecheck": "tsc --noEmit"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/table/package.json` around lines 21 - 22, The package.json typecheck script is inconsistent with build/dev; change the "typecheck" script to use the same plain tsc invocation as "build" and "dev" but with --noEmit (e.g., replace the current "bun run --bun tsc --noEmit" value with "tsc --noEmit") so all scripts consistently call tsc; update the "typecheck" entry in package.json accordingly.packages/table/src/export.tsx (1)
80-84: Use a local variable to avoid the non-null assertion.
exportConfig.columnWidths!is safe because we're inside the truthy branch ofexportConfig?.columnWidths, but a local variable is clearer:♻️ Proposed refactor
- const exportColumnWidths = exportConfig?.columnWidths - ? exportHeaders.map( - (_, i) => exportConfig.columnWidths![i] || { wch: 15 } - ) - : exportHeaders.map(() => ({ wch: 15 })); + const userWidths = exportConfig?.columnWidths; + const exportColumnWidths = userWidths + ? exportHeaders.map((_, i) => userWidths[i] || { wch: 15 }) + : exportHeaders.map(() => ({ wch: 15 }));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/table/src/export.tsx` around lines 80 - 84, The code uses a non-null assertion exportConfig.columnWidths! inside the exportColumnWidths computation; extract exportConfig.columnWidths into a local const (e.g., const columnWidths = exportConfig?.columnWidths) within the same scope and then use columnWidths[i] when mapping exportHeaders to avoid the bang and make the intent clearer; update the conditional to check columnWidths truthiness (or reuse the existing exportConfig?.columnWidths check) and fall back to { wch: 15 } when columnWidths[i] is undefined, keeping the variable name exportColumnWidths and referencing exportHeaders as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/vite-web-example/request.json`:
- Around line 1-44: This JSON payload (the accidental AI API request containing
top-level keys "contents", "generationConfig", and "safetySettings") should be
removed from the commit and repository; delete the file, remove it from git
history/index (e.g., git rm or git rm --cached and commit) so it isn’t picked up
by CI, and add an appropriate ignore rule to .gitignore to prevent future
accidental commits of similar test payloads.
---
Outside diff comments:
In `@packages/table/src/export.tsx`:
- Around line 62-78: The current logic sets exportColumnMapping to
exportConfig.columnMapping or else an auto-generated mapping, which causes a
regression where a partial user-provided mapping replaces all auto-detected
headers; update exportColumnMapping so you first build the default mapping from
orderedColumns (using col.columnDef.header and col.id fallback logic) and then
shallow-merge/overlay exportConfig.columnMapping on top (so keys in exportConfig
override defaults but unspecified columns keep the generated friendly names);
refer to exportColumnMapping, exportConfig.columnMapping, orderedColumns,
col.columnDef.header and col.id to locate and implement the merge.
---
Nitpick comments:
In `@apps/vite-web-example/src/pages/orders-page.tsx`:
- Around line 17-23: The transformFunction is unnecessarily wrapping row.total
with Number(...) even though OrdersRow.total is already a number; update the
transformFunction to format total using row.total.toFixed(2) (inside the
template string `₹${...}`) and remove the Number(...) conversion so the code
reads `total: \`₹${row.total.toFixed(2)}\``; keep the existing createdAt
handling and ensure you reference transformFunction and OrdersRow.total when
making the change.
In `@packages/table/package.json`:
- Around line 21-22: The package.json typecheck script is inconsistent with
build/dev; change the "typecheck" script to use the same plain tsc invocation as
"build" and "dev" but with --noEmit (e.g., replace the current "bun run --bun
tsc --noEmit" value with "tsc --noEmit") so all scripts consistently call tsc;
update the "typecheck" entry in package.json accordingly.
In `@packages/table/src/data-table.tsx`:
- Line 648: Replace the inline dynamic type cast used in the JSX prop
(exportConfig={exportConfig as unknown as
import("./types").ExportConfig<ExportableData>}) with a top-level named type
import: add ExportConfig to the existing type import where DataTableProps is
imported (line with DataTableProps) and then cast exportConfig to that imported
ExportConfig<ExportableData> type in the JSX; update the JSX attribute to use
the named ExportConfig type instead of the inline import() expression so editors
and linters handle it cleanly.
In `@packages/table/src/export.tsx`:
- Around line 80-84: The code uses a non-null assertion
exportConfig.columnWidths! inside the exportColumnWidths computation; extract
exportConfig.columnWidths into a local const (e.g., const columnWidths =
exportConfig?.columnWidths) within the same scope and then use columnWidths[i]
when mapping exportHeaders to avoid the bang and make the intent clearer; update
the conditional to check columnWidths truthiness (or reuse the existing
exportConfig?.columnWidths check) and fall back to { wch: 15 } when
columnWidths[i] is undefined, keeping the variable name exportColumnWidths and
referencing exportHeaders as before.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (9)
apps/vite-web-example/package.jsonapps/vite-web-example/request.jsonapps/vite-web-example/src/pages/orders-page.tsxdocs/export-config.mdpackages/table/package.jsonpackages/table/src/data-table.tsxpackages/table/src/export.tsxpackages/table/src/toolbar.tsxpackages/table/src/types.ts
✅ Files skipped from review due to trivial changes (1)
- docs/export-config.md
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/vite-web-example/package.json
- fix(tablecraft-adapter): chunk queryByIds requests by maxPageSize to prevent URL/backend limits - fix(export): merge custom column mapping on top of auto-generated headers - fix(export): safely handle exportConfig.columnWidths without non-null assertion - chore: remove accidental request.json payload and add to .gitignore - refactor(orders-page): remove unnecessary Number() cast for row.total - refactor(data-table): replace inline type cast with imported ExportConfig
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Overview
This PR overhauls the Export System for better Developer Experience and type-safety, alongside crucial fixes for monorepo build stability.
Highlights & Features
removeHeaders):Replaced the inclusion-based
headersarray with an exclusion-basedremoveHeaders. This makes configuring exports for tables with many columns significantly simpler.KnownStringKeys<T>:Resolved an autocomplete bug where index signatures (like
Record<string, unknown>) swallowed column suggestions. Autocomplete forremoveHeadersandcolumnMappingnow perfectly suggests generated*Columnkeys!Added
queryByIdssupport to the adapter for fetching user selections that span across multiple paginated pages during export operations.Fixes
--bunflag from packagetscbuild scripts. When the Vite app rebuilt, Bun'stscimplementation would aggressively cleardistfolders without successfully regenerating all files, leading to persistent local TS errors. Standardtscis now enforced.Documentation
docs/export-config.mdto reflect theremoveHeadersshift and fully explain export options.