luthor 1.0.0
luthor: ^1.0.0 copied to clipboard
A Dart validation library inspired by https://zod.dev with support for code generation.
1.0.0 #
- BREAKING: Require Dart 3.11 or later (Flutter 3.41 or later).
- CHORE: Drop the unused
metadependency, soluthorhas no runtime dependencies.
Validators #
- BREAKING:
lis now aValidatorFactory, and type methods exist only onl, sol.string().int()no longer compiles.l.custom(),l.customWithSchema(),l.required()andl.withName()are removed from the factory: start from a type, such asl.any().custom(...). - BREAKING: Rename
l.number()tol.num()andl.boolean()tol.bool(). - BREAKING: Validators are generic over their output type.
l.string()validates toString?, and.required()narrows it toString, so success data is typed without code generation. The typed classes areStringValidator,NumberValidator(with theIntValidator,DoubleValidatorandNumValidatortypedefs),BoolValidator,AnyValidator,NullValidator,FileValidator,ListValidator,MapValidator,SchemaValidator,UnionValidatorandOneOfValidator. The publicValidator(initialValidations:)constructor is removed. - BREAKING: Every modifier returns the validator's own type, so modifiers chain in any order:
l.string().required().min(3)compiles. - BREAKING:
withName()returns a new validator instead of renaming the receiver. - BREAKING:
custom()andcustomWithSchema()functions receive the typed value, such as aString, and are not called fornull. Their typedefs areCustomValidator<T>andSchemaCustomValidator<T>. - BREAKING:
customWithSchema()functions receiveSchemaData, a read-only map of the sibling fields with arootgetter for the outermost schema. Inside a list, they receive the data of the schema that holds the list. Outside a schema, they receive empty data instead of passing automatically. - BREAKING:
l.list(validators: [...])becomesl.list(element), with a single element validator. Usel.list(l.any())for any elements andl.list(l.union([...]))for elements that may match one of several validators. - BREAKING:
regex()takes aRegExpinstead of aString, so flags such ascaseSensitiveapply and an invalid pattern fails when the validator is built. - BREAKING: Schemas strip unknown keys from success data by default.
.passthrough()keeps them and.strict()reports anunrecognizedKeyissue for each one. - BREAKING: Validation stops after a failed type check, so
l.int().min(1).validate('5')reports one error instead of three. - BREAKING:
l.nullValue()has no.required(), since a required value can never benull. - BREAKING: Stop exporting the
Validationclass, the concrete*Validationclasses and internal members (validations,schemaValidation,hasRequiredValidation,setSchemaDataForValidations,validateValueWithFieldName,validateSchemaWithFieldNameand thevalidatingSchemasparameters).forwardRef()returns a publicValidatorReference<O>. - FEAT: Add
l.union([...]), which accepts a value that passes any of its options (closes #24, #25 and #26). - FEAT: Add
l.oneOf([...]), which accepts only a fixed list of values, such as the serialized values of an enum. - FEAT: Add
.finite()to number validators.NaNand infinity are doubles, so they passl.double()andl.num()unless.finite()is present. - FEAT: Add an
accept:predicate tol.file()for package types such asXFile. - FEAT: Add
messageandmessageBuildertol.schema()for the "must be a map" error. - FEAT:
ValidationFailure.toString()shows object-level errors, whose error path is'', under(root), so they no longer print as{: [message]}.errorsandgetError('')are unchanged. - FEAT: Add
l.maxDepth(default 512). Input nested deeper fails with atooDeepissue.
Results and messages #
- BREAKING: Replace
SingleValidationResultandSchemaValidationResult, and theirSuccessandErrorvariants, with one sealedValidationResult<T>:ValidationSuccess<T>(data)orValidationFailure<T>(input, issues). - BREAKING: Rename
validateValue(value)tovalidate(input). It accepts any input and never throws. - BREAKING:
validateSchema(input, fromJson:)requiresfromJsonand accepts any input. WithoutfromJson, usevalidate, whose data is typed asMap<String, Object?>. AfromJsonthat throws now produces afromJsonFailedissue instead of an exception, andnullinput produces arequiredissue. - BREAKING: Errors are
ValidationIssues, each with anIssueCodecode, a path, a message, params and a field name. The error map (errors) is derived from them and is flat, keyed by error path such as'address.city'or'items.1.id'. The[DEFAULT],keysandvalueskeys and the"a.b: msg"strings are gone. - BREAKING: Errors from
.custom()on a schema are object-level errors at the schema's own path (''at the root), so they no longer collide with field errors. - BREAKING: List element errors are reported at the element's index. Map value errors are reported at the key's path, and map key errors are
invalidKeyissues at the map's path. - BREAKING:
getError(path)returns the first error at exactly that path, andnullwhen there is none. It never throws. - BREAKING: Replace
messageFn: String? Function()withmessageBuilder: String Function(ValidationIssue issue)on every method. The issue carries the default message, code, path, field name and params.message:stays as a plain-string shorthand. - BREAKING: Default messages change in a few places: "must be a Map" is now "must be a map", the regex message names the pattern, and the list message no longer says "or does not match the validations".
- FEAT: Add
l.messageBuilder, one global hook that builds every message without a per-validationmessageormessageBuilder, for example for i18n. - FEAT: Add
messages,errors,getError()andgetErrors()to every result, so they can be read without matching on the result first.
Annotations #
- BREAKING: Merge
HasMinDouble,HasMinNumber,HasMaxDoubleandHasMaxNumberintoHasMinandHasMax, which take anum. On aStringfield the value is the length; on a number field it is the value. - BREAKING: Replace the
messageFnparameter of every annotation withmessageBuilder. It must be a top-level or static function, since annotation arguments are constants. - BREAKING:
WithCustomValidatortakes abool Function(Never value)andWithSchemaCustomValidatorabool Function(Never value, SchemaData data), so typed functions such asbool isEven(int value)can be used. - BREAKING: Annotation classes are
final. - FEAT: Add the
isIpconstant. - FEAT: Add
caseSensitive,multiLine,unicodeanddotAlltoMatchRegex, andaccepttoIsFile. - DEPRECATE: Deprecate
LuthorForwardRefand@luthorForwardRef.luthor_generatorwraps every nested schema reference inforwardRef(), so the annotation has no effect. Remove it.
Fixes #
- FIX: Validate every level of recursive schemas built with
forwardRef()or mutual references, instead of skipping nested levels. - FIX: Keep a parent's errors when a schema recurses through a list; validations no longer store state between calls.
- FIX: Return a
tooDeepissue for deeply nested input instead of throwing aStackOverflowError. - FIX: Report errors for each list element instead of one generic message, and use the field name in list messages.
- FIX: Stop object-level schema errors from colliding with field errors or throwing a cast error.
- FIX: Match IP addresses against the whole string, and accept
::1and::. - FIX: Make error shapes independent of a map's runtime type arguments, so
Map<dynamic, dynamic>input behaves likeMap<String, Object?>. - FIX: Stop
customWithSchema()from reusing data from an earlier validation. - FIX: Use the field name in the "must be a map" message of nested schemas.
- FIX: Pick "a" or "an" by the type name in the message for a map key or value that fails its type argument, so
l.map<String, int>()reportsvalue must be an intinstead ofvalue must be a int. - FIX: Distinguish map key errors from value errors, and keep entry errors instead of replacing them with a
.custom()error. Custom checks on schemas, lists, maps and unions now run only when every child passed. - FIX: Honour the message builder in
ip(). - FIX: Anchor the
cuid()pattern at the start of the string. - FIX: Reject impossible dates and times in
dateTime(), such as2023-02-30and25:00, and strings outside the ISO 8601 extended format. - FIX: Require a scheme in
uri(), and compare allowed schemes case-insensitively. - FIX: Detect emoji with Unicode properties, so punctuation, currency and arrows fail and
❤️and1️⃣pass. - FIX: Detect files structurally in
l.file(), so files pass in minified web builds and classes named likeUserProfilefail. - FIX: Throw an
ArgumentErrorfor negative lengths inmin(),max()andlength(), also in release builds. - FIX: Compile the built-in patterns once instead of on every validation.
- FIX: Correct wrong documentation comments.
Migrating from 0.x #
| 0.x | 1.0 |
|---|---|
l.number() |
l.num() |
l.boolean() |
l.bool() |
v.validateValue(x) |
v.validate(x) |
schema.validateSchema(map) |
schema.validate(map) |
schema.validateSchema<User>(map, fromJson: User.fromJson) |
schema.validateSchema(map, fromJson: User.fromJson) |
SingleValidationResult<T>, SchemaValidationResult<T> |
ValidationResult<T> |
SingleValidationSuccess(data:), SchemaValidationSuccess(data:) |
ValidationSuccess(data) |
SingleValidationError(errors:) |
ValidationFailure(messages:) |
SchemaValidationError(errors:) |
ValidationFailure(errors:) |
(result as SchemaValidationError).getError('a.b') |
result.getError('a.b') |
errors['[DEFAULT]'] |
result.getErrors('') |
l.list(validators: [v]) |
l.list(v) |
l.list(validators: [a, b]) |
l.list(l.union([a, b])) |
l.list() |
l.list(l.any()) |
l.custom(f), l.required() |
l.any().custom(f), l.any().required() |
l.withName('x').string() |
l.string().withName('x') |
l.string().regex(r'^\d+$') |
l.string().regex(RegExp(r'^\d+$')) |
messageFn: () => 'text' |
messageBuilder: (issue) => 'text' |
custom((Object? value) => ...) |
custom((value) => ...), where value has the validator's type |
Validator v = l.string() |
StringValidator v = l.string() |
@HasMinDouble(1.5), @HasMinNumber(1) |
@HasMin(1.5), @HasMin(1) |
@HasMaxDouble(1.5), @HasMaxNumber(1) |
@HasMax(1.5), @HasMax(1) |
@IsEmail(messageFn: f) |
@IsEmail(messageBuilder: f), where f takes a ValidationIssue |
@MatchRegex(r'...') |
unchanged; the generator builds the RegExp |
0.18.0 #
- FIX: Keep all string and number modifier chains immutable so reusable validators are not mutated by later
.email(),.uuid(),.min(),.max(), and related calls. - FIX: Preserve
withName()across typed validators and single-value validation so generated default messages use the configured field name. - FIX: Treat
l.file()as optional for null values unless.required()is also present, matching other single-value validators. - FIX: Avoid printing caught custom-validator exceptions while still treating thrown validators as validation failures.
- FIX: Return flattened errors for field-named map key/value validation failures instead of throwing when structured map errors are produced.
0.17.0 #
- FEAT: Add support for validating files with
l.file()validator.
0.16.0 #
- FEAT: Update version to match luthor_generator.
0.15.0 #
- FEAT: Update version to match luthor_generator.
0.14.0 #
- FEAT: Add support for validating map keys and values using
keyValidatorandvalueValidatorparameters inl.map(). Map validation errors are structured as{'keys': {'key1': ['error', 'messages']}, 'values': {'key1': ['error', 'messages']}}, preventing collisions when maps contain keys named'keys'or'values'. - FEAT: Add support for
forwardRef()function to handle self-referential validators. This prevents stack overflow errors when defining schemas that reference themselves (e.g., aNodeclass withList<Node>? children). TheforwardRef()function defers validator resolution until validation time, allowing recursive schema definitions. - FEAT: Add
ValidatorReferenceinterface to unifyValidatorandForwardReftypes, enabling both to be used interchangeably in validation contexts. - FEAT: Add
@luthorForwardRefannotation to mark a field as using a forward reference, to be used in code generation. - FEAT: Add
@IsUuid/@isUuid,@IsCuid/@isCuid,@IsCuid2/@isCuid2, and@IsEmoji/@isEmojiannotations for string validation. These annotations can be used with code generation to validate UUID, CUID, CUID2, and emoji strings respectively.
0.13.0 #
- FEAT: Add
messageFnparameter to all validation methods and annotations, allowing dynamic error message generation through top-level functions.
0.12.0 #
- FEAT: Update dependencies.
0.11.0 #
- FEAT: Add schema-aware custom validation with
customWithSchema()method for cross-field validation scenarios like password confirmation. - FEAT: Add
WithSchemaCustomValidatorannotation for code generation support of cross-field validation.
0.10.0 #
- FEAT: Add support for generating type safe error keys in luthor_generator.
0.9.0 #
- BREAKING: Validators are now immutable - chaining methods like
.required()returns new instances instead of mutating the original validator. This fixes issues where reusing validators would have unexpected side effects. - FIX: Fix schema validation to properly skip validation for missing optional fields.
0.6.1 #
Note: This release has breaking changes.
- FIX: failedMessage not set is value is not a Map for SchemaValidation (#108).
0.6.0 #
Note: This release has breaking changes.
- BREAKING FEAT(luthor_generator): Add support for Freezed 3.0 (#106).
0.5.2+1 #
- FEAT: Export validations.
0.4.4 #
- FEAT: Updated dependencies.
0.4.1 #
- FEAT: deprecate luthor_annotation (#77).
- FEAT: Deprecate luthor_annoation and add annotations to luthor package.
0.4.0 #
- FEAT: Deprecate luthor_annoation and add annotations to luthor package.
0.3.1+1 #
0.3.0 #
0.2.2 #
- FIX: redundant calls fromJson in SchemaValidationError.
- FIX(luthor): SchemaValidation null error due to covariant.
- FIX(luthor): l.list() does not validate inner values correctly.
- FEAT(luthor): add fromJson argument to validateSchema.
0.2.1 #
- FIX: redundant calls fromJson in SchemaValidationError.
- FIX(luthor): SchemaValidation null error due to covariant.
- FIX(luthor): l.list() does not validate inner values correctly.
- FEAT(luthor): add fromJson argument to validateSchema.
0.2.0 #
- Upgrade minimum Dart SDK version to 3.0.0
validateandvalidateSchemanow return sealed classes instead of Freezed unions- Add
fromJsonargument tovalidateSchemato allow for custom JSON deserialization - Fix a bug where
l.list()does not validated inner values correctly (#33) - Fix a bug where nested schemas throw a null error when the data is empty (#41)
0.1.6 #
- Added better documentation and examples
0.1.5 #
- Fixed a bug where errors from previous validations are persisted
0.1.3 #
- Fixes with previous release
0.1.1 #
- Add support for validating emojis with
l.string().emoji() - Add support for validating uuids with
l.string().uuid() - Add support for validating cuids with
l.string().cuid()andl.string().cuid2() - Add support for validating regexs with
l.string().regex()andl.string().cuid2() - Add better errors
0.1.0 #
- (Breaking change) Migrate
ValidationResultto freezed's union type for better type safety
0.0.2 #
- Add string validation for Uris
- Add list validation
0.0.1+2 #
- Export
ValidatorandStringValidatorclasses
0.0.1+1 #
- Add more examples to README.md
0.0.1 #
- Initial release