fix: address Copilot review round 34 — structured constants, non-required const controls
- A `const` may be an object or an array, with or without a `type`, and
`String({...})` is "[object Object]" — which no CLI argument can equal, so
the only value such a schema accepts never matched. Supplied text is parsed
and compared structurally through the resolver's own comparison (exported as
`sameJsonValue` so the two cannot disagree), for both branch selection and
the substitution.
- The TUI's one-option const control is never marked required: its option may
legitimately be the empty string, which ink-form's required gate can never
accept, so submission would not even reach `decodeFormValues`. The value is
fixed by the schema, and `missingRequiredFields` still validates the call.
Signed-off-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
parent
ffa08d76f5
commit
ba91db97e2
3 changed files with 79 additions and 3 deletions
|
|
@ -503,6 +503,24 @@ describe("schemaToForm", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("never marks a const control required", () => {
|
||||
// Its one option may be the empty string, which ink-form's required gate
|
||||
// can never accept — the call would not even reach `decodeFormValues`.
|
||||
const form = schemaToForm(
|
||||
{
|
||||
type: "object",
|
||||
properties: { kind: { type: "string", const: "" } },
|
||||
required: ["kind"],
|
||||
},
|
||||
"empty_const",
|
||||
);
|
||||
expect(form.sections[0]!.fields[0]).toMatchObject({
|
||||
name: "kind",
|
||||
type: "select",
|
||||
required: false,
|
||||
});
|
||||
});
|
||||
|
||||
it("renders a const outside a union the same way", () => {
|
||||
const form = schemaToForm(
|
||||
{
|
||||
|
|
|
|||
|
|
@ -357,6 +357,12 @@ function buildFields(schema: JsonSchemaObject): FormField[] {
|
|||
fields.push({
|
||||
type: "select",
|
||||
...baseField,
|
||||
// Never required: the one option may legitimately be the empty string,
|
||||
// which ink-form's required gate can never accept — submission would
|
||||
// not even reach `decodeFormValues`, which reapplies the constant. The
|
||||
// value is fixed by the schema, and `missingRequiredFields` still
|
||||
// validates the decoded call.
|
||||
required: false,
|
||||
initialValue: String(pinned),
|
||||
options: [{ label: String(pinned), value: String(pinned) }],
|
||||
} as FormField);
|
||||
|
|
|
|||
|
|
@ -312,6 +312,28 @@ describe("JSON Utils", () => {
|
|||
).toEqual({ value: "true" });
|
||||
});
|
||||
|
||||
it("matches a structured const by parsing the supplied text (#2123)", () => {
|
||||
const structured: Tool = {
|
||||
name: "structured-const",
|
||||
inputSchema: {
|
||||
type: "object",
|
||||
properties: { tag: { const: { kind: "x", n: 1 } } },
|
||||
},
|
||||
};
|
||||
// `String({...})` is "[object Object]", which no argument can equal — so
|
||||
// the only value the schema accepts would never have matched.
|
||||
expect(
|
||||
convertToolParameters(structured, { tag: '{"n":1,"kind":"x"}' }),
|
||||
).toEqual({ tag: { kind: "x", n: 1 } });
|
||||
// Text that is not that value, or not JSON at all, is left alone.
|
||||
expect(convertToolParameters(structured, { tag: "{}" })).toEqual({
|
||||
tag: "{}",
|
||||
});
|
||||
expect(convertToolParameters(structured, { tag: "nope" })).toEqual({
|
||||
tag: "nope",
|
||||
});
|
||||
});
|
||||
|
||||
it("leaves an ambiguously typed argument uncoerced (#2123)", () => {
|
||||
const ambiguous: Tool = {
|
||||
name: "ambiguous",
|
||||
|
|
|
|||
Loading…
Reference in a new issue