fix: address Copilot review round 10 — structural const match, deep-link constants

- selectBranchIndex compares a discriminator structurally: a `const` may be
  an object or array, and deep-link arguments arrive as freshly parsed
  instances that could never be reference-equal to the schema's own.
- New applySchemaConstants, applied by InspectorView AFTER the deep link's
  `appArgs` are spread over the seeded defaults. The form renders a pinned
  field read-only, so a link disagreeing with one would otherwise auto-open an
  App with a hidden value contradicting the shape on screen.

Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
cliffhall 2026-08-26 04:23:14 -04:00
commit 775fec0f6a

View file

@ -6,6 +6,7 @@ import {
getValueAtPath,
collectSchemaDefaults,
hasMissingRequiredFields,
applySchemaConstants,
} from "./jsonUtils";
import type { InspectorFormSchema } from "./jsonUtils";
@ -432,6 +433,28 @@ describe("root composition (#2123)", () => {
expect(hasMissingRequiredFields(schema, values)).toBe(false);
});
it("re-applies a branch's constants over conflicting supplied values", () => {
// A read-only field cannot be corrected by the user, so a deep link
// disagreeing with a `const` must not survive into the submitted arguments.
expect(applySchemaConstants(UNION, { kind: "sms", note: "hi" })).toEqual({
kind: "sms",
note: "hi",
});
expect(applySchemaConstants(UNION, { kind: "nonsense" })).toEqual({
kind: "email",
});
});
it("leaves values alone when nothing is pinned", () => {
const values = { a: 1 };
expect(
applySchemaConstants(
{ type: "object", properties: { a: { type: "number" } } },
values,
),
).toBe(values);
});
it("blocks submission while no branch is satisfied", () => {
expect(hasMissingRequiredFields(UNION, {})).toBe(true);
expect(hasMissingRequiredFields(UNION, { kind: "email" })).toBe(true);