{"id":22744200,"url":"https://github.com/rametta/zodot","last_synced_at":"2025-06-26T08:33:15.876Z","repository":{"id":200198935,"uuid":"704957209","full_name":"rametta/zodot","owner":"rametta","description":"🛡️ Zod for Godot Data Validation","archived":false,"fork":false,"pushed_at":"2025-01-02T22:49:05.000Z","size":257,"stargazers_count":54,"open_issues_count":1,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-06-19T09:58:43.543Z","etag":null,"topics":["data-validator","gdscript","godot","json","runtime-validation","schema-validation","zod"],"latest_commit_sha":null,"homepage":"","language":"GDScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rametta.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"zenodo":null}},"created_at":"2023-10-14T16:09:50.000Z","updated_at":"2025-05-31T03:56:41.000Z","dependencies_parsed_at":"2023-10-16T03:22:20.768Z","dependency_job_id":"7c38d63e-2caf-419b-b535-be037f4931d2","html_url":"https://github.com/rametta/zodot","commit_stats":null,"previous_names":["rametta/zodot"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/rametta/zodot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rametta%2Fzodot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rametta%2Fzodot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rametta%2Fzodot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rametta%2Fzodot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rametta","download_url":"https://codeload.github.com/rametta/zodot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rametta%2Fzodot/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":262030347,"owners_count":23247632,"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":["data-validator","gdscript","godot","json","runtime-validation","schema-validation","zod"],"created_at":"2024-12-11T01:40:58.747Z","updated_at":"2025-06-26T08:33:15.859Z","avatar_url":"https://github.com/rametta.png","language":"GDScript","readme":"# 🛡️ Zodot\n\n\u003e Data Validator for [Godot](https://godotengine.org/) - _inspired by [Zod](https://github.com/colinhacks/zod)_\n\nZodot is a lightweight data validation library for Godot. Define a schema shape, then use that schema to validate any data. Excellent for parsing data that was stored in JSON, or data returned from API's.\n\n## Features:\n\n- Validators for all the Godot [Variant Types](https://docs.godotengine.org/en/latest/classes/class_@globalscope.html#enum-globalscope-variant-type)\n- More expressive than what GDScript provides, allows things like `.nullable()`, `.union()`, typed dictionaries, and more\n- Automatic data coercing with `.coerce()` that uses `str_to_var`\n- Extendible with custom validators for your own types\n- Clear error messages\n- Lightweight \u0026 Zero Dependencies\n\n## Installation\n\nClone `addons/zodot` into your projects `addons` folder, or [download directly](https://godotengine.org/asset-library/asset/2261) from the Godot Asset Store.\n\n## Usage\n\nHere is an example of a defined schema for a `User` with 3 fields and their corresponding types. We can also see there are extra constraints on the name and age, such as name can not be empty, and age must be greater than 12.\n\n```gdscript\n# Our User Schema\nvar UserSchema = Z.schema({\n  \"name\": Z.string().non_empty().maximum(5),\n  \"age\": Z.integer().minimum(12),\n  \"is_tall\": Z.boolean()\n})\n\n# Our data we want to validate\nvar user = {\n  \"name\": \"Jason\",\n  \"age\": 100,\n  \"is_tall\": true\n}\n\nfunc _ready():\n  # Validate the data against the schema and get the result\n  var result = UserSchema.parse(user)\n  print(result.ok()) # true\n```\n\nUsing the same schema, here is an example where the validation fails:\n\n```gdscript\nvar user = {\n  \"name\": \"Jason\",\n  \"age\": 10,\n  \"is_tall\": true\n}\n\nvar result = UserSchema.parse(user)\nprint(result.ok()) # false\nprint(result.error) # \"Field 'age' has value lower than desired minimum of 12\"\n```\n\nExample where data was stored by calling `var_to_str` on every field and stored as `JSON`.\n\n```gdscript\nvar schema = Z.schema({\n  \"my_float\": Z.float().coerce(),\n  \"my_color\": Z.color().coerce(),\n  \"my_vect3\": Z.vector3().coerce()\n})\n\nvar data = {\n  \"my_float\": var_to_str(1.23),\n  \"my_color\": var_to_str(Color(5.5,6.6,7.7, .5)),\n  \"my_vect3\": var_to_str(Vector3(1.9,2.3,3.5)),\n}\n\n# Simulate retreiving this data from external source\n# by stringifying than parsing\nvar json_string = JSON.stringify(data)\nvar json = JSON.new()\njson.parse(json_string)\n\nvar result = schema.parse(json.data)\n\nassert_eq(result.data.my_color, Color(5.5,6.6,7.7, .5)) # true\nassert_eq(result.data.my_float, 1.23) # true\nassert_eq(result.data.my_vect3, Vector3(1.9,2.3,3.5)) # true\n```\n\n## Types\n\nHere is a list of all the available types to use for validation, and their associated constraints. All types also have these base constraints available:\n\n- `.coerce()` calls `str_to_var()` before validation, useful if previously called `var_to_str()`\n- `.nullable()` allows the field to be null or missing\n\nExamples\n\n```gdscript\n# Coerce example\nvar schema = Z.integer().minimum(2).maximum(20).coerce()\nschema.parse(\"5\").ok() # true\nvar result = schema.parse(var_to_str(26)).ok() # false\nresult.data == 26 # true, result data contains the coerced value\n\n# Nullable example\nvar schema = Z.integer().minimum(2).maximum(20).nullable()\nschema.parse(5).ok() # true\nschema.parse(null).ok() # true\nschema.parse(26).ok() # false\n```\n\n### Z.string()\n\nParse [string](https://docs.godotengine.org/en/latest/classes/class_string.html#class-string) type.\n\nAvailable extension constraints:\n\n- `.non_empty()` enforces strings to not be empty\n- `.minimum(value: int)` enforces a minimum length\n- `.maximum(value: int)` enforces a maximum length\n\nExample\n\n```gdscript\nvar schema = Z.string().minimum(1).maximum(20)\nschema.parse(\"hello\").ok() # true\nschema.parse(\"\").ok() # false\n```\n\n### Z.integer()\n\nParse [integer](https://docs.godotengine.org/en/latest/classes/class_int.html#class-int) type.\n\nAvailable extension constraints:\n\n- `.minimum(value: int)` enforces a minimum value\n- `.maximum(value: int)` enforces a maximum value\n\nExample\n\n```gdscript\nvar schema = Z.integer().minimum(1).maximum(20)\nschema.parse(5).ok() # true\nschema.parse(100).ok() # false\nschema.parse(5.5).ok() # false - float is not an integer\n```\n\n### Z.float()\n\nParse [float](https://docs.godotengine.org/en/latest/classes/class_float.html#class-float) type.\n\nAvailable extension constraints:\n\n- `.minimum(value: float)` enforces a minimum value\n- `.maximum(value: float)` enforces a maximum value\n\nExample\n\n```gdscript\nvar schema = Z.float().minimum(1.0).maximum(20.5)\nschema.parse(5.5).ok() # true\nschema.parse(100.45).ok() # false\nschema.parse(5).ok() # false - int is not a float\n```\n\n### Z.boolean()\n\nParse [boolean](https://docs.godotengine.org/en/latest/classes/class_bool.html#class-bool) type.\n\nAccepts enum to constrain to either only `true` or only `false`. Default is both.\n\nExample\n\n```gdscript\nZ.boolean().parse(true).ok() # true\nZ.boolean().parse(false).ok() # true\n\nZ.boolean(z_boolean.Kind.ONLY_TRUE).parse(true).ok() # true\nZ.boolean(z_boolean.Kind.ONLY_TRUE).parse(false).ok() # false\n\nZ.boolean(z_boolean.Kind.ONLY_FALSE).parse(true).ok() # false\nZ.boolean(z_boolean.Kind.ONLY_FALSE).parse(false).ok() # true\n```\n\n### Z.array()\n\nParse [array](https://docs.godotengine.org/en/latest/classes/class_array.html#class-array) type.\n\nAccepts an optional extra schema to constrain array items to a certain type.\n\nAvailable extension constraints:\n\n- `.non_empty()` enforces the array to have at least 1 item\n\nExample\n\n```gdscript\nZ.array().parse([1,2,3]).ok() # true\nZ.array().non_empty().parse([]).ok() # false, empty\nZ.array(Z.integer()).parse([1,2,3]).ok() # true\nZ.array(Z.integer()).parse([\"1\",2,3]).ok() # false, item[0] is a string\n```\n\n### Z.dictionary()\n\nParse [dictionary](https://docs.godotengine.org/en/latest/classes/class_dictionary.html#class-dictionary) type.\n\nAccepts an optional extra schema to constrain dictionary items to a certain type. (For a specific dictionary shape, use `Z.schema()` instead)\n\nAvailable extension constraints:\n\n- `.non_empty()` enforces the dictionary to have at least 1 item\n\nExample\n\n```gdscript\nZ.dictionary().parse({\"key\": 1}).ok() # true\nZ.dictionary().non_empty().parse({}).ok() # false, empty\nZ.dictionary(Z.integer()).parse({\"key\": 1}).ok() # true\nZ.dictionary(Z.integer()).parse({\"key\": \"a\"}).ok() # false, key is a string\n```\n\n### Z.schema()\n\nA special type for defining specific object shapes that are more rigid than a standard dictionary. Takes a dictionary as argument, where each key defines a type.\n\nExample\n\n```gdscript\nvar schema = Z.schema({\n  \"first_name\": Z.string().non_empty(),\n  \"fave_color\": Z.color().nullable()\n})\n\nvar data = {\n  \"first_name\": \"Jason\",\n  \"fave_color\": Color.ALICE_BLUE\n}\n\nschema.parse(data).ok() # true\n```\n\n### Z.union()\n\nA special type for allowing a field to be more than 1 type. Takes an array of schemas to validate against.\n\nExample:\n\n```gdscript\n# Allow a field to be a color OR a vector3\nvar schema = Z.union([Z.color(), Z.vector3()])\n\nschema.parse(Vector3(1,2,3)).ok() # true\nschema.parse(Color(1,2,3)).ok() # true\nschema.parse(67).ok() # false\n```\n\n### Z.zenum() (Z.enum())\n\nA special type for parsing Godot `enum`'s.\n\nNote: Since `enum` is a reserved word, this validator can not be called `Z.enum()` so it is called `Z.zenum()` instead.\n\nExample:\n\n```gdscript\nenum Speed = {\n  Fast,\n  Slow,\n  Medium\n}\n\nvar schema = Z.zenum(Speed)\n\nschema.parse(Speed.Fast).ok() # true\nschema.parse(Speed.Slow).ok() # true\nschema.parse(Speed.Medium).ok() # true\nschema.parse(0).ok() # true\nschema.parse(1).ok() # true\nschema.parse(2).ok() # true\nschema.parse(67).ok() # false\n```\n\n### Z.color()\n\nParse [Color](https://docs.godotengine.org/en/latest/classes/class_color.html#class-color) type.\n\nExample\n\n```gdscript\nZ.color().parse(Color.ALICE_BLUE).ok() # true\nZ.color().parse(Color(1,2,3,0.5)).ok() # true\nZ.color().parse(\"blue\").ok() # false\n```\n\n### Z.vector2() \u0026 Z.vector2i()\n\nParse [Vector2](https://docs.godotengine.org/en/latest/classes/class_vector2.html#class-vector2) and [Vector2i](https://docs.godotengine.org/en/latest/classes/class_vector2i.html#class-vector2i) types respectively.\n\nExample\n\n```gdscript\n# Vector2\nZ.vector2().parse(Vector2.ZERO).ok() # true\nZ.vector2().parse(Vector2(1.1, 2)).ok() # true\nZ.vector2().parse(Vector3.ZERO).ok() # false\n\n# Vector2I\nZ.vector2i().parse(Vector2i.ZERO).ok() # true\nZ.vector2i().parse(Vector2i(1, 2)).ok() # true\nZ.vector2i().parse(Vector2.ZERO).ok() # false\n```\n\n### Z.vector3() \u0026 Z.vector3i()\n\nParse [Vector3](https://docs.godotengine.org/en/latest/classes/class_vector3.html#class-vector3) and [Vector3i](https://docs.godotengine.org/en/latest/classes/class_vector3i.html#class-vector3i) types respectively.\n\nExample\n\n```gdscript\n# Vector3\nZ.vector3().parse(Vector3.ZERO).ok() # true\nZ.vector3().parse(Vector3(1.1, 2, 6)).ok() # true\nZ.vector3().parse(Vector4.ZERO).ok() # false\n\n# Vector3I\nZ.vector3i().parse(Vector3i.ZERO).ok() # true\nZ.vector3i().parse(Vector3i(1, 2, 7)).ok() # true\nZ.vector3i().parse(Vector3.ZERO).ok() # false\n```\n\n### Z.vector4() \u0026 Z.vector4i()\n\nParse [Vector4](https://docs.godotengine.org/en/latest/classes/class_vector4.html#class-vector4) and [Vector4i](https://docs.godotengine.org/en/latest/classes/class_vector4i.html#class-vector4i) types respectively.\n\nExample\n\n```gdscript\n# Vector4\nZ.vector4().parse(Vector4.ZERO).ok() # true\nZ.vector4().parse(Vector4(1.1, 2, 6, 1)).ok() # true\nZ.vector4().parse(Vector3.ZERO).ok() # false\n\n# Vector4I\nZ.vector4i().parse(Vector4i.ZERO).ok() # true\nZ.vector4i().parse(Vector4i(1, 2, 7, 8)).ok() # true\nZ.vector4i().parse(Vector4.ZERO).ok() # false\n```\n\n### Z.transform2d() \u0026 Z.transform3d()\n\nParse [Transform2D](https://docs.godotengine.org/en/latest/classes/class_transform2d.html#class-transform2d) and [Transform3D](https://docs.godotengine.org/en/latest/classes/class_transform3d.html#class-transform3d) types respectively.\n\nExample\n\n```gdscript\n# Transform2D\nZ.transform2d().parse(Transform2D.FLIP_X).ok() # true\nZ.transform2d().parse(Vector3.ZERO).ok() # false\n\n# Transform3D\nZ.transform3d().parse(Transform3D.FLIP_Z).ok() # true\nZ.transform3d().parse(Vector4.ZERO).ok() # false\n```\n\n### Z.rect2() \u0026 Z.rect2i()\n\nParse [Rect2](https://docs.godotengine.org/en/latest/classes/class_rect2.html#class-rect2) and [Rect2i](https://docs.godotengine.org/en/latest/classes/class_rect2i.html#class-rect2i) types respectively.\n\nExample\n\n```gdscript\n# Rect2\nZ.rect2().parse(Rect2(1,2,3,4.5)).ok() # true\nZ.rect2().parse(Vector3.ZERO).ok() # false\n\n# Rect2i\nZ.rect2i().parse(Rect2(1,2,3,4)).ok() # true\nZ.rect2i().parse(Vector4.ZERO).ok() # false\n```\n\n### Z.plane()\n\nParse [Plane](https://docs.godotengine.org/en/latest/classes/class_plane.html#class-plane) type.\n\nExample\n\n```gdscript\nZ.plane().parse(Plane.PLANE_XY).ok() # true\n```\n\n### Z.projection()\n\nParse [Projection](https://docs.godotengine.org/en/latest/classes/class_projection.html#class-projection) type.\n\nExample\n\n```gdscript\nZ.projection().parse(Projection.ZERO).ok() # true\n```\n\n### Z.quaternion()\n\nParse [Quaternion](https://docs.godotengine.org/en/latest/classes/class_quaternion.html#class-quaternion) type.\n\nExample\n\n```gdscript\nZ.quaternion().parse(Quaternion.IDENTITY).ok() # true\n```\n\n### Z.aabb()\n\nParse [AABB](https://docs.godotengine.org/en/latest/classes/class_aabb.html#class-aabb) type.\n\nExample\n\n```gdscript\nZ.aabb().parse(AABB()).ok() # true\n```\n\n### Z.rid()\n\nParse [RID](https://docs.godotengine.org/en/latest/classes/class_rid.html#class-rid) type.\n\nExample\n\n```gdscript\nZ.rid().parse(RID()).ok() # true\n```\n\n### Z.basis()\n\nParse [Basis](https://docs.godotengine.org/en/latest/classes/class_basis.html#class-basis) type.\n\nExample\n\n```gdscript\nZ.basis().parse(Basis.FLIP_X).ok() # true\n```\n\n### More\n\nThere are many more validators, please check [here](https://github.com/rametta/zodot/tree/main/addons/zodot/type_classes) for a full list of all the validators available.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frametta%2Fzodot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frametta%2Fzodot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frametta%2Fzodot/lists"}