Skip to content

Commit 92e485a

Browse files
committed
fix: address Copilot review round 21 — seed a nested object's own union branch
collectSchemaDefaults recursed only when a nested object declared `properties` directly, so one keeping its fields on a composition branch was skipped — while SchemaForm rendered that branch and displayed its read-only discriminator. The constant was shown and never submitted, and the server rejected the call. The recursion now gates on declaresAnyFields. Signed-off-by: cliffhall <cliff@futurescale.com>
1 parent d334710 commit 92e485a

2 files changed

Lines changed: 40 additions & 1 deletion

File tree

‎clients/web/src/utils/jsonUtils.test.ts‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,37 @@ describe("root composition (#2123)", () => {
457457
expect(Object.hasOwn(seeded, "__proto__")).toBe(true);
458458
});
459459

460+
it("seeds defaults from a nested object's own union branch", () => {
461+
// The nested form renders that branch, so its read-only discriminator has
462+
// to reach the submitted values too.
463+
expect(
464+
collectSchemaDefaults({
465+
type: "object",
466+
properties: {
467+
config: {
468+
type: "object",
469+
anyOf: [
470+
{
471+
type: "object",
472+
properties: {
473+
kind: { type: "string", const: "email" },
474+
address: { type: "string" },
475+
},
476+
},
477+
{
478+
type: "object",
479+
properties: {
480+
kind: { type: "string", const: "sms" },
481+
phone: { type: "string" },
482+
},
483+
},
484+
],
485+
},
486+
},
487+
}),
488+
).toEqual({ config: { kind: "email" } });
489+
});
490+
460491
it("re-applies a nested object's constants", () => {
461492
// The overlay replaces the whole nested object rather than merging into it,
462493
// so a link naming `{ config: { kind: "sms" } }` would otherwise slip past

‎clients/web/src/utils/jsonUtils.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
} from "@inspector/core/json/nullableUnion.js";
55
import {
66
branchAcceptsValues,
7+
declaresAnyFields,
78
resolveRootUnion,
89
selectBranchIndex,
910
} from "@inspector/core/json/rootUnion.js";
@@ -176,7 +177,14 @@ export function collectSchemaDefaults(
176177
seed(fieldName, fieldSchema.const);
177178
} else if (fieldSchema.default !== undefined) {
178179
seed(fieldName, fieldSchema.default);
179-
} else if (fieldSchema.type === "object" && fieldSchema.properties) {
180+
} else if (
181+
fieldSchema.type === "object" &&
182+
// Not `fieldSchema.properties`: a nested object can keep its fields on a
183+
// composition branch too, and `SchemaForm` renders that branch — so
184+
// skipping it here would leave its read-only discriminator displayed but
185+
// never submitted, and the server would reject the call (#2123).
186+
declaresAnyFields(fieldSchema)
187+
) {
180188
const nested = collectSchemaDefaults(fieldSchema);
181189
if (Object.keys(nested).length > 0) {
182190
seed(fieldName, nested);

0 commit comments

Comments
 (0)