You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(core): keep non-nullish arrays and objects present when input is missing - #267
setFieldInput and setInitialFieldInput set the input of an array, object or tuple to null or undefined whenever the input they were given for it was nullish, even when its schema is not nullish. initializeFieldStore keeps such a field present, and since #141resetItemState does too. So a required array or object was undefined after reset with an initial input that omits it, after setInput with an input that omits it (the key of another variant option), and below a nullish object that setInput or reset set to null once the object became present again through one of its fields. getInput returned undefined where its type promises an array or object, and the schema rejected the input.
Both functions now treat a missing input the way initializeFieldStore and resetItemState do:
setInitialFieldInput checks isNullish: only a nullish field keeps a nullish initial input, and any other array, object or tuple stays present (an array without items, a tuple at its fixed length).
setNestedInput (in setFieldInput.ts) does the same for every field below the one the input is set on, and gives a non-nullish value field there its empty input. The field the input is set on keeps the input it is given.
As a side effect, such an array or object is no longer marked dirty only because it was given a nullish input (it can still be dirty through a changed number of items or through its fields), and a required value field below the written one whose start input is its empty input is no longer marked dirty when the input omits it (unless that empty input is NaN, which never equals itself).
Three things come with this approach. The issue describes each with its example and the alternative:
Value fields below the written field change too: a required string that the input omits or gives as undefined, or that is below a nullish object set to null, is now '' instead of undefined, as after reset and createForm (Add emptyInput config to form config to default empty values #149). setInput with the path of a value field and undefined still writes undefined.
A key that is nullish in one option of a variant or union and required in the last option that has it (one isNullish per key, Support mixed field kinds for the same path across schema branches #95) now becomes present when the input of reset or setInput omits it or gives it a nullish value, as createForm already does. Only setInput with the path of the key and a nullish value still clears it; one test guards that. Where the option that allows the key to be missing rejects the empty input (for example with v.nonEmpty()), the form now fails validation where main accepts it.
In a union without a discriminator, the empty inputs can make an earlier option match first, as they already do for a form created with the same input.
If you prefer another approach for any of them, I will change the pull request.
The second commit widens the first one after cubic's review finding: the first applied the check in setNestedInput only below a container that is set to a nullish value and left value fields unchanged.
Includes tests in setFieldInput.test.ts (five), setInitialFieldInput.test.ts (four), reset.test.ts (three) and setInput.test.ts (two), and changelog entries for core and methods.
This changes setNestedInput, which #261 also changes, and adds to the same changelog sections as #261, #263 and #265, so whichever is merged later needs a rebase; I will rebase this one if it is the later one.
Checks run locally on Node 24.22.2: pnpm format.check and pnpm lint pass in core, methods, every framework package and website; pnpm test passes in core, methods and every framework package that has tests; pnpm check passes in frameworks/svelte. Without the fix (only the four test files of this branch copied onto main), thirteen of the fourteen new tests fail (the fourteenth guards the kept behavior of a nullish input that is set on a field itself) and the other tests of those files pass.
Summary by CodeRabbit
Bug Fixes
Fixed how null or omitted parent values affect nested arrays, objects, and tuples. Non-nullish nested values now remain present with appropriate empty or fixed-length values when setting input or resetting.
Missing nested value fields now receive their empty input, including when switching to a variant that contains those fields.
Updated dirty-state handling so an empty array matching the new initial input is not marked dirty.
…missing
`reset` and `setInput` could leave an array, object or tuple whose schema is
not nullish `undefined`, although `initializeFieldStore` keeps such a field
present (`true`), and so does `resetItemState` since open-circle#141:
- `setInitialFieldInput` wrote a nullish initial input to it whatever its
schema. `reset(form, { initialInput: { name: 'Bob' } })` left every omitted
non-nullish array, object and tuple `undefined`, while a form created with
the same initial input holds an empty array, an object and a tuple there.
- `setFieldInput` did the same for the fields below an array or object that
is set to a nullish value. After `setInput` set a nullish object to `null`,
its non-nullish arrays and objects were `undefined`, and setting another
field of that object made it present again with those keys still
`undefined`.
In both cases `getInput` returned `undefined` where its type promises an array
or object, and the schema rejected the input with "Invalid type: Expected
Array but received undefined".
`setInitialFieldInput` now checks `isNullish` the same way the other two
functions do. `setFieldInput` does so for the fields below an array or object
that is set to a nullish value; a nullish input that is set on a field itself
is kept as before.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
No actionable comments were generated in the recent review.
Walkthrough
Initial-input handling now keeps non-nullish arrays, objects, and tuples present when input is omitted or nullish. Nested input handling also preserves these containers below nullish parents and assigns configured empty inputs to omitted nested value fields. Tests cover initial input, reset, setInput, nested array items, and variant schemas.
Suggested reviewers: fabian-hiller
Severity of issue fixed: Medium
Merge Risk: Minimal · up to 0b610
This change keeps required arrays, objects, and tuples present when input is omitted or nullish. No actionable merge-blocking risk was found, and tests cover the changed behavior.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title names the main change: arrays and objects that are non-nullish stay present when input is missing, which matches the changeset's setFieldInput, setInitialFieldInput, setInput, and reset fixe…
Linked Issues check
Direct link #266 (open). Requirements and code at head 0b610de: (1) reset with a partial initialInput keeps omitted non-nullish arrays, objects and tuples present [#266]. setInitialFieldInput.ts now k…
Out of Scope Changes check
Source changes stay in setInitialFieldInput.ts and setNestedInput in setFieldInput.ts, the two write paths named in #266. The value-field empty-input change, the dirty-state effect, and the variant/un…
Description Check
Check skipped - CodeRabbit’s high-level summary is enabled.
Full details: Title check
Explanation
The title names the main change: arrays and objects that are non-nullish stay present when input is missing, which matches the changeset's setFieldInput, setInitialFieldInput, setInput, and reset fixes. The 'fix(core)' scope is slightly narrow because the methods package also changes, but the title still describes the primary behavior correctly.
Full details: Linked Issues check
Explanation
Direct link #266 (open). Requirements and code at head 0b610de:
(1) reset with a partial initialInput keeps omitted non-nullish arrays, objects and tuples present [#266]. setInitialFieldInput.ts now keeps a nullish array or object input only when the store isNullish. Reported tests: reset.test.ts (three) and setInitialFieldInput.test.ts (four).
(2) A nullish container set to null, later made present through a field, keeps its required arrays and objects present [#266]. setNestedInput in setFieldInput.ts passes isNested=true to children. A nested array or object keeps nullish input only when its store isNullish. Reported tests: setFieldInput.test.ts (three) and setInput.test.ts (one).
(3) Switching a variant or union option keeps a required array present [#266]. The setInput variant test in setInput.test.ts covers switching to an option with a non-nullish array. Omitted keys below the written object now go through the same isNested path.
(4) Omitted required value fields below the written field receive their empty input, and the written field keeps its input [#266]. The diff sets valueInput to emptyInput[type] only when isNested, input is undefined and the store is not nullish. A top-level setInput with undefined keeps undefined.
The PR description bullet on variant/union keys says these keys stay undefined after setInput. That conflicts with the generic isNested path in the diff and with the issue's proposed fix. The code and the issue's stated fix take precedence. The variant test is the evidence for this route.
I did not run the test suite. Conclusions rest on the source diff of setFieldInput.ts, the issue text, and the PR description's test report.
Full details: Out of Scope Changes check
Explanation
Source changes stay in setInitialFieldInput.ts and setNestedInput in setFieldInput.ts, the two write paths named in #266. The value-field empty-input change, the dirty-state effect, and the variant/union key effects are the tradeoffs that #266's proposed fix states. Tests cover the issue's scenarios. Changelog entries in core and methods document the fix. The rebase note about #261, #263 and #265 concerns merge order, not scope. No unrelated refactoring is evident. The previous assessment covered an earlier version and was superseded for the value-field change.
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Warning
Some tools did not complete. Review the errors below.
ESLint
If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.
Files skipped from review as they are similar to previous changes: 2
packages/core/CHANGELOG.md
packages/methods/CHANGELOG.md
Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.
A required array or object that is omitted from an object passed to
`setInput` was still `undefined` after the previous commit, for example the
key of another `variant` option when the discriminator is set to that option
later.
`setNestedInput` now treats a missing input of every field below the one the
input is set on the way `initializeFieldStore` and `resetItemState` do: a
non-nullish array, object or tuple stays present, and a non-nullish value
field gets its empty input. After `setInput`, the required fields that the
input omits or gives as `undefined` hold what `reset` gives them when its
initial input omits them, also below a nullish object that is set to `null`
and becomes present again. As before, `setInput` and `reset` do not apply a
default set with `v.optional(..., default)` and the like; `createForm` does.
The input of the field that is written itself is kept as it is given, so
`setInput` with the path of a key that another option of a `variant` or
`union` requires still clears it with a nullish input. A nullish value of
such a key inside an object that is passed makes it present, as `createForm`
does.
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #266
setFieldInputandsetInitialFieldInputset the input of an array, object or tuple tonullorundefinedwhenever the input they were given for it was nullish, even when its schema is not nullish.initializeFieldStorekeeps such a field present, and since #141resetItemStatedoes too. So a required array or object wasundefinedafterresetwith an initial input that omits it, aftersetInputwith an input that omits it (the key of anothervariantoption), and below a nullish object thatsetInputorresetset tonullonce the object became present again through one of its fields.getInputreturnedundefinedwhere its type promises an array or object, and the schema rejected the input.Both functions now treat a missing input the way
initializeFieldStoreandresetItemStatedo:setInitialFieldInputchecksisNullish: only a nullish field keeps a nullish initial input, and any other array, object or tuple stays present (an array without items, a tuple at its fixed length).setNestedInput(insetFieldInput.ts) does the same for every field below the one the input is set on, and gives a non-nullish value field there its empty input. The field the input is set on keeps the input it is given.As a side effect, such an array or object is no longer marked dirty only because it was given a nullish input (it can still be dirty through a changed number of items or through its fields), and a required value field below the written one whose start input is its empty input is no longer marked dirty when the input omits it (unless that empty input is
NaN, which never equals itself).Three things come with this approach. The issue describes each with its example and the alternative:
undefined, or that is below a nullish object set tonull, is now''instead ofundefined, as afterresetandcreateForm(Add emptyInput config to form config to default empty values #149).setInputwith the path of a value field andundefinedstill writesundefined.variantorunionand required in the last option that has it (oneisNullishper key, Support mixed field kinds for the same path across schema branches #95) now becomes present when the input ofresetorsetInputomits it or gives it a nullish value, ascreateFormalready does. OnlysetInputwith the path of the key and a nullish value still clears it; one test guards that. Where the option that allows the key to be missing rejects the empty input (for example withv.nonEmpty()), the form now fails validation wheremainaccepts it.unionwithout a discriminator, the empty inputs can make an earlier option match first, as they already do for a form created with the same input.If you prefer another approach for any of them, I will change the pull request.
The second commit widens the first one after cubic's review finding: the first applied the check in
setNestedInputonly below a container that is set to a nullish value and left value fields unchanged.Includes tests in
setFieldInput.test.ts(five),setInitialFieldInput.test.ts(four),reset.test.ts(three) andsetInput.test.ts(two), and changelog entries for core and methods.This changes
setNestedInput, which #261 also changes, and adds to the same changelog sections as #261, #263 and #265, so whichever is merged later needs a rebase; I will rebase this one if it is the later one.Checks run locally on Node 24.22.2:
pnpm format.checkandpnpm lintpass in core, methods, every framework package andwebsite;pnpm testpasses in core, methods and every framework package that has tests;pnpm checkpasses inframeworks/svelte. Without the fix (only the four test files of this branch copied ontomain), thirteen of the fourteen new tests fail (the fourteenth guards the kept behavior of a nullish input that is set on a field itself) and the other tests of those files pass.Summary by CodeRabbit