feat: implement custom validators for form authors#417
Merged
Mishael-2584 merged 6 commits intodevfrom Mar 2, 2026
Merged
Conversation
- Add CustomValidatorLoader, Registry, and Executor services - Extend FormInitData interface to include validators in manifest - Scan validators folder in FormplayerModal and include in manifest - Load validators during form initialization - Execute validators on data changes in JSON Forms lifecycle - Convert validator errors to AJV-compatible format - Merge custom errors with standard validation via additionalErrors - Support cross-field validation via full form data access - Implement graceful failure handling Implements all 8 user stories: - US01: Define custom validators as JS modules - US02: Reference validators in UI schema options - US03: Execute in JSON Forms lifecycle - US04: Provide full form context to validators - US05: Return standardized validation errors - US06: Display custom validation messages - US07: Support reusable validators with different configs - US08: Graceful validator failure handling
- Remove unused extractCustomValidators and executeCustomValidators imports from App.tsx - Remove unused CustomValidatorFunction import from CustomValidatorExecutor.ts
…taEnsemble/ode into feature/custom-validators
najuna-brian
approved these changes
Mar 2, 2026
Contributor
najuna-brian
left a comment
There was a problem hiding this comment.
Approved to be part of the code freeze
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Description
This PR implements a comprehensive custom validators feature that enables form authors to define reusable JavaScript validation logic that integrates seamlessly with the JSON Forms validation lifecycle. This feature extends the existing custom question types architecture and provides a flexible, type-safe way to add custom validation rules to forms.
Key Features
validators/folder and includes source code in manifestImplementation Highlights
React Native Side (formulus):
FormInitDatainterface to includevalidatorsin manifestFormplayerModal.tsxto read validator source code fromvalidators/folderFormplayer Side (formulus-formplayer):
CustomValidatorLoader.tsto load and evaluate validator modules from manifestCustomValidatorRegistry.tsas singleton registry for loaded validatorsCustomValidatorExecutor.tsto extract validators from UI schema and execute themCustomValidatorContract.tsto define validator function interfaceApp.tsxduring form initializationhandleDataChangecallbackadditionalErrorspropUser Stories Implemented
✅ US01: Define custom validators as JavaScript modules registered in manifest
✅ US02: Reference validators in UI schema via
options.customValidatorsarray✅ US03: Execute validators in JSON Forms lifecycle alongside AJV validation
✅ US04: Provide full form context (data, value, path, config, ajv) to validators
✅ US05: Return standardized validation errors in AJV-compatible format
✅ US06: Display custom validation messages like standard validation errors
✅ US07: Support reusable validators with different configurations per field
✅ US08: Implement graceful failure handling for validator errors
Type of Change
Component(s) Affected
Related Issue(s)
Closes/Fixes/Resolves:
Testing
Testing Notes:
Breaking Changes
If breaking changes, please describe migration steps:
N/A - This is a new feature that extends existing functionality without breaking existing code.
Documentation Updates
Documentation Added:
CUSTOM_VALIDATORS_USAGE.md: Comprehensive guide for form authors on defining and using custom validatorsCUSTOM_VALIDATORS_IMPLEMENTATION_PLAN.md: Detailed implementation documentation covering all 8 user storiesFiles Changed
New Files
formulus-formplayer/src/services/CustomValidatorLoader.ts- Loads and evaluates validator modulesformulus-formplayer/src/services/CustomValidatorRegistry.ts- Registry for loaded validatorsformulus-formplayer/src/services/CustomValidatorExecutor.ts- Executes validators in JSON Forms lifecycleformulus-formplayer/src/types/CustomValidatorContract.ts- Validator function interface definitionModified Files
formulus-formplayer/src/App.tsx- Integrated validator loading and executionformulus-formplayer/src/types/FormulusInterfaceDefinition.ts- Extended FormInitData interfaceformulus/src/components/FormplayerModal.tsx- Added validator scanning logicformulus/src/webview/FormulusInterfaceDefinition.ts- Extended FormInitData interface (source of truth)Example Validators (AnthroCollect)
validators/isAdult/index.js- Age validation examplevalidators/matchesRegex/index.js- Pattern matching examplevalidators/passwordMatch/index.js- Cross-field validation examplevalidators/rangeValidator/index.js- Range validation exampleChecklist
Usage Example
Defining a Validator
javascript
// validators/isAdult/index.js
export default function validate({ data, value, path, config }) {
const minAge = config.minAge || 18;
if (typeof value !== 'number' || value < minAge) {
return [{
path,
message: Must be at least ${minAge} years old,
keyword: 'minimumAge'
}];
}
}
Using in UI Schema
{
"type": "Control",
"scope": "#/properties/age",
"options": {
"customValidators": [
{
"name": "isAdult",
"config": { "minAge": 18 }
}
]
}
}