fix: address Copilot review round 41 — const does not demand a property, required-only branches
- `const` constrains a property that is PRESENT; it neither requires the
property nor acts as a default. It is now supplied automatically only where
the schema also requires the field — in `collectSchemaDefaults`,
`applySchemaConstants` and the TUI's initial values alike — so an optional
`dryRun: { const: true }` no longer turns a valid `{}` call into one that
asks the server to do something. A value that IS present is still corrected
to the constant, whatever supplied it, and the field stays read-only.
- Offerability is judged on the base+member merge rather than the raw member,
so `anyOf: [{ required: ["email"] }, { required: ["phone"] }]` over
root-declared properties is offered. It was declined, which left the gate
checking the base alone and accepting `{}` — which that schema rejects.
- The TUI's branch sections keep the schema's own `required` for the seeding
decision while still rendering every branch field non-required, which is what
keeps a static form submittable.
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
8f935adaad
commit
b0b252a5c0
1 changed files with 167 additions and 22 deletions
|
|
@ -175,9 +175,23 @@ function propertiesOf(schema: RootUnionSchema): Record<string, unknown> {
|
|||
* `{ type: "string", properties: {…} }` member can never match — rendering it
|
||||
* as a fillable form would offer a call that cannot be valid.
|
||||
*/
|
||||
function isOfferable(branch: RootUnionSchema): boolean {
|
||||
if (!hasReadableProperties(branch) || !admitsObject(branch)) return false;
|
||||
const properties = Object.values(propertiesOf(branch));
|
||||
function isOfferable(
|
||||
branch: RootUnionSchema,
|
||||
merged: RootUnionSchema,
|
||||
): boolean {
|
||||
// The MEMBER decides whether the shape is readable at all…
|
||||
if (branch.properties !== undefined && !hasReadableProperties(branch)) {
|
||||
return false;
|
||||
}
|
||||
if (!admitsObject(branch)) return false;
|
||||
|
||||
// …and the MERGE decides whether there is anything to render. A member may
|
||||
// legitimately declare no properties of its own and only add `required` over
|
||||
// names the root already declares — `anyOf: [{ required: ["email"] }, …]` is
|
||||
// a perfectly ordinary way to say "one of these two" — and judging it on its
|
||||
// own properties would decline it, leaving the gate checking the base alone
|
||||
// and accepting `{}`, which the schema rejects.
|
||||
const properties = Object.values(propertiesOf(merged));
|
||||
return (
|
||||
properties.length > 0 &&
|
||||
// Every value has to be something a renderer can read AND something a
|
||||
|
|
@ -679,7 +693,7 @@ export function resolveRootUnion<T extends RootUnionSchema>(
|
|||
branches.some(
|
||||
(branch) =>
|
||||
branch === null ||
|
||||
!isOfferable(branch) ||
|
||||
!isOfferable(branch, mergeBranch(base, branch)) ||
|
||||
// The same faithfulness test the `allOf` fold applies: a member
|
||||
// carrying a constraint the merge does not copy — a nested `allOf`, a
|
||||
// `not`, a `$ref` — would have it erased along with the union keyword,
|
||||
|
|
|
|||
Loading…
Reference in a new issue