{"id":16367299,"url":"https://github.com/medz/ovo","last_synced_at":"2025-04-04T17:08:22.796Z","repository":{"id":62526639,"uuid":"72900085","full_name":"medz/OvO","owner":"medz","description":"OvO is a Dart-first schema declaration and validation library.","archived":false,"fork":false,"pushed_at":"2024-10-10T19:43:40.000Z","size":49,"stargazers_count":178,"open_issues_count":3,"forks_count":39,"subscribers_count":25,"default_branch":"main","last_synced_at":"2024-10-12T02:49:28.548Z","etag":null,"topics":["dart","flutter","json-schema","runtime-validation","schema","schema-validator","validate"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/ovo","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/medz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-11-05T02:25:27.000Z","updated_at":"2024-08-17T16:05:47.000Z","dependencies_parsed_at":"2024-04-11T18:43:36.608Z","dependency_job_id":"5e0db7bf-7a49-47d3-9b04-8ee4f7202fb3","html_url":"https://github.com/medz/OvO","commit_stats":null,"previous_names":["medz/fans","medz/phpwind","medz/ovo","medz/heroicons"],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medz%2FOvO","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medz%2FOvO/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medz%2FOvO/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/medz%2FOvO/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/medz","download_url":"https://codeload.github.com/medz/OvO/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247217184,"owners_count":20903009,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dart","flutter","json-schema","runtime-validation","schema","schema-validator","validate"],"created_at":"2024-10-11T02:49:33.552Z","updated_at":"2025-04-04T17:08:22.780Z","avatar_url":"https://github.com/medz.png","language":"Dart","funding_links":["https://github.com/sponsors/odroe","https://opencollective.com/openodroe","https://www.patreon.com/user?u=80114587"],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003ch1 align=\"center\"\u003e✨ OvO ✨\u003c/h1\u003e\n    \u003cp align=\"center\"\u003e\n        OvO is a Dart-first schema declaration and validation library.\n    \u003c/p\u003e\n\u003c/p\u003e\n\n## Introduction\n\nOvO is a Dart-first schema declaration and validation library. We use the technical term **\"Schema\"** to define any data type, from simple single data (for example: `string`/`int`, etc.) to complex nested `Map`.\n\nOvO is designed to be as user-friendly and developer-friendly as possible, with the goal of eliminating tedious type checking and object deserialization. It is easy to compose complex data structure validation using simple declaration validation.\n\nseveral important aspects\n\n- A fun walkthrough of Dart type extensions\n- Simple and chained interface calls\n- Can be used on any Dart platform (Dart, Web, Flutter)\n\n### Sponsors\n\nI am very grateful and encouraged for any level of sponsorship, which will help me continue to develop and maintain this project.\n\n- [GitHub Sponsors](https://github.com/sponsors/odroe)\n- [Open collective](https://opencollective.com/openodroe)\n- [Patreon](https://www.patreon.com/user?u=80114587)\n\n## Installation\n\n\u003e We are more aggressive and use higher versions of Dart stable versions as much as possible.\n\n### Install from command line\n\n```bash\n# Dart project\ndart pub add ovo\n\n# Flutter project\nflutter pub add ovo\n```\n\n### Install from `pubspec.yaml`\n\n```yaml\ndependencies:\n  ovo: latest\n```\n\n## Basic Usage\n\nCreate a simple string schema:\n\n```dart\nimport 'package:ovo/ovo.dart' as ovo;\n\n// Create a schema for string.\nfinal schema = ovo.String();\n\n// Parsing\nawait schema.parse('Hello World'); // =\u003e 'Hello World'\nawait schema.parse(123); // =\u003e throws OvOException\n```\n\nCreating an JSON schema:\n\n```dart\nimport 'package:ovo/ovo.dart' as ovo;\n\nfinal schema = ovo.Object({\n    'name': ovo.String(),\n});\n\nawait schema.parse({\n    'name': 'John',\n}); // =\u003e {'name': 'John'}\n```\n\n## Types\n\nOvO provides type validation with dependent type parameters, and also built-in some common types. You can declare a type validation by `OvO\u003cT\u003e`, where `T` is a type parameter and can be any type.\n\nLet's try to create a `String` type validation:\n\n```dart\nimport 'package:ovo/ovo.dart' as ovo;\n\nfinal schema = ovo.OvO\u003cString\u003e();\n\nawait schema.parse('Hello World'); // =\u003e 'Hello World'\nawait schema.parse(123); // =\u003e throws OvOException\n```\n\nOr, we create a validation of `Record` type that is not built-in:\n\n```dart\nimport 'package:ovo/ovo.dart' as ovo;\n\nfinal schema = ovo.OvO\u003c(int, String)\u003e();\n\nawait schema.parse((123, 'Hello World')); // =\u003e (123, 'Hello World')\n```\n\nOf course, you can also use it to validate a custom class:\n\n```dart\nimport 'package:ovo/ovo.dart' as ovo;\n\nclass User {\n    final String name;\n    final int age;\n\n    User(this.name, this.age);\n}\n\nfinal schema = ovo.OvO\u003cUser\u003e();\n\nawait schema.parse(User('John', 18)); // =\u003e User('John', 18)\n```\n\nBasic type validation depends on the built-in `is` keyword in Dart, which is fully capable of most type validation. However, if you need more complex type validation, you can use the constructor of `OvO\u003cT\u003e` to create a custom type validation.\n\n```dart\nimport 'package:ovo/ovo.dart' as ovo;\n\nclass User {\n    final String name;\n    final int age;\n\n    User(this.name, this.age);\n\n    factory User.fromJson(Map\u003cString, dynamic\u003e json) {\n        return User(json['name'], json['age']);\n    }\n}\n\nclass MyOvO implements ovo.OvO\u003cUser\u003e {\n    @override\n    Future\u003cUser\u003e parse(ovo.Context, dynamic value) {\n        if (value is Map\u003cString, dynamic\u003e) {\n            return User.fromJson(value);\n        }\n        throw ovo.OvOException('Invalid value');\n    }\n}\n\nfinal schema = ovo.Object({\n    'data': MyOvO(),\n    'status': ovo.Boolean(),\n});\n\nfinal data = {\n    'data': {\n        'name': 'John',\n        'age': 18,\n    },\n    'status': true,\n};\n\nawait schema.parse(data); // =\u003e {'data': User('John', 18), 'status': true}\n```\n\n### Any\n\n`Any` type validation can accept a non-`null` value of any type. It is an alias of `OvO\u003cObject\u003e`.\n\n### Array\n\n`Array` type validation can accept an iterable value (for example: `List`, `Set`, `Iterable`, etc.). It is not an alias of `OvO\u003cIterable\u003cT\u003e\u003e`, but a specific type validation.\n\n`Array` accepts a parameter of type `OvO\u003cT\u003e` to validate each element in the array.\n\n```dart\nimport 'package:ovo/ovo.dart' as ovo;\n\nfinal schema = ovo.Array(ovo.String());\n\nawait schema.parse(['Hello', 'World']); // =\u003e ['Hello', 'World']\nawait schema.parse([123, 456]); // =\u003e throws OvOException\n```\n\n#### `.min`/`.max`/`.size`\n\n`.min`/`.max`/`.size` have the same parameters and type signatures, and they all accept a parameter of type `int` to validate the length of the array.\n\n- `.min` validates that the length of the array must be greater than or equal to the specified length.\n- `.max` validates that the length of the array must be less than or equal to the specified length.\n- `.size` validates that the length of the array must be equal to the specified length.\n\n```dart\novo.Array(ovo.String()).min(2); // must contain at least 2 elements\novo.Array(ovo.String()).max(2); // must contain no more than 2 elements\novo.Array(ovo.String()).size(2); // must contain exactly 2 elements\n```\n\n#### `.unique`\n\n`.unique` validates that the elements in the array must be unique, similar to `Set` in Dart.\n\n```dart\novo.Array(ovo.String()).unique(); // must contain unique elements\n```\n\n### Boolean\n\n`Boolean` type validation can accept a value of type `bool`. It is an alias of `OvO\u003cbool\u003e`.\n\n另外，他还有两个额外的扩展方法：\n\nIn addition, it has two additional extension methods:\n\n- `.isTrue` - 验证值必须为 `true`。\n- `.isFalse` - 验证值必须为 `false`。\n\n- `.isTrue` - validates that the value must be `true`.\n- `.isFalse` - validates that the value must be `false`.\n\n```dart\novo.Boolean(); // must be a boolean, `true` or `false`\novo.Boolean().isTrue(); // must be true\novo.Boolean().isFalse(); // must be false\n```\n\nOf course, you can use `OvO\u003cbool\u003e` instead of `Boolean` type validation, but `Boolean` type validation is more semantic.\n\n### Number\n\n`Number` type validation can accept a value of type `num`. It is an alias of `OvO\u003cnum\u003e`.\n\n```dart\novo.Number(); // must be a number, `int` or `double`\n```\n\nIf you need to validate an integer, you can use the `Integer` type validation, which is an alias of `OvO\u003cint\u003e`. To validate a floating-point number, you can use the `Double` type validation, which is an alias of `OvO\u003cdouble\u003e`.\n\n`Integer` and `Double` are both subtypes of `Number`:\n\n```dart\novo.Integer(); // must be an integer, `int`\novo.Double(); // must be a double, `double`\n```\n\n`OvO\u003cnum\u003e` also contains some additional methods:\n\n```dart\novo.Number().gt(10); // must be greater than 10\novo.Number().gte(10); // must be greater than or equal to 10\novo.Number().lt(10); // must be less than 10\novo.Number().lte(10); // must be less than or equal to 10\novo.Number().finite(); // must be finite\novo.Number().negative(); // must be negative\n```\n\n### String\n\n`String` type validation can accept a value of type `String`. It is an alias of `OvO\u003cString\u003e`.\n\n```dart\novo.String(); // must be a string, `String`\n```\n\n`OvO\u003cString\u003e` also contains some additional methods:\n\n```dart\n// Validations\novo.String().min(5); // must be at least 5 characters long\novo.String().max(5); // must be no more than 5 characters long\novo.String().length(5); // must be exactly 5 characters long\novo.String().regex(RegExp(r'^[a-z]+$')); // must match the regular expression\novo.String().contains('abc'); // must contain the substring\novo.String().isNotEmpty(); // must not be empty\novo.String().startsWith('abc'); // must start with the substring\novo.String().endsWith('abc'); // must end with the substring\novo.String().equals('abc'); // must be equal to the string\n\n// Transformations\novo.String().trim(); // trim whitespace\novo.String().toLowerCase(); // convert to lowercase\novo.String().toUpperCase(); // convert to uppercase\n```\n\n### Object\n\n`Object` type validation can accept a value of type `Map`. It is an implementation of `OvO\u003cMap\u003cString, T\u003e\u003e`.\n\n```dart\nimport 'package:ovo/ovo.dart' as ovo;\n\nfinal user = ovo.Object({\n    'name': ovo.String(),\n    'age': ovo.Number(),\n});\n\nawait user.parse({\n    'name': 'John',\n    'age': 18,\n}); // =\u003e {'name': 'John', 'age': 18}\n\nawait user.parse({\n    'name': 'John',\n    'age': '18',\n}); // =\u003e throws OvOException\n```\n\nOf course, if you just want to simply validate a value of type `Map\u003cK, T\u003e`, you can use `OvO\u003cMap\u003cK, T\u003e\u003e` instead of `Object` type validation.\n\n```dart\nimport 'package:ovo/ovo.dart' as ovo;\n\nfinal user = ovo.OvO\u003cMap\u003cString, dynamic\u003e\u003e();\n\nawait user.parse({\n    'name': 'John',\n    'age': 18,\n}); // =\u003e {'name': 'John', 'age': 18}\n\nawait user.parse({\n    'name': 'John',\n    'age': '18',\n}); // =\u003e {'name': 'John', 'age': '18'}\n```\n\n## Functional\n\n### `.nullable`\n\nThe `.nullable` method can convert a type validation to a type validation that accepts `null`.\n\n```dart\novo.String().nullable(); // must be a string or null\n```\n\n### `.refine`\n\n`.refine` is a method that allows you to customize the validation. It accepts a validation function of `FutureOr\u003cbool\u003e Function(T data)` to facilitate validation according to the actual situation.\n\n```dart\novo.String().refine(\n    (value) =\u003e value.length \u003e 5,\n    message: 'must be greater than 5',\n); // must be a string and length greater than 5\n```\n\n\u003e It is worth noting that many of the built-in extension methods are implemented based on the `.refine` method.\n\n### `.transform`\n\n`.transform` is a method that allows you to customize the method of converting data types. It works on the principle of **Onion Model**.\n\n#### Pre-transformation\n\nPre-transformation allows you to pre-process the raw data to be parsed, and then hand it over to the next converter, and finally hand it over to the type validator for verification:\n\n```dart\n\nfinal schema = ovo.String().transform(\n    (ovo.Context context, dynamic data, Future\u003cT\u003e Function(dynamic data) next) {\n        // data convert to string\n        return next(data.toString());\n    }\n);\n\nawait schema.parse(123); // =\u003e '123'\nawait schema.parse(#symbol); // =\u003e '#symbol'\n```\n\n#### Post-transformation\n\nUsing the `next` parameter in the callback, you can perform post-transformation of the data after the type validator is verified successfully:\n\n```dart\nfinal schema = ovo.String().transform(\n    (ovo.Context context, dynamic data, Future\u003cT\u003e Function(dynamic data) next) async {\n        final value = await next(data);\n\n        // value convert to int\n        return int.parse(value);\n    }\n);\n\nawait schema.parse('123'); // =\u003e 123\n```\n\n### `.withDefault`\n\n`.withDefault` allows a nullable value `T?` to be replaced with a default value `T` when the value is `null`.\n\n```dart\nfinal schema = ovo.String().withDefault('Hello World'); // must be a string or null, default value is 'Hello World'\n\nawait schema.parse(null); // =\u003e 'Hello World'\nawait schema.parse('Hello'); // =\u003e 'Hello'\n```\n\nAs you can see, the `.withDefault` method will automatically attach the `.nullable` method, so you don't need to call the `.nullable` method manually.\n\n## Compositions\n\n### `AnyOf` (OR)\n\n`AnyOf` type validation can accept any of the multiple type validations.\n\n```dart\novo.AnyOf([\n    ovo.String(),\n    ovo.Integer(),\n]); // must be a string or an integer\n```\n\n### `AllOf` (AND)\n\n`AllOf` type validation can accept all types in multiple type validations.\n\n```dart\novo.AllOf([\n    ovo.Double(),\n    ovo.Integer(),\n]); // must be a double and an integer\n```\n\n### `OneOf` (XOR)\n\n`OneOf` type validation can accept one type in multiple type validations. If multiple validations match, an exception is thrown.\n\n```dart\nfinal schema = ovo.OneOf([\n    ovo.String().size(5),\n    ovo.String().size(10),\n]);\n\nawait schema.parse('12345'); // =\u003e '12345'\nawait schema.parse('1234567890'); // =\u003e '1234567890'\nawait schema.parse('123456'); // =\u003e throws OvOException\n```\n\n### `Not` (NOT)\n\n`Not` type validation can accept any type validation, but if the specified type validation is matched, an exception is thrown.\n\n```dart\nfinal schema = ovo.Not(ovo.String());\n\nawait schema.parse(123); // =\u003e 123\nawait schema.parse({'name': 'Seven'}) // =\u003e {'name': 'Seven'}\nawait schema.parse('123'); // =\u003e throws OvOException\n```\n\n### `Const`\n\n`Const` type validation can accept a constant value.\n\n```dart\novo.Const(123); // must be 123\n```\n\nUsing `Const` we can implement string literals in JSON:\n\n```dart\nfinal schema = ovo.OneOf([\n    ovo.Const('mobile'),\n    ovo.Const('web'),\n]);\n\nawait schema.parse('mobile'); // =\u003e 'mobile'\nawait schema.parse('web'); // =\u003e 'web'\n```\n\nUsed in conjunction with conversion, we can implement `Enum` type validation:\n\n```dart\nenum MyEnum {\n    mobile,\n    web,\n}\n\nfinal schema = ovo.OneOf(\n    MyEnum.values.map((e) =\u003e ovo.Const(e.name)),\n).transform\u003cMyEnum\u003e(\n    (ovo.Context context, dynamic data, Future\u003cT\u003e Function(dynamic data) next) async {\n        final value = await next(data);\n\n        return MyEnum.values.firstWhere((e) =\u003e e.name == value);\n    }\n);\n\nawait schema.parse('mobile'); // =\u003e MyEnum.mobile\nawait schema.parse('web'); // =\u003e MyEnum.web\nawait schema.parse('desktop'); // =\u003e throws OvOException\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmedz%2Fovo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmedz%2Fovo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmedz%2Fovo/lists"}