Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .changeset/6956-listview-export-options-spec-mirror.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
'@object-ui/types': minor
---

**Breaking for authored metadata:** the `exportOptions` member of the ListView
zod mirror (`ListViewSchema` in `@object-ui/types/zod`) is now `@objectstack/spec`'s
own `ListViewSchema.shape.exportOptions`, bound by reference rather than
restated (objectui#6956). A `list-view` document that authors the retired `'pdf'`
format — in either spelling, `exportOptions: ['csv', 'pdf']` or
`exportOptions: { formats: ['pdf'] }` — or a sixth key on the object form
(`{ formats: ['csv'], compression: 'gzip' }`) no longer validates through this
package's mirror. It never validated at the platform's publish gate:
`@objectstack/spec` 17.0.0 removed `'pdf'` from the format enum (objectstack#8010;
PDF export itself was declined as objectstack#1301 NOT_PLANNED) and made the
object form strict, so the mirror was passing locally what the platform refuses
with an `os migrate meta --from 16` prescription — an author saw green here and
a refusal upstream. `streaming`, the fifth spec key, is now declared on this face
(the renderer honoured it; no local declaration carried it).

**What was measured, on this branch's base.** The mirror declared a pre-#8010
shape of its own — `'pdf'` in both branches, no `streaming`, a non-strict
`z.object` — and `ListViewInferred` is `z.input` of that mirror, so the
`ListViewSchema` TYPE the ListView renderer is written against disagreed with
its sibling `ObjectGridSchema['exportOptions']` (the clean five-key
`ListViewExportOptions`), and the renderer could only read `streaming` through
`as any`. Against the installed pin (`@objectstack/spec@17.2.0`, not a working
tree), `ListViewSchema.shape.exportOptions` from `@objectstack/spec/ui` lifts
`['csv', 'xlsx']` to `{ formats: ['csv', 'xlsx'] }`, refuses `['csv', 'pdf']`
with the migration prescription, refuses `{ formats: ['csv'], compression: 'gzip' }`
(strict), and accepts `{ formats: ['csv'], streaming: true }` with the value
intact. The mirror now IS that schema object, so the four verdicts are the
spec's by construction; `export-options-spec-parity.test.ts` pins the identity,
the four verdicts, and the survival of `streaming` through a parse.

**The TS face follows.** `ListViewSchema['exportOptions']` is now the spec's
INPUT type: `ListViewExportFormat[] | ListViewExportOptions` — the bare array
stays admissible on input because nothing on the render path parses, and the
object arm IS the same `ListViewExportOptions` that `ObjectGridSchema` and
`NamedListView` carry. One spec key, one type, on every local authoring surface;
`'pdf'` is a compile-time refusal in both spellings.

**Who is NOT affected.** A document authoring `['csv', 'xlsx']`,
`{ formats: ['csv', 'json'] }` or any combination of the five spec keys is
untouched; absent stays valid; the member's description is now the spec's own
text. The spec's parse-time lift of a bare array to `{ formats }` now runs for
whoever parses through this mirror as well.

**Migration:** delete `'pdf'` (the surviving formats are `'csv'`, `'xlsx'` and
`'json'`); delete any key outside `formats` / `maxRecords` / `includeHeaders` /
`fileNamePrefix` / `streaming`. `os migrate meta --from 16` lists the mechanical
edits for existing sources.

Graded `minor`, not `patch`: this narrows the accepted input set, which is
breaking for any author who wrote the tolerated value. It is not `major` per
this repo's fixed-group convention (objectui's own breaking changes ship as
`minor`; the group's major tracks `@objectstack` — AGENTS.md 版本号策略,
mechanically enforced by `scripts/check-changeset-no-major.mjs`).
17 changes: 17 additions & 0 deletions .changeset/6956-plugin-list-export-options-casts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@object-ui/plugin-list': patch
---

`ListView` reads `exportOptions.streaming` without a cast (objectui#6956). The
two `as any` reads — the `exportableFormats` server-availability check and
`handleExport`'s server-eligibility gate — and the `'pdf'` in the bare-array
fold's cast are gone: the `ListViewSchema` type now carries `streaming` and not
`'pdf'`, because `@object-ui/types`' zod mirror binds the spec's `exportOptions`
field by reference. No behaviour change: the same formats are offered,
`streaming: false` still forces the client-side path, and the bare-array fold
(`resolvedExportOptions`, a stored `['csv', 'xlsx']` folded to `{ formats }`)
STAYS — nothing on the render path parses and `ObjectView` forwards a stored
value verbatim, so the spec's parse-time lift never runs before this renderer
and the fold is load-bearing rather than legacy. A `'pdf'` stored before the
retirement still arrives as data and is still dropped from the export menu with
the existing one-time warning.
6 changes: 3 additions & 3 deletions packages/plugin-list/src/ListView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1301,7 +1301,7 @@ export const ListView = React.forwardRef<ListViewHandle, ListViewProps>(({
if (!schema.exportOptions) return undefined;
// Spec format: simple string[] like ['csv', 'xlsx']
if (Array.isArray(schema.exportOptions)) {
return { formats: schema.exportOptions as Array<'csv' | 'xlsx' | 'json' | 'pdf'> };
return { formats: schema.exportOptions };
}
// ObjectUI format: already an object
return schema.exportOptions;
Expand All @@ -1321,7 +1321,7 @@ export const ListView = React.forwardRef<ListViewHandle, ListViewProps>(({
const declared = resolvedExportOptions?.formats || ['csv', 'json'];
const serverAvailable = typeof dataSource?.exportDownload === 'function'
&& !!schema.objectName
&& (resolvedExportOptions as any)?.streaming !== false;
&& resolvedExportOptions?.streaming !== false;
const supported = serverAvailable ? ['csv', 'xlsx', 'json'] : ['csv', 'json'];
return declared.filter((f: string) => supported.includes(f));
}, [resolvedExportOptions, dataSource, schema.objectName]);
Expand Down Expand Up @@ -2973,7 +2973,7 @@ export const ListView = React.forwardRef<ListViewHandle, ListViewProps>(({
const serverEligible = (format === 'csv' || format === 'xlsx' || format === 'json')
&& typeof dataSource?.exportDownload === 'function'
&& !!schema.objectName
&& (exportConfig as any)?.streaming !== false;
&& exportConfig?.streaming !== false;
if (serverEligible) {
const fields = effectiveFields
.map((f: any) => columnIdentity(f))
Expand Down
Loading
Loading