Skip to content

feat(react-lang): expand parser validation with rule-tagged errors#350

Merged
Aditya-thesys merged 8 commits intothesysdev:mainfrom
AbhinRustagi:feat/parser-validation
Mar 22, 2026
Merged

feat(react-lang): expand parser validation with rule-tagged errors#350
Aditya-thesys merged 8 commits intothesysdev:mainfrom
AbhinRustagi:feat/parser-validation

Conversation

@AbhinRustagi
Copy link
Copy Markdown
Contributor

@AbhinRustagi AbhinRustagi commented Mar 17, 2026

Summary

Closes #349

  • Adds three new validation checks to ParseResult.meta.errors: unknown-component, excess-args, and the existing missing-required/null-required rules
  • Replaces ValidationError/ValidationRule with a discriminated OpenUIError type using type + code fields — extensible for future type: "parser" diagnostics
  • Renames meta.validationErrorsmeta.errors
  • Removes StreamParser.finalize() — unresolved references stay in meta.unresolved for consumers to check after streaming ends
  • Exports new OpenUIError and ValidationErrorCode types from the package index

Rendering behavior is unchanged — the parser stays permissive and renders what it can.

Test plan

  • pnpm test in packages/react-lang — 16 tests, all passing
  • unknown-component: parse root = DataTable("col") with DataTable absent from schema → errors contains { type: "validation", code: "unknown-component" }
  • excess-args: parse root = Title("a", "b") where Title takes 1 arg → errors contains { type: "validation", code: "excess-args" }
  • Unresolved refs tracked in meta.unresolved without producing errors (both one-shot and streaming)
  • Streaming: unresolved refs resolve automatically when defined in later chunks
  • Existing missing-required and null-required errors carry new type/code shape
  • TypeScript compiles cleanly (tsc --noEmit)

🤖 Generated with Claude Code

AbhinRustagi and others added 3 commits March 17, 2026 20:30
Adds three new validation checks to ParseResult.meta.validationErrors:
- unknown-component: PascalCase name not found in the library schema
- excess-args: more positional args passed than the schema defines
- unresolved-ref: identifier referenced but never assigned (one-shot and
  stream-end only — forward refs are valid mid-stream)

Also adds:
- ValidationError.rule discriminant field for consumer-side filtering
- ValidationRule type exported from the parser index
- StreamParser.finalize() to signal stream completion and trigger
  unresolved-ref promotion

Existing missing-required and null-required errors now carry rule tags.
Rendering behavior is unchanged — the parser stays permissive.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds vitest and 17 tests covering the three new validation checks:
- unknown-component: unknown PascalCase names are reported, element still renders
- excess-args: extra positional args reported, component still renders
- unresolved-ref: promoted to validationErrors in one-shot and on finalize(),
  but not mid-stream (forward refs are valid during streaming)

Also tests that existing missing-required and null-required errors carry
the new rule field.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add StreamParser.finalize() docs with usage note about when to call it
- Add ValidationRule type and table of all rule values
- Add ValidationError.rule field to type signatures
- Add filtering example showing consumer-side rule usage
- Update README types import list to include ValidationError, ValidationRule

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@Aditya-thesys
Copy link
Copy Markdown
Contributor

@AbhinRustagi Thanks for improving the validation handling, we were also planning to address these issues.

I took a quick look at the PR and will review it in detail when I get some time. At the moment, everything looks good to me except for the finalize part, which I’m not entirely sure about, let me think more about it.

The original idea was that developers would check validation errors only once after the entire streaming is complete, rather than on every chunk. Once streaming is done, we can then check for validation errors and handle them gracefully.

@AbhinRustagi
Copy link
Copy Markdown
Contributor Author

Sure @Aditya-thesys ! Happy to help brainstorm it. My rationale behind the .finalize() was that unresolved-refs is a state-dependent error while other two are stateless. I needed to add an explicit way to indicate the stream has ended and we need to check for remainder values in the meta.unresolved

@Aditya-thesys
Copy link
Copy Markdown
Contributor

Makes sense. I’m a bit hesitant to overload validationErrors here, because to me that should stay limited to schema-validation issues. unresolved and incomplete feel more like parser/generation diagnostics, so instead of promoting unresolved-ref into validationErrors, how about a separate meta.errors field with a union of validation and parser errors?

type OpenUIError =
  | { type: "validation"; code: "missing-required" | "null-required" | "unknown-component" | "excess-args"; ... }
  | { type: "parser"; code: "incomplete" | "unresolved-ref"; ... };

now checking getResult().meta.errors at the end of stream should be enough, wdyt?

@AbhinRustagi
Copy link
Copy Markdown
Contributor Author

AbhinRustagi commented Mar 18, 2026

@Aditya-thesys Sounds good. On a second thought, do you think we can keep meta.unresolved as is and create a flag for it? At the end of the stream, currently, the meta.unresolved is not cleared or discarded - which is where I wanted to add .finalize() to make use of that information. Realised that adding an error for it will essentially add redundancy (same information (that a component is unresolved) in two keys meta.unresolved & meta.errors).

Essentially, we can document how the consumer can check for the unresolved refs -> either check for elements in meta.unresolved (helpful if they want to silently ignore it and only account during development) or we create an optional flag like promoteUnresolvedToError if they want to flag it for strictly monitoring model outputs, in which case we promote to errors and flush meta.unresolved. That being said, we still rename validationErrors to errors for the future. Finally, remove .finalize() Wdyt?

@Aditya-thesys
Copy link
Copy Markdown
Contributor

The promoteUnresolvedToError flag idea is nice, but I would prefer not to incorporate it in this PR for now, since I am already working on OpenUI Lang 1.5 and rewriting the parser. Keeping meta.unresolved as-is and removing .finalize() sounds good to me. I will be adding some opt-in flags/features there, so I can incorporate this as part of that work.

@AbhinRustagi
Copy link
Copy Markdown
Contributor Author

@Aditya-thesys Okay, I will update the PR to remove .finalize(), keep .meta.unresolved as is, and update the error signature to a generalized OpenUIError. Do you reckon I should update the docs for now to address the current error monitoring methodologies, unless OpenUI Lang 1.5 is close to completion?

@Aditya-thesys
Copy link
Copy Markdown
Contributor

yes it is close, also i am not sure where will you add error handling in the documentation,react-lang api-reference?

@AbhinRustagi
Copy link
Copy Markdown
Contributor Author

I was thinking of adding a Guides section to OpenUI page because the error handling is supposed to be suggestive and a how-to of how the library offers visibility on errors, and not a strict mandate.

@Aditya-thesys
Copy link
Copy Markdown
Contributor

makes sense, but we haven't documented much about our parser. We expect devs to use Renderer directly.

The issue is that Renderer don't take any callbacks like onError to notify dev if anything went wrong, we just log currently link, this is in our backlog to improve our logging/error handling but paused due to ongoing changes for openui lang 1.5.
Can you for now update the api-reference please.

AbhinRustagi and others added 2 commits March 18, 2026 18:18
…UIError type

Remove finalize() and unresolved-ref error promotion per reviewer feedback.
Rename validationErrors → errors, use type+code discriminated union for
forward-compatible error taxonomy. Update tests and api-reference docs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@AbhinRustagi
Copy link
Copy Markdown
Contributor Author

@Aditya-thesys Updated the PR to the latest discussion

Aditya-thesys
Aditya-thesys previously approved these changes Mar 20, 2026
Copy link
Copy Markdown
Contributor

@Aditya-thesys Aditya-thesys left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lg, thanks for the contribution @AbhinRustagi 🙌
please run the lint, build is failing due to it.

@AbhinRustagi
Copy link
Copy Markdown
Contributor Author

@Aditya-thesys fixed ✅

@Aditya-thesys Aditya-thesys merged commit e483dad into thesysdev:main Mar 22, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve Parser Validation

2 participants