feat(methods): add getDeepError and getDeepErrorEntry methods - #192
feat(methods): add getDeepError and getDeepErrorEntry methods#192fabian-hiller wants to merge 3 commits into
Conversation
Add two methods that walk a field's subtree and stop at the first field with errors, to display a single error message for a field whose value is a nested structure (e.g. a rich text editor or tags input). Includes unit and type tests, website API docs with config type routes, menu and guide updates across all frameworks, and a changelog entry.
WalkthroughAdds Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31151d7ee3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // field with errors, so errors of a field surface before its descendants | ||
| let deepError: string | null = null; | ||
| walkFieldStore( | ||
| config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], |
There was a problem hiding this comment.
Guard array paths that have no live field store
When a schema contains a dynamic array whose current input has fewer elements than the requested numeric path—for example, an empty items array with { path: ['items', 0] }—the path passes type checking, but getFieldStore returns undefined and walkFieldStore immediately dereferences it, throwing a TypeError rather than returning null. Guard the lookup before walking it; the equivalent expression in getDeepErrorEntry needs the same handling.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Pull request overview
Adds two new @formisch/methods helpers (getDeepError, getDeepErrorEntry) that traverse a field subtree (or the full form) and stop at the first field with errors, enabling parent-level error display for nested-value fields (issue #153). The PR also wires the new APIs into exports, docs site navigation/API reference, and includes unit + type tests.
Changes:
- Add
getDeepError(returns first error message) andgetDeepErrorEntry(returns first erroring field’s entry) usingwalkFieldStoreearly-stop traversal. - Add Vitest unit tests and
*.test-d.tstype tests for both new methods. - Update website docs: method pages + config type pages, and add method links to framework menus/guides; add changelog entry.
Reviewed changes
Copilot reviewed 41 out of 41 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| website/src/routes/(docs)/vue/guides/(main-concepts)/form-methods/index.mdx | Add getDeepError/getDeepErrorEntry to Vue form-methods guide list |
| website/src/routes/(docs)/vue/api/menu.md | Add new methods + config types to Vue API menu |
| website/src/routes/(docs)/svelte/guides/(main-concepts)/form-methods/index.mdx | Add getDeepError/getDeepErrorEntry to Svelte form-methods guide list |
| website/src/routes/(docs)/svelte/api/menu.md | Add new methods + config types to Svelte API menu |
| website/src/routes/(docs)/solid/guides/(main-concepts)/form-methods/index.mdx | Add getDeepError/getDeepErrorEntry to Solid form-methods guide list |
| website/src/routes/(docs)/solid/api/menu.md | Add new methods + config types to Solid API menu |
| website/src/routes/(docs)/react/guides/(main-concepts)/form-methods/index.mdx | Add getDeepError/getDeepErrorEntry to React form-methods guide list |
| website/src/routes/(docs)/react/api/menu.md | Add new methods + config types to React API menu |
| website/src/routes/(docs)/react-native/guides/(main-concepts)/form-methods/index.mdx | Add getDeepError/getDeepErrorEntry to React Native form-methods guide list |
| website/src/routes/(docs)/react-native/api/menu.md | Add new methods + config types to React Native API menu |
| website/src/routes/(docs)/qwik/guides/(main-concepts)/form-methods/index.mdx | Add getDeepError/getDeepErrorEntry to Qwik form-methods guide list |
| website/src/routes/(docs)/qwik/api/menu.md | Add new methods + config types to Qwik API menu |
| website/src/routes/(docs)/preact/guides/(main-concepts)/form-methods/index.mdx | Add getDeepError/getDeepErrorEntry to Preact form-methods guide list |
| website/src/routes/(docs)/preact/api/menu.md | Add new methods + config types to Preact API menu |
| website/src/routes/(docs)/angular/guides/(main-concepts)/form-methods/index.mdx | Add getDeepError/getDeepErrorEntry to Angular form-methods guide list |
| website/src/routes/(docs)/angular/api/menu.md | Add new methods + config types to Angular API menu |
| website/src/routes/(docs)/methods/api/(types)/GetFormDeepErrorEntryConfig/properties.ts | Add API property metadata for GetFormDeepErrorEntryConfig |
| website/src/routes/(docs)/methods/api/(types)/GetFormDeepErrorEntryConfig/index.mdx | Add API type page for GetFormDeepErrorEntryConfig |
| website/src/routes/(docs)/methods/api/(types)/GetFormDeepErrorConfig/properties.ts | Add API property metadata for GetFormDeepErrorConfig |
| website/src/routes/(docs)/methods/api/(types)/GetFormDeepErrorConfig/index.mdx | Add API type page for GetFormDeepErrorConfig |
| website/src/routes/(docs)/methods/api/(types)/GetFieldDeepErrorEntryConfig/properties.ts | Add API property metadata for GetFieldDeepErrorEntryConfig |
| website/src/routes/(docs)/methods/api/(types)/GetFieldDeepErrorEntryConfig/index.mdx | Add API type page for GetFieldDeepErrorEntryConfig |
| website/src/routes/(docs)/methods/api/(types)/GetFieldDeepErrorConfig/properties.ts | Add API property metadata for GetFieldDeepErrorConfig |
| website/src/routes/(docs)/methods/api/(types)/GetFieldDeepErrorConfig/index.mdx | Add API type page for GetFieldDeepErrorConfig |
| website/src/routes/(docs)/methods/api/(types)/DeepErrorEntry/index.mdx | Update DeepErrorEntry docs to mention getDeepErrorEntry |
| website/src/routes/(docs)/methods/api/(methods)/getDeepErrors/index.mdx | Link getDeepError from getDeepErrors related methods |
| website/src/routes/(docs)/methods/api/(methods)/getDeepErrorEntry/properties.ts | Add API reference property metadata for getDeepErrorEntry |
| website/src/routes/(docs)/methods/api/(methods)/getDeepErrorEntry/index.mdx | Add API doc page for getDeepErrorEntry |
| website/src/routes/(docs)/methods/api/(methods)/getDeepErrorEntries/index.mdx | Link getDeepErrorEntry from getDeepErrorEntries related methods |
| website/src/routes/(docs)/methods/api/(methods)/getDeepError/properties.ts | Add API reference property metadata for getDeepError |
| website/src/routes/(docs)/methods/api/(methods)/getDeepError/index.mdx | Add API doc page for getDeepError |
| packages/methods/src/index.ts | Export new getDeepError and getDeepErrorEntry from package entrypoint |
| packages/methods/src/getDeepErrorEntry/index.ts | Add barrel export for getDeepErrorEntry |
| packages/methods/src/getDeepErrorEntry/getDeepErrorEntry.ts | Implement getDeepErrorEntry (early-stop deep traversal) + config interfaces |
| packages/methods/src/getDeepErrorEntry/getDeepErrorEntry.test.ts | Add unit tests for getDeepErrorEntry |
| packages/methods/src/getDeepErrorEntry/getDeepErrorEntry.test-d.ts | Add type tests for getDeepErrorEntry |
| packages/methods/src/getDeepError/index.ts | Add barrel export for getDeepError |
| packages/methods/src/getDeepError/getDeepError.ts | Implement getDeepError (early-stop deep traversal) + config interfaces |
| packages/methods/src/getDeepError/getDeepError.test.ts | Add unit tests for getDeepError |
| packages/methods/src/getDeepError/getDeepError.test-d.ts | Add type tests for getDeepError |
| packages/methods/CHANGELOG.md | Add changelog entry for new deep-error helpers |
Suppressed comments (1)
website/src/routes/(docs)/methods/api/(methods)/getDeepErrorEntry/index.mdx:14
- This paragraph suggests the helper is for displaying a "single" error message, but the return value contains an array of error messages (
errors). Tweaking the wording to "first" (or similar) would better match the API.
Retrieves the errors of the first erroring field of a specific field or the entire form as an entry pairing the path to the field with its error messages, by walking through the field store and all its descendants and stopping at the first field with errors. This is useful for displaying a single error message for a field whose value is a nested structure while still knowing where the error occurred.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| @@ -0,0 +1,54 @@ | |||
| --- | |||
| title: getDeepErrorEntry | |||
| description: Retrieves the errors of the first erroring field of a specific field or the entire form as a path and error message entry. | |||
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/methods/src/getDeepError/getDeepError.ts`:
- Line 83: Handle an undefined resolved field store before traversal in
getDeepError.ts at lines 83-83 and getDeepErrorEntry.ts at lines 88-88: return
null when the config path resolves through getFieldStore to no store, and only
call walkFieldStore when a store exists.
In `@packages/methods/src/getDeepErrorEntry/getDeepErrorEntry.test-d.ts`:
- Around line 22-25: Run the repository’s configured Prettier formatter on the
test declaration file containing the getDeepErrorEntry type assertion, and
retain the formatter’s output so the file passes pnpm format.check.
In
`@website/src/routes/`(docs)/methods/api/(types)/GetFieldDeepErrorEntryConfig/index.mdx:
- Line 1: Run Prettier with write mode on the complete
GetFieldDeepErrorEntryConfig documentation file, then retain and commit the
resulting formatting changes so the website formatting check passes.
In
`@website/src/routes/`(docs)/methods/api/(types)/GetFormDeepErrorEntryConfig/index.mdx:
- Line 1: Format the GetFormDeepErrorEntryConfig documentation file with
Prettier using the project’s standard write command, then commit the resulting
formatted output.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: fdc25d18-1c04-4738-a9f5-560758936f87
📒 Files selected for processing (41)
packages/methods/CHANGELOG.mdpackages/methods/src/getDeepError/getDeepError.test-d.tspackages/methods/src/getDeepError/getDeepError.test.tspackages/methods/src/getDeepError/getDeepError.tspackages/methods/src/getDeepError/index.tspackages/methods/src/getDeepErrorEntry/getDeepErrorEntry.test-d.tspackages/methods/src/getDeepErrorEntry/getDeepErrorEntry.test.tspackages/methods/src/getDeepErrorEntry/getDeepErrorEntry.tspackages/methods/src/getDeepErrorEntry/index.tspackages/methods/src/index.tswebsite/src/routes/(docs)/angular/api/menu.mdwebsite/src/routes/(docs)/angular/guides/(main-concepts)/form-methods/index.mdxwebsite/src/routes/(docs)/methods/api/(methods)/getDeepError/index.mdxwebsite/src/routes/(docs)/methods/api/(methods)/getDeepError/properties.tswebsite/src/routes/(docs)/methods/api/(methods)/getDeepErrorEntries/index.mdxwebsite/src/routes/(docs)/methods/api/(methods)/getDeepErrorEntry/index.mdxwebsite/src/routes/(docs)/methods/api/(methods)/getDeepErrorEntry/properties.tswebsite/src/routes/(docs)/methods/api/(methods)/getDeepErrors/index.mdxwebsite/src/routes/(docs)/methods/api/(types)/DeepErrorEntry/index.mdxwebsite/src/routes/(docs)/methods/api/(types)/GetFieldDeepErrorConfig/index.mdxwebsite/src/routes/(docs)/methods/api/(types)/GetFieldDeepErrorConfig/properties.tswebsite/src/routes/(docs)/methods/api/(types)/GetFieldDeepErrorEntryConfig/index.mdxwebsite/src/routes/(docs)/methods/api/(types)/GetFieldDeepErrorEntryConfig/properties.tswebsite/src/routes/(docs)/methods/api/(types)/GetFormDeepErrorConfig/index.mdxwebsite/src/routes/(docs)/methods/api/(types)/GetFormDeepErrorConfig/properties.tswebsite/src/routes/(docs)/methods/api/(types)/GetFormDeepErrorEntryConfig/index.mdxwebsite/src/routes/(docs)/methods/api/(types)/GetFormDeepErrorEntryConfig/properties.tswebsite/src/routes/(docs)/preact/api/menu.mdwebsite/src/routes/(docs)/preact/guides/(main-concepts)/form-methods/index.mdxwebsite/src/routes/(docs)/qwik/api/menu.mdwebsite/src/routes/(docs)/qwik/guides/(main-concepts)/form-methods/index.mdxwebsite/src/routes/(docs)/react-native/api/menu.mdwebsite/src/routes/(docs)/react-native/guides/(main-concepts)/form-methods/index.mdxwebsite/src/routes/(docs)/react/api/menu.mdwebsite/src/routes/(docs)/react/guides/(main-concepts)/form-methods/index.mdxwebsite/src/routes/(docs)/solid/api/menu.mdwebsite/src/routes/(docs)/solid/guides/(main-concepts)/form-methods/index.mdxwebsite/src/routes/(docs)/svelte/api/menu.mdwebsite/src/routes/(docs)/svelte/guides/(main-concepts)/form-methods/index.mdxwebsite/src/routes/(docs)/vue/api/menu.mdwebsite/src/routes/(docs)/vue/guides/(main-concepts)/form-methods/index.mdx
| // field with errors, so errors of a field surface before its descendants | ||
| let deepError: string | null = null; | ||
| walkFieldStore( | ||
| config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
Handle missing dynamic field stores before traversal. getFieldStore can return undefined for a type-valid path to an uncreated dynamic array item. Both methods pass that value to walkFieldStore, which throws instead of returning null.
packages/methods/src/getDeepError/getDeepError.ts#L83-L83: handle a missing resolved store and returnnullbefore callingwalkFieldStore.packages/methods/src/getDeepErrorEntry/getDeepErrorEntry.ts#L88-L88: handle a missing resolved store and returnnullbefore callingwalkFieldStore.
📍 Affects 2 files
packages/methods/src/getDeepError/getDeepError.ts#L83-L83(this comment)packages/methods/src/getDeepErrorEntry/getDeepErrorEntry.ts#L88-L88
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/methods/src/getDeepError/getDeepError.ts` at line 83, Handle an
undefined resolved field store before traversal in getDeepError.ts at lines
83-83 and getDeepErrorEntry.ts at lines 88-88: return null when the config path
resolves through getFieldStore to no store, and only call walkFieldStore when a
store exists.
There was a problem hiding this comment.
2 issues found across 41 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/methods/src/getDeepError/getDeepError.ts">
<violation number="1" location="packages/methods/src/getDeepError/getDeepError.ts:83">
P1: Calling `getDeepError` for a schema-valid but currently absent array item throws instead of returning `null`, which can break error rendering after array removal. Handle a missing runtime path before invoking `walkFieldStore` (ideally by making shared `getFieldStore` return an optional value and updating its consumers).</violation>
<violation number="2" location="packages/methods/src/getDeepError/getDeepError.ts:84">
P3: getDeepError and getDeepErrorEntry (added together in this PR) duplicate the same walk-with-early-stop logic and config interfaces, differing only in what they capture from the first errored field (`errors[0]` vs `{ path, errors }`). The deepest erroring path is available in both cases, so a single shared internal helper (e.g. one that walks and returns the first errored `InternalFieldStore` or `null`) could back both methods and avoid the two near-identical walk loops diverging over time. Since this follows Formisch's one-file-per-method convention this is a minor maintainability consideration rather than a functional problem.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| // field with errors, so errors of a field surface before its descendants | ||
| let deepError: string | null = null; | ||
| walkFieldStore( | ||
| config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], |
There was a problem hiding this comment.
P1: Calling getDeepError for a schema-valid but currently absent array item throws instead of returning null, which can break error rendering after array removal. Handle a missing runtime path before invoking walkFieldStore (ideally by making shared getFieldStore return an optional value and updating its consumers).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/methods/src/getDeepError/getDeepError.ts, line 83:
<comment>Calling `getDeepError` for a schema-valid but currently absent array item throws instead of returning `null`, which can break error rendering after array removal. Handle a missing runtime path before invoking `walkFieldStore` (ideally by making shared `getFieldStore` return an optional value and updating its consumers).</comment>
<file context>
@@ -0,0 +1,93 @@
+ // field with errors, so errors of a field surface before its descendants
+ let deepError: string | null = null;
+ walkFieldStore(
+ config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL],
+ (internalFieldStore) => {
+ const errors = internalFieldStore.errors.value;
</file context>
| let deepError: string | null = null; | ||
| walkFieldStore( | ||
| config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], | ||
| (internalFieldStore) => { |
There was a problem hiding this comment.
P3: getDeepError and getDeepErrorEntry (added together in this PR) duplicate the same walk-with-early-stop logic and config interfaces, differing only in what they capture from the first errored field (errors[0] vs { path, errors }). The deepest erroring path is available in both cases, so a single shared internal helper (e.g. one that walks and returns the first errored InternalFieldStore or null) could back both methods and avoid the two near-identical walk loops diverging over time. Since this follows Formisch's one-file-per-method convention this is a minor maintainability consideration rather than a functional problem.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/methods/src/getDeepError/getDeepError.ts, line 84:
<comment>getDeepError and getDeepErrorEntry (added together in this PR) duplicate the same walk-with-early-stop logic and config interfaces, differing only in what they capture from the first errored field (`errors[0]` vs `{ path, errors }`). The deepest erroring path is available in both cases, so a single shared internal helper (e.g. one that walks and returns the first errored `InternalFieldStore` or `null`) could back both methods and avoid the two near-identical walk loops diverging over time. Since this follows Formisch's one-file-per-method convention this is a minor maintainability consideration rather than a functional problem.</comment>
<file context>
@@ -0,0 +1,93 @@
+ let deepError: string | null = null;
+ walkFieldStore(
+ config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL],
+ (internalFieldStore) => {
+ const errors = internalFieldStore.errors.value;
+ if (errors) {
</file context>
Format the type test and two config type doc routes, reword the form-methods guide bullet to say that getDeepErrorEntry returns all error messages of the first erroring field, and document that form-level errors are included with an empty path.
The v1.0.0 release PR dates the unreleased section and already includes the getDeepError and getDeepErrorEntry entry, so keeping the placeholder edit here would only conflict with it.
Resolves #153 — the last item in the v1 milestone.
Adds two methods that walk a field's subtree and stop at the first field with errors, for displaying a single error message on a field whose value is a nested structure (rich text editor, tags input, file upload with metadata).
getDeepErrorreturns the first error message;getDeepErrorEntryadditionally returns the path where it occurred. Both reusewalkFieldStore's early-stop support, so the walk ends at the first match instead of collecting everything likegetDeepErrors.Includes unit + type tests, website API docs (method and config type routes), and menu and form-methods guide updates for all eight frameworks.
Note: the changelog entry is intentionally omitted so this PR stays independent of the v1.0.0 release PR (#194). When merging this after the release, add the entry under a new
## vX.X.X (Month DD, YYYY)placeholder section inpackages/methods/CHANGELOG.md:Summary by CodeRabbit
New Features
getDeepErrorto retrieve the first error message from a form or selected field path.getDeepErrorEntryto retrieve the first error along with its field path and messages.Documentation
Tests