fix: empty changeset must serialize as [] not null (white-screen after snapshot)

toChangesetResponse initialises updates/prunes/readOnly so a zone matching
its template exactly (e.g. right after 'create template from zone') marshals
arrays, not null. DiffView/DomainDiffPage also normalise null defensively.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
This commit is contained in:
2026-07-05 13:10:08 +07:00
parent 08697e06d7
commit bc2e77ad4e
5 changed files with 47 additions and 6 deletions
+14
View File
@@ -26,3 +26,17 @@ test("marks read-only records", () => {
render(<DiffView changeset={cs} />)
expect(screen.getByText(/NS/)).toBeInTheDocument()
})
test("does not crash when changeset fields are null", () => {
// An empty changeset from an older/edge backend can arrive with null slices
// instead of []. DiffView must normalise them, not blow up on .length/.map.
const nullish = {
updates: null,
prunes: null,
readOnly: null,
inSyncCount: 5,
} as unknown as ChangesetResponse
render(<DiffView changeset={nullish} />)
expect(screen.getByText(/5/)).toBeInTheDocument()
expect(screen.getByText(/in sync/)).toBeInTheDocument()
})
+5 -3
View File
@@ -137,11 +137,13 @@ export function DiffView({
changeset: ChangesetResponse
footerExtra?: ReactNode
}) {
// Defensive: a field may arrive as null (e.g. a nil slice from an older
// backend) — normalise to [] so Section never calls .length/.map on null.
return (
<div className="flex flex-col gap-6">
<Section tone="update" records={changeset.updates} />
<Section tone="delete" records={changeset.prunes} />
<Section tone="readonly" records={changeset.readOnly} />
<Section tone="update" records={changeset.updates ?? []} />
<Section tone="delete" records={changeset.prunes ?? []} />
<Section tone="readonly" records={changeset.readOnly ?? []} />
<div className="flex items-center justify-between gap-3 border-t border-border pt-4">
<div className="flex items-center gap-1.5 text-sm text-muted-foreground">
+2 -2
View File
@@ -41,8 +41,8 @@ export function DomainDiffPage() {
const pruneCheckboxId = useId()
const changeset = check.data
const hasPrunes = (changeset?.prunes.length ?? 0) > 0
const hasUpdates = (changeset?.updates.length ?? 0) > 0
const hasPrunes = (changeset?.prunes?.length ?? 0) > 0
const hasUpdates = (changeset?.updates?.length ?? 0) > 0
const pruneWarning = applyPrunes && hasPrunes
const recordList = zoneRecords.data ?? []