{"id":15509125,"url":"https://github.com/x-slayer/ezvalidator","last_synced_at":"2025-04-23T02:27:18.231Z","repository":{"id":50624814,"uuid":"408517900","full_name":"X-SLAYER/EzValidator","owner":"X-SLAYER","description":"Dead simple field/object schema validation for flutter inspired by Yup","archived":false,"fork":false,"pushed_at":"2024-10-12T08:08:45.000Z","size":399,"stargazers_count":19,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-12-18T10:41:29.171Z","etag":null,"topics":["dart","flutter","form-validation","form-validator","object-validator","validation","yup","yup-validation"],"latest_commit_sha":null,"homepage":"https://pub.dev/packages/ez_validator","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/X-SLAYER.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":"2021-09-20T16:27:19.000Z","updated_at":"2024-11-07T09:01:45.000Z","dependencies_parsed_at":"2023-12-21T01:03:44.842Z","dependency_job_id":"31e1d6c1-fdbe-45ef-8afe-6805893f29da","html_url":"https://github.com/X-SLAYER/EzValidator","commit_stats":{"total_commits":79,"total_committers":1,"mean_commits":79.0,"dds":0.0,"last_synced_commit":"8dd13a4d4f589f84d07e521032ea1a16f92fc2bb"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X-SLAYER%2FEzValidator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X-SLAYER%2FEzValidator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X-SLAYER%2FEzValidator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/X-SLAYER%2FEzValidator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/X-SLAYER","download_url":"https://codeload.github.com/X-SLAYER/EzValidator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250356645,"owners_count":21417122,"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","form-validation","form-validator","object-validator","validation","yup","yup-validation"],"created_at":"2024-10-02T09:41:26.633Z","updated_at":"2025-04-23T02:27:18.210Z","avatar_url":"https://github.com/X-SLAYER.png","language":"Dart","readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"#\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/X-SLAYER/EzValidator/master/ez_validator_logo.png\" height=250 /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# Ez Validator\n\nEzValidator offers a dead-simple approach to field and object schema validation tailored for Flutter. Inspired by the intuitive API of [Yup](https://github.com/jquense/yup), EzValidator simplifies the process of defining and enforcing data schemas within your Flutter applications.\n\n## Key Features of EzValidator\n\n- **Flutter Schema Builder**: Seamlessly integrate EzValidator into your Flutter projects to build and manage data validation schemas.\n- **Versatile Validation**: Whether you need to validate individual fields or entire objects, EzValidator is designed to handle both with ease.\n\n## Installing\n\nAdd EzValidator to your pubspec:\n\n```yaml\ndependencies:\n  ez_validator: any # or the latest version on Pub\n```\n\n## Getting Started\n\nTo begin with `EzValidator`, define a schema object that represents the structure and validation rules for your data. Below is an example demonstrating how to create a schema for user data, including validations for email, password, and date fields.\n\n### Defining a Schema\n\nCreate a schema using `EzSchema.shape` where each field in the data object is associated with an `EzValidator` specifying its validation rules:\n\n```dart\nfinal EzSchema userSchema = EzSchema.shape(\n  {\n    \"email\": EzValidator\u003cString\u003e(label: \"Email\").required().email(),\n    \"password\": EzValidator\u003cString\u003e(label: 'Password').required().minLength(8),\n    'date': EzValidator\u003cDateTime\u003e()\n        .required()\n        .date()\n        .minDate(DateTime(2019))\n        .maxDate(DateTime(2025)),\n  },\n);\n```\n\n### Validating Data\n\nUse the `catchErrors` method of the schema to validate a data object. This method returns a map of validation errors, if any:\n\n```dart\nfinal errors = userSchema.catchErrors({\n  'email': 'example@domain.com',\n  'password': '12345678',\n  'date': DateTime.now(),\n});\n\nprint(errors);\n\n```\n\n### Understanding the Output\n\n- If there are validation errors, `errors` will contain a map of field names to error messages. For example:\n\n```dart\n{\n  \"password\": \"Minimum six characters, at least one letter, one number and one special character\",\n  \"age\": \"The field must be greater than or equal to 18\"\n}\n\n```\n\n- If the data object is valid according to the schema, `errors` will be an empty map (`{}`).\n\n---\n\nAdditionally, use the `validateSync` method to validate data and simultaneously retrieve the processed data along with any errors:\n\n```dart\nfinal (data, errors) = userSchema.validateSync({\n  'email': 'example@domain.com',\n  'password': '12345678',\n  'date': DateTime.now(),\n});\n\nprint(data);   // Processed data\nprint(errors); // Validation errors\n\n```\n\n### Understanding the Output\n\n- If there are validation errors, the `errors` map will contain field names and their corresponding error messages.\n- If the data object passes all validations, `errors` will be an empty map (`{}`).\n- The `data` map returned by `validateSync` contains the processed data, which may include default values set by the schema.\n\n## Custom Validation with `addMethod`\n\n`EzValidator` also supports custom validation rules through the `addMethod` function. This feature allows you to define your own validation logic, making `EzValidator` highly adaptable to various unique use cases.\n\n### Using `addMethod` for Custom Validation\n\nYou can use `addMethod` to add custom validation functions to your validator. Each function should take the value to be validated as an argument and return `null` if the value passes the validation or an error message if it fails.\n\nHere's an example of using `addMethod` to validate a JSON structure:\n\n```dart\nfinal checkJson = EzValidator\u003cMap\u003cString, dynamic\u003e\u003e()\n    .addMethod((v) =\u003e v?['foo'] == 'bar' ? null : 'Foo should be bar')\n    .addMethod((v) =\u003e v?['bar'] == 'Flutter' ? null : 'Bar should be Flutter')\n    .addMethod((v) =\u003e v?['items'][0] == 'a' ? null : 'First item should be a')\n    .build();\n\nfinal errors = checkJson({\n  'foo': 'bar',\n  'bar': 'Flutter',\n  'items': ['a', 'b', 'c']\n});\n\nprint(errors); // Outputs the validation errors, if any\n\n```\n\nIf any of these checks fail, the corresponding error message is returned.\n\n### Flexibility of Custom Validation\n\nThe `addMethod` function opens up endless possibilities for custom validation logic, allowing `EzValidator` to be tailored to your specific validation needs.\n\n# Direct Use in Flutter Widgets\n\n`EzValidator` is designed to integrate smoothly with Flutter widgets, providing a straightforward way to add validation to user inputs. One common use case is within forms, where you can directly use `EzValidator` in form fields such as `TextFormField`.\n\n### Example: Email Validation in a TextFormField\n\nHere's an example of how to apply `EzValidator` for email validation in a `TextFormField`:\n\n```dart\nTextFormField(\n  validator: EzValidator\u003cString\u003e()\n      .required()\n      .email()\n      .build(),\n  decoration: InputDecoration(labelText: 'Email'),\n),\n\n```\n\nIf the input fails these validations, the corresponding error message is displayed under the `TextFormField`.\n\n# Validation Methods\n\n`EzValidator` offers a wide range of validation methods to suit different data types and validation scenarios. Below is a breakdown of these methods:\n\n- ### General/Common Validations\n\n  - **`.required([String? message])`**: Ensures that the value is not null or empty. This is a fundamental validation to check for the presence of a value.\n  - **`.isType(Type type, [String? message])`**: Validates that the value matches the specified `type`. This method is useful for type checking in more dynamic contexts.\n  - **`.minLength(int minLength, [String? message])`**: Checks that the length of the value (String, List, or Map) is not less than the specified `minLength`.\n  - **`.maxLength(int maxLength, [String? message])`**: Ensures that the length of the value (String, List, or Map) does not exceed the specified `maxLength`.\n  - **`.addMethod(bool Function(T? v) validWhen, [String? message])`**: Allows for the addition of custom validation logic. If the provided function `validWhen` returns `false`, the custom error message is returned.\n  - **`.when(ValidationCallback\u003cT\u003e validator)`**: Provides conditional validation based on the value of another field in the schema. The method accepts a `ref`, which refers to another field in the schema, and a `validator`, which is a function that executes the validation logic. The `validator` function should return `null` if the validation passes or a custom error message if it fails. This method is particularly useful for scenarios where the validation of one field depends on the value of another field, such as confirming a password.\n  - **`.dependsOn({required bool Function(Map\u003cdynamic, dynamic\u003e? ref) condition, required EzValidator\u003cT\u003e then, EzValidator\u003cT\u003e? orElse})`**: Enables conditional validation based on the state of other fields within the same schema. This method requires a `condition` function, which evaluates the schema's current values and decides which validation to apply based on its return value. If the condition is `true`, the validation defined in `then` is applied; otherwise, the validation in `orElse` (if provided) is applied. This is especially useful for cases where the requirement for a field is dependent on another field's value, offering a flexible way to implement complex validation logic within your schema.\n  - **`.transform(T Function(T) transformFunction)`**: Applies a transformation function to the field's value before any validation is performed. The method takes a `transformFunction` which receives the current field value and returns a transformed value. This method is useful for preprocessing the data, such as trimming strings, converting types, or formatting values, before applying the validation rules.\n  - **`.arrayOf\u003cEzValidator\u003cT\u003e\u003e(EzValidator\u003cT\u003e itemValidator)`**: when you have a list of items that need to be individually validated. This method is ideal for scenarios like validating a list of user inputs, where each input must pass certain validation criteria.\n  - **`.schema\u003cEzSchema\u003e(EzSchema schema)`**: is ideal for nested or complex data structures where multiple fields need to be validated in relation to each other. It's particularly useful in cases where you need to enforce a specific data format, such as validating JSON objects, complex forms, or data models.\n  - **`.union(List\u003cEzValidator\u003e validators)`**: Used for composing \"OR\" types by combining multiple validators into a union\n\n- ### String Validations\n\n  - **`.email([String? message])`**: Validates if the value is a valid email address.\n  - **`.phone([String? message])`**: Validates if the value is a valid phone number.\n  - **`.ip([String? message])`**: Validates if the value is a correct IPv4 address.\n  - **`.ipv6([String? message])`**: Validates if the value is a correct IPv6 address.\n  - **`.url([String? message])`**: Validates if the value is a valid URL address.\n  - **`.uuid([String? message])`**: Validates if the value is a valid UUID.\n  - **`.lowerCase([String? message])`**: Checks if the value is in lowercase.\n  - **`.upperCase([String? message])`**: Checks if the value is in uppercase.\n  - **`.matches(RegExp reg, [String? message])`**: Validates if the value matches the provided regular expression pattern.\n\n- ### Numerical Validations\n\n  - **`.min(num min, [String? message])`**: Validates if the numeric value is greater than or equal to `min`.\n  - **`.max(num max, [String? message])`**: Validates if the numeric value is less than or equal to `max`.\n  - **`.positive([String? message])`**: Validates if the numeric value is positive.\n  - **`.negative([String? message])`**: Validates if the numeric value is negative.\n  - **`.number([String? message])`**: Checks if the value is a number.\n  - **`.isInt([String? message])`**: Checks if the value is an integer.\n  - **`.isDouble([String? message])`**: Checks if the value is a double. Also validates integers, as they can be implicitly converted to doubles.\n  - **`.notNumber([String? message])`**: Checks if the value is not a number.\n\n- ### Date Validations\n\n  - **`.date([String? message])`**: Checks if the value is a valid date. If the value is a `DateTime` object or can be parsed into a `DateTime`, the validation passes.\n  - **`.minDate(DateTime date, [String? message])`**: Ensures the date value is not earlier than the specified minimum date. If the value is a `DateTime` object and is equal to or after the provided `date`, the validation passes.\n  - **`.maxDate(DateTime date, [String? message])`**: Ensures the date value is not later than the specified maximum date. If the value is a `DateTime` object and is equal to or before the provided `date`, the validation passes.\n\n- ### Boolean Validation\n\n  - **`.boolean([String? message])`**: Validates whether the value is a boolean (`true` or `false`). This method checks the data type of the value and ensures it is strictly a boolean.\n\n- ### List (Array) Validations\n\n  - **`.listOf(Type type, [String? message])`**: Validates that each element in the list is of the specified `type`. It iterates through the list and checks if each item matches the given type.\n  - **`.oneOf(List\u003cT\u003e items, [String? message])`**: Checks if the value is one of the specified items in the list. It is useful for ensuring a value is among a predefined set of options.\n  - **`.notOneOf(List\u003cT\u003e items, [String? message])`**: Ensures that the value is not one of the specified items in the list. This is the opposite of `.oneOf` and is used to exclude certain values.\n\n## Using Custom Locales with `EzValidator`\n\n`EzValidator` allows the integration of custom locales, facilitating localization of error messages. Below is an example of creating an Arabic locale (`ArLocale`) and applying it in `EzValidator`.\n\n#### Creating `ArLocale`\n\n**Define the Custom Locale**: Implement the `EzLocale` interface to create `ArLocale` with Arabic error messages.\n\n```dart\nclass ArLocale implements EzLocale {\n  const ArLocale();\n\n  // Implement all required methods with Arabic error messages\n  @override\n  String minLength(String v, int n, [String? label]) =\u003e\n      '${label ?? 'الحقل'} يجب أن يحتوي على الأقل $n أحرف';\n  // ... other method implementations ...\n\n  @override\n  String required([String? label]) =\u003e '${label ?? 'الحقل'} مطلوب';\n\n  // Example implementation for a valid email\n  @override\n  String email(String v, [String? label]) =\u003e\n      '${label ?? 'الحقل'} ليس بريدًا إلكترونيًا صحيحًا';\n\n  // ... further implementations for other methods ...\n}\n```\n\n**Set the Locale in `EzValidator`**: Configure `EzValidator` to use the `ArLocale`.\n\n```dart\nEzValidator.setLocale(const ArLocale());\n```\n\n**Use Validators as Usual**: Now, the validation error messages will be in Arabic.\n\n## Nested Validation Example with `EzValidator`\n\n`EzValidator` not only handles simple validations but also excels in managing complex, nested data structures. This is particularly useful when dealing with intricate data models, like user profiles with multiple layers of details. Here’s an example of how you can define a nested validation schema using `EzValidator`:\n\n#### Defining a Complex User Profile Schema\n\n```dart\nfinal EzSchema userProfileSchema = EzSchema.shape({\n  \"firstName\": EzValidator\u003cString\u003e().required(),\n  \"lastName\": EzValidator\u003cString\u003e().required(),\n  \"email\": EzValidator\u003cString\u003e().required().email(),\n  \"age\": EzValidator\u003cint\u003e().min(18).max(100),\n  'contactDetails': EzSchema.shape({\n    'mobile': EzValidator\u003cString\u003e()\n        .required()\n        .matches(RegExp(r'^\\+\\d{10,15}$'), 'Invalid phone number'),\n    'landline': EzValidator\u003cString?\u003e(optional: true),\n  }),\n  'address': EzSchema.shape({\n    'street': EzValidator\u003cString\u003e().required(),\n    'city': EzValidator\u003cString\u003e().required(),\n    'state': EzValidator\u003cString\u003e().required(),\n    'zipCode': EzValidator\u003cnum\u003e().required(),\n    'country': EzSchema.shape({\n      'name': EzValidator\u003cString\u003e(defaultValue: 'TUNISIA').required(),\n      'code': EzValidator\u003cString\u003e().required(),\n      'continent': EzSchema.shape({\n        'name': EzValidator\u003cString\u003e().required(),\n        'code': EzValidator\u003cString\u003e().required(),\n      })\n    }),\n  }),\n  'employment': EzSchema.shape({\n    'current': EzValidator\u003cString?\u003e(optional: true),\n    'previous': EzSchema.shape({\n      'companyName': EzValidator\u003cString\u003e().required(),\n      'position': EzValidator\u003cString\u003e().required(),\n      'years': EzValidator\u003cint\u003e().min(1).max(50),\n    }),\n  }),\n});\n\n```\n\nValidation\n\n```dart\n  final (data, errors) = userProfileSchema.validateSync({\n      'firstName': 'John',\n      'lastName': 'Doe',\n      'email': 'john.doe@example.com',\n      'age': 30,\n      'contactDetails': {\n        'mobile': '+12345678901',\n      },\n      'address': {\n        'street': '123 Main St',\n        'city': 'Anytown',\n        'state': 'Anystate',\n        'zipCode': 12345,\n        'country': { }, // I will not define the country\n      },\n      'employment': {\n        'current': 'Current Company',\n        'previous': {\n          'companyName': 'Previous Company',\n          'position': 'Previous Position',\n          'years': 5,\n        },\n      },\n    });\n\n  print(data);\n\n// Result of displayed data will contain country with default values\n// {\n//   firstName: John,\n//   lastName: Doe,\n//   email: john.doe@example.com,\n//   age: 30,\n//   contactDetails: { mobile: +12345678901, landline: null },\n//   address:\n//     {\n//       street: 123 Main St,\n//       city: Anytown,\n//       state: Anystate,\n//       zipCode: 12345,\n//       country:\n//         { name: TUNISIA, code: null, continent: { name: null, code: null } },\n//     },\n//   employment:\n//     {\n//       current: Current Company,\n//       previous:\n//         {\n//           companyName: Previous Company,\n//           position: Previous Position,\n//           years: 5,\n//         },\n//     },\n// }\n//\n\nprint(errors)\n\n// {address: {country: {code: The field is required, continent: {name: The field is required, code: The field is required}}}}\n\n\n```\n\n### Example Usage of `.when` and `.transform` and `.dependsOn` and `.union`\n\nThis example demonstrates how to use the `.when` and `.transform` methods in `EzValidator` to perform conditional validations and pre-validate data transformations.\n\n```dart\n\nfinal EzSchema schema = EzSchema.shape({\n  // Use .transform to trim whitespace before validating the name\n  \"name\": EzValidator\u003cString\u003e()\n      .transform((value) =\u003e value.trim())\n      .minLength(3, \"Name must be at least 3 characters long.\"),\n\n  // Use .when to validate confirmPassword based on the password field\n  \"password\": EzValidator\u003cString\u003e()\n      .minLength(8, \"Password must be at least 8 characters long.\"),\n  \"confirmPassword\": EzValidator\u003cString\u003e().when(\n    \"password\",\n    (confirmValue, [ref]) =\u003e\n        confirmValue == ref?[\"password\"] ? null : \"Passwords do not match\",\n  )\n});\n\nvar result = schema.validateSync({\n  \"name\": \"  John  \",\n  \"password\": \"password123\",\n  \"confirmPassword\": \"password123\",\n});\n\nprint(result); // Should be empty if no validation errors\n\n```\n\n```dart\n  /// Use .dependsOn to dynamically adjust field validation based on another field's value.\n  final EzSchema carValidationSchema = EzSchema.shape({\n    \"car_type\": EzValidator\u003cCarType\u003e(defaultValue: CarType.suv).required(),\n    \"passangers_number\": EzValidator\u003cint\u003e().dependsOn(\n      condition: (ref) =\u003e ref![\"car_type\"] == CarType.suv,\n      then: EzValidator\u003cint\u003e().required().max(6, 'Max 6 passangers'),\n      orElse: EzValidator\u003cint\u003e().required().max(4, 'Max 4 passangers'),\n    ),\n  });\n\n  final errors = carValidationSchema.catchErrors({\n    \"car_type\": CarType.suv,\n    \"passangers_number\": 7,\n  });\n\n  print(errors); // {'passangers_number' : 'Max 6 passangers'}\n\n```\n\n```dart\n  /// Use .union to compose \"OR\" types.\n  final schema = EzSchema.shape({\n    'mixedField': EzValidator().union([\n      EzValidator\u003cString\u003e().isType(String),\n      EzValidator\u003cnum\u003e().isType(num)\n    ])\n  });\n\n  schema.catchErrors({'mixedField': 'test'}) // passed\n  schema.catchErrors({'mixedField': true}) // not passed\n\n```\n\n### Example Usage of `.arrayOf` and `.schema`\n\nThis example shows how to use `.arrayOf` for validating a list of items and `.schema` for validating nested objects within a schema.\n\n```dart\n// Define a schema for individual student validation\nfinal EzSchema studentSchema = EzSchema.shape({\n  \"name\": EzValidator\u003cString\u003e().required(),\n  \"age\": EzValidator\u003cint\u003e().min(18, \"Students must be at least 18 years old.\"),\n});\n\n// Validator for validating a list of students\nfinal EzValidator\u003cList\u003cMap\u003cString, dynamic\u003e\u003e\u003e studentsListValidator =\n  EzValidator\u003cList\u003cMap\u003cString, dynamic\u003e\u003e\u003e()\n    .required()\n    .arrayOf\u003cMap\u003cString, dynamic\u003e\u003e(\n      EzValidator\u003cMap\u003cString, dynamic\u003e\u003e().schema(studentSchema),\n    );\n\n// Define a schema for a classroom, which includes a list of students\nfinal EzSchema classroomSchema = EzSchema.shape({\n  \"className\": EzValidator\u003cString\u003e().required(),\n  \"students\": studentsListValidator,\n});\n\nvar result = classroomSchema.validateSync({\n  \"className\": \"Advanced Mathematics\",\n  \"students\": [\n    {\"name\": \"John Doe\", \"age\": 20},\n    {\"name\": \"Jane Smith\", \"age\": 17}, // ---\u003e This will cause a validation error\n  ],\n});\n\nprint(result.$2); // {students: {\"age\":\"Students must be at least 18 years old.\"}}\n\n```\n\n### Example Usage of `noUnknown` in the `.schema`\n\n```dart\n\nfinal EzSchema strictSchema = EzSchema.shape(\n  {\n    'email': EzValidator\u003cString\u003e().required().email(),\n    'password': EzValidator\u003cString\u003e().required(),\n    'username': EzValidator\u003cString\u003e().required(),\n  },\n  noUnknown: true, // Disallow unknown fields\n  fillSchema: false, // Do not fill missing fields\n);\n\nfinal Map\u003cString, dynamic\u003e dataWithUnknown = {\n  'email': 'test@email.com',\n  'password': 'password',\n  'age': 30, // Unknown field\n};\n\nfinal (_, errors) = strictSchema.validateSync(dataWithUnknown);\n\nprint(errors) // {age: is not defined in the schema} \n\n\n\n```\n\n## ShowCase\n\n| with default locale                                                                                                                    | with French locale                                                                                                                     |\n| -------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| \u003cimg src=\"https://user-images.githubusercontent.com/22800380/134804272-16909d85-fee5-4a51-8cec-08b69533c01a.gif?raw=true\" width=\"250\"\u003e | \u003cimg src=\"https://user-images.githubusercontent.com/22800380/134804269-cc1d48ff-a1e0-401a-a7cd-2d5cd8b3b869.gif?raw=true\" width=\"250\"\u003e |\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fx-slayer%2Fezvalidator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fx-slayer%2Fezvalidator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fx-slayer%2Fezvalidator/lists"}