fix: address Copilot review — const precedence, TUI variant model, decline both-keyword unions
- rootUnion: decline a schema carrying BOTH oneOf and anyOf rather than reading one and silently dropping the other's constraints. - SchemaForm: handle `const` before every type dispatch and for every constant type, so a numeric/boolean constant, or a schema carrying both `const` and `enum`, can no longer offer a forbidden value. - collectSchemaDefaults / TUI initialValue: `const` outranks `default`, which is an annotation a schema may set to something its const rejects. - convertToolParameters: resolve from the first branch's merged schema, so a branch's specialization of a root-declared property is what types the arg. - TUI: ink-form keys values by field name across the whole form, so two branches' `kind` were one field and the later section's initial value decided what the earlier submitted. Branch fields now render under prefixed names behind a Variant select, and decodeFormValues translates back on submit, dropping every branch but the chosen one. Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
5933162dd0
commit
2782ed75d1
2 changed files with 370 additions and 68 deletions
|
|
@ -382,7 +382,10 @@ describe("root composition (#2123)", () => {
|
|||
).toEqual({ version: "1" });
|
||||
});
|
||||
|
||||
it("prefers an explicit default over a const", () => {
|
||||
it("prefers a const over a conflicting default", () => {
|
||||
// `default` is an annotation, not a constraint, so a schema may advertise
|
||||
// one its own `const` rejects — seeding it would submit an invalid value
|
||||
// through a read-only field.
|
||||
expect(
|
||||
collectSchemaDefaults({
|
||||
type: "object",
|
||||
|
|
@ -390,7 +393,7 @@ describe("root composition (#2123)", () => {
|
|||
v: { type: "string", const: "a", default: "b" },
|
||||
},
|
||||
}),
|
||||
).toEqual({ v: "b" });
|
||||
).toEqual({ v: "a" });
|
||||
});
|
||||
|
||||
it("collects defaults from a root allOf", () => {
|
||||
|
|
|
|||
|
|
@ -139,13 +139,19 @@ export function collectSchemaDefaults(
|
|||
// without this the form would *display* a hoisted default that never
|
||||
// reached the seeded values — the field would submit empty (#1928).
|
||||
const fieldSchema = normalizeNullableUnion(rawSchema);
|
||||
if (fieldSchema.default !== undefined) {
|
||||
result[fieldName] = fieldSchema.default;
|
||||
} else if (fieldSchema.const !== undefined) {
|
||||
if (fieldSchema.const !== undefined) {
|
||||
// `const` is a one-value enumeration, so the only submittable value is
|
||||
// already known — seeding it spares the user hand-typing a discriminator
|
||||
// the schema has fixed (#2123), and matches what a `default` would do.
|
||||
// the schema has fixed (#2123).
|
||||
//
|
||||
// It outranks `default`, which JSON Schema defines as an annotation
|
||||
// rather than a constraint: a schema may advertise a default its own
|
||||
// `const` rejects, and seeding that would submit an invalid argument
|
||||
// through a field rendered read-only, leaving the user no way to correct
|
||||
// it.
|
||||
result[fieldName] = fieldSchema.const;
|
||||
} else if (fieldSchema.default !== undefined) {
|
||||
result[fieldName] = fieldSchema.default;
|
||||
} else if (fieldSchema.type === "object" && fieldSchema.properties) {
|
||||
const nested = collectSchemaDefaults(fieldSchema);
|
||||
if (Object.keys(nested).length > 0) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue