Skip to main content

Vet

Lua-first schema validation with type inference.

local v = require(ReplicatedStorage.Packages.Vet)

local Name = v.string():min(3):max(22)

Name:parse("kyle") --> "kyle"
Name:parse("ky")   --> errors

Types​

Issue​

interface Issue {
path: {string | number}--

Location of the failure inside the value

message: string--

Human-readable description of the failure

}

Failure​

type Failure = {
success: false,
issues: {Issue}
}

The unsuccessful arm of Vet.Result. Naming it lets a helper return a failure that fits any Result<T> without widening T to any.

Result<T>​

type Result<T> = {
success: true,
value: T
} | Failure

Returned by safeParse. Narrow it with if result.success then to get a typed result.value.

infer<S>​

type infer<S> = thevaluetypeproducedbyschemaS

Extracts the type a schema parses to, so a schema can be the single source of truth for both validation and types.

local Username = v.string():min(3)
type Username = v.infer<typeof(Username)> --> string

local Nickname = v.string():optional()
type Nickname = v.infer<typeof(Nickname)> --> string?

Given a union of schemas, the result is the union of what each one parses to: Vet.infer<StringSchema | NumberSchema> is string | number.

This is a user-defined type function, so it runs during analysis and costs nothing at runtime. It requires a Luau version that supports user-defined type functions.

objectOutput<S>​

type objectOutput<S> = therecordtypeashapeofschemasparsesto

Maps { name: StringSchema, age: NumberSchema } to { name: string, age: number }. Used to type the result of Vet.object, so the shape you pass in is the only place the field types are written down.

A field wrapped by ReadonlySchema or WriteonlySchema carries an access marker, which becomes a read or write qualifier on the property it produces:

local PlayerData = v.object({
	id = v.number():readonly(),
	name = v.string(),
	secretToken = v.string():writeonly(),
})

type PlayerData = v.infer<typeof(PlayerData)>
--> { read id: number, name: string, write secretToken: string }

The property walk duplicates the one in Vet.infer because a type function cannot call another type function.

AnySchema<T>​

type AnySchema<T> = {
parse: (
self,
unknown
) → T,
safeParse: (
self,
unknown
) → Result<T>
}

The shape every schema shares. Constructors that take another schema accept this, so passing something that is not a schema is a type error at the call site rather than a failure at parse time.

Access​

type Access = "read" | "write" | "readwrite"

What a schema's access property holds. Every schema carries one, so a shape can be inspected without knowing which kind of schema a field is:

local Player = v.object({ id = v.number():readonly(), name = v.string() })

Player.shape.id.access   --> "read"
Player.shape.name.access --> "readwrite"

Each schema type declares the single value it can hold rather than this union, which is what lets Vet.objectOutput read the marker during analysis.

Color3Schema​

type Color3Schema = DatatypeSchema<Color3>

What Vet.color3 returns.

UDim2Schema​

type UDim2Schema = DatatypeSchema<UDim2>

What Vet.udim2 returns.

UuidVersion​

type UuidVersion = "v4" | "v5" | "v6" | "v7" | "v8"

Versions accepted by Vet.uuid and Vet.guid. Version 4 is random, 5 is name-based, 6 is reordered time, 7 is Unix epoch time, and 8 is custom.

UuidOptions​

interface UuidOptions {
version: UuidVersion?--

Version the identifier must declare

}

Functions​

string​

Constructor
Vet.string() → StringSchema

Creates a schema that accepts any string.

number​

Constructor
Vet.number() → NumberSchema

Creates a schema that accepts any number except NaN.

boolean​

Constructor
Vet.boolean() → BooleanSchema

Creates a schema that accepts true or false.

color3​

Constructor
Vet.color3() → Color3Schema

Creates a schema that accepts a Color3.

local Tint = v.color3()

Tint:parse(Color3.fromRGB(255, 0, 0)) --> Color3(1, 0, 0)
Tint:parse({ R = 1, G = 0, B = 0 })   --> errors: expected Color3, got table

The schema holds no state, so every call returns the same frozen instance.

udim2​

Constructor
Vet.udim2() → UDim2Schema

Creates a schema that accepts a UDim2.

local Size = v.udim2()

Size:parse(UDim2.fromScale(1, 0.5)) --> UDim2
Size:parse({ X = 1, Y = 0.5 })      --> errors: expected UDim2, got table

The schema holds no state, so every call returns the same frozen instance.

literal​

Constructor
Vet.literal(
value: T--

The only value the schema accepts

) → LiteralSchema<T>

Creates a schema that accepts exactly value, compared with ==.

local Consented = v.literal(true)

Consented:parse(true)  --> true
Consented:parse(false) --> errors: expected true, got false

Luau widens a literal argument when it infers a generic, so the result is typed boolean rather than true. Annotate the schema if you need the singleton: local Kind: LiteralSchema<"player"> = v.literal("player").

uuid​

Constructor
Vet.uuid(
options: UuidOptions?--

Pass { version = "v4" } to pin the version

) → StringSchema

Creates a schema that accepts a UUID: 8-4-4-4-12 hexadecimal digits, in either case, carrying a version of 1 to 8 and an RFC 9562 variant.

local Id = v.uuid()
local SessionId = v.uuid({ version = "v7" })

Id:parse("01912d68-783e-7cc0-a2f0-5f8b1d3e9a7c") --> ok
SessionId:parse("6f9619ff-8b86-4d11-b42d-00cf4fc964ff")
--> errors: expected a v7 uuid, got v4

Because the version and variant are checked, the nil UUID and the max UUID are both rejected. Use Vet.guid when you only care about the layout.

The result is a plain StringSchema, so :optional(), :default() and Vet.infer behave exactly as they do for any other string.

Errors

TypeDescription
"unknown uuid version ..."Thrown when `options.version` is not v4 to v8

guid​

Constructor
Vet.guid(
options: UuidOptions?--

Pass { version = "v4" } to pin the version

) → StringSchema

Creates a schema that accepts a GUID: the same 8-4-4-4-12 layout as Vet.uuid, optionally wrapped in braces, with no variant requirement.

local Id = v.guid()

Id:parse("{6F9619FF-8B86-D011-B42D-00CF4FC964FF}") --> ok
Id:parse("6f9619ff-8b86-d011-b42d-00cf4fc964ff")   --> ok

This is the shape HttpService:GenerateGUID returns, which is braced and uppercase by default. Values Microsoft produced do not always follow the RFC variant rule, so only the layout is enforced unless you pass a version.

Errors

TypeDescription
"unknown guid version ..."Thrown when `options.version` is not v4 to v8

array​

Constructor
Vet.array(
item: S--

Schema every element must satisfy

) → ArraySchema<Vet.infer<S>>

Creates a schema that accepts an array of item.

local Scores = v.array(v.number():integer())

Scores:parse({ 1, 2, 3 }) --> { 1, 2, 3 }
Scores:parse({ 1, 1.5 })  --> errors: [2]: expected an integer, got 1.5

tuple​

Constructor
Vet.tuple(
positions: {S}--

One schema per position, in order

) → TupleSchema<Vet.infer<S>>

Creates a schema that accepts a fixed sequence, checking each position against its own schema.

local Waypoint = v.tuple({ v.string(), v.number():integer() })

Waypoint:parse({ "spawn", 3 }) --> { "spawn", 3 }
Waypoint:parse({ "spawn" })    --> errors: [2]: expected number, got nil

Luau infers a mixed array literal as a union of its element types, so the positions are already merged by the time a type function can see them. The result is therefore { string | number } rather than a true positional type — the runtime check is exact, the static type is not.

object​

Constructor
Vet.object(
shape: S--

Table mapping field names to schemas

) → ObjectSchema<Vet.objectOutput<S>,S>

Creates a schema that validates a table field by field. The field types come from the shape, so the schema is the only place they are declared.

local User = v.object({
	name = v.string():min(3),
	nickname = v.string():optional(),
})

type User = v.infer<typeof(User)> --> { name: string, nickname: string? }

union​

Constructor
Vet.union(
options: {S}--

Schemas to try, in order

) → UnionSchema<Vet.infer<S>>

Creates a schema that accepts a value matching any one of options, trying them in order and keeping what the first match parsed to.

local Id = v.union({ v.string():min(1), v.number():integer() })

type Id = v.infer<typeof(Id)> --> string | number

Id:parse("player_1") --> "player_1"
Id:parse(7)          --> 7
Id:parse(1.5)        --> errors: expected one of 2 options
                     --  (option 1: expected string, got number;
                     --   option 2: expected an integer, got 1.5)

Luau infers a mixed array literal as a union of its element types, which is exactly what a union needs, so the inferred output is the union of what each option parses to.

Errors

TypeDescription
"expected at least one option"Thrown when `options` is empty

readonly​

Constructor
Vet.readonly(
schema: S--

Schema the field is validated against

) → ReadonlySchema<Vet.infer<S>>

Marks a schema read-only, so a field using it is declared read in the record Vet.object parses to.

local PlayerData = v.object({
	id = v.readonly(v.number():integer()),
	name = v.string(),
})

type PlayerData = v.infer<typeof(PlayerData)>
--> { read id: number, name: string }

Every schema also has a :readonly() method, which reads better at the end of a chain: v.number():integer():readonly(). Use this form when the schema arrives from somewhere else and there is no chain to put it on.

writeonly​

Constructor
Vet.writeonly(
schema: S--

Schema the field is validated against

) → WriteonlySchema<Vet.infer<S>>

Marks a schema write-only, so a field using it is declared write in the record Vet.object parses to.

local PlayerData = v.object({
	name = v.string(),
	secretToken = v.writeonly(v.string():length(32)),
})

type PlayerData = v.infer<typeof(PlayerData)>
--> { name: string, write secretToken: string }

The token is still validated on the way in and still present at runtime — what changes is that reading it back is an analysis error, so it cannot be logged or replicated by accident.

Show raw api
{
    "functions": [
        {
            "name": "string",
            "desc": "Creates a schema that accepts any string.",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "StringSchema"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 3466,
                "path": "src/init.luau"
            }
        },
        {
            "name": "number",
            "desc": "Creates a schema that accepts any number except NaN.",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "NumberSchema"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 3477,
                "path": "src/init.luau"
            }
        },
        {
            "name": "boolean",
            "desc": "Creates a schema that accepts `true` or `false`.",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "BooleanSchema"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 3488,
                "path": "src/init.luau"
            }
        },
        {
            "name": "color3",
            "desc": "Creates a schema that accepts a `Color3`.\n\n```lua\nlocal Tint = v.color3()\n\nTint:parse(Color3.fromRGB(255, 0, 0)) --> Color3(1, 0, 0)\nTint:parse({ R = 1, G = 0, B = 0 })   --> errors: expected Color3, got table\n```\n\nThe schema holds no state, so every call returns the same frozen instance.",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Color3Schema"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 3508,
                "path": "src/init.luau"
            }
        },
        {
            "name": "udim2",
            "desc": "Creates a schema that accepts a `UDim2`.\n\n```lua\nlocal Size = v.udim2()\n\nSize:parse(UDim2.fromScale(1, 0.5)) --> UDim2\nSize:parse({ X = 1, Y = 0.5 })      --> errors: expected UDim2, got table\n```\n\nThe schema holds no state, so every call returns the same frozen instance.",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "UDim2Schema"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 3528,
                "path": "src/init.luau"
            }
        },
        {
            "name": "literal",
            "desc": "Creates a schema that accepts exactly `value`, compared with `==`.\n\n```lua\nlocal Consented = v.literal(true)\n\nConsented:parse(true)  --> true\nConsented:parse(false) --> errors: expected true, got false\n```\n\nLuau widens a literal argument when it infers a generic, so the result is\ntyped `boolean` rather than `true`. Annotate the schema if you need the\nsingleton: `local Kind: LiteralSchema<\"player\"> = v.literal(\"player\")`.",
            "params": [
                {
                    "name": "value",
                    "desc": "The only value the schema accepts",
                    "lua_type": "T"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "LiteralSchema<T>"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 3550,
                "path": "src/init.luau"
            }
        },
        {
            "name": "uuid",
            "desc": "Creates a schema that accepts a UUID: 8-4-4-4-12 hexadecimal digits, in\neither case, carrying a version of 1 to 8 and an RFC 9562 variant.\n\n```lua\nlocal Id = v.uuid()\nlocal SessionId = v.uuid({ version = \"v7\" })\n\nId:parse(\"01912d68-783e-7cc0-a2f0-5f8b1d3e9a7c\") --> ok\nSessionId:parse(\"6f9619ff-8b86-4d11-b42d-00cf4fc964ff\")\n--> errors: expected a v7 uuid, got v4\n```\n\nBecause the version and variant are checked, the nil UUID and the max UUID\nare both rejected. Use [Vet.guid] when you only care about the layout.\n\nThe result is a plain [StringSchema], so `:optional()`, `:default()` and\n`Vet.infer` behave exactly as they do for any other string.",
            "params": [
                {
                    "name": "options",
                    "desc": "Pass `{ version = \"v4\" }` to pin the version",
                    "lua_type": "UuidOptions?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "StringSchema"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "errors": [
                {
                    "lua_type": "\"unknown uuid version ...\"",
                    "desc": "Thrown when `options.version` is not v4 to v8"
                }
            ],
            "source": {
                "line": 3579,
                "path": "src/init.luau"
            }
        },
        {
            "name": "guid",
            "desc": "Creates a schema that accepts a GUID: the same 8-4-4-4-12 layout as\n[Vet.uuid], optionally wrapped in braces, with no variant requirement.\n\n```lua\nlocal Id = v.guid()\n\nId:parse(\"{6F9619FF-8B86-D011-B42D-00CF4FC964FF}\") --> ok\nId:parse(\"6f9619ff-8b86-d011-b42d-00cf4fc964ff\")   --> ok\n```\n\nThis is the shape `HttpService:GenerateGUID` returns, which is braced and\nuppercase by default. Values Microsoft produced do not always follow the\nRFC variant rule, so only the layout is enforced unless you pass a version.",
            "params": [
                {
                    "name": "options",
                    "desc": "Pass `{ version = \"v4\" }` to pin the version",
                    "lua_type": "UuidOptions?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "StringSchema"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "errors": [
                {
                    "lua_type": "\"unknown guid version ...\"",
                    "desc": "Thrown when `options.version` is not v4 to v8"
                }
            ],
            "source": {
                "line": 3604,
                "path": "src/init.luau"
            }
        },
        {
            "name": "array",
            "desc": "Creates a schema that accepts an array of `item`.\n\n```lua\nlocal Scores = v.array(v.number():integer())\n\nScores:parse({ 1, 2, 3 }) --> { 1, 2, 3 }\nScores:parse({ 1, 1.5 })  --> errors: [2]: expected an integer, got 1.5\n```",
            "params": [
                {
                    "name": "item",
                    "desc": "Schema every element must satisfy",
                    "lua_type": "S"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "ArraySchema<Vet.infer<S>>"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 3623,
                "path": "src/init.luau"
            }
        },
        {
            "name": "tuple",
            "desc": "Creates a schema that accepts a fixed sequence, checking each position\nagainst its own schema.\n\n```lua\nlocal Waypoint = v.tuple({ v.string(), v.number():integer() })\n\nWaypoint:parse({ \"spawn\", 3 }) --> { \"spawn\", 3 }\nWaypoint:parse({ \"spawn\" })    --> errors: [2]: expected number, got nil\n```\n\nLuau infers a mixed array literal as a union of its element types, so the\npositions are already merged by the time a type function can see them. The\nresult is therefore `{ string | number }` rather than a true positional\ntype — the runtime check is exact, the static type is not.",
            "params": [
                {
                    "name": "positions",
                    "desc": "One schema per position, in order",
                    "lua_type": "{ S }"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "TupleSchema<Vet.infer<S>>"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 3652,
                "path": "src/init.luau"
            }
        },
        {
            "name": "object",
            "desc": "Creates a schema that validates a table field by field. The field types come\nfrom the shape, so the schema is the only place they are declared.\n\n```lua\nlocal User = v.object({\n\tname = v.string():min(3),\n\tnickname = v.string():optional(),\n})\n\ntype User = v.infer<typeof(User)> --> { name: string, nickname: string? }\n```",
            "params": [
                {
                    "name": "shape",
                    "desc": "Table mapping field names to schemas",
                    "lua_type": "S"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "ObjectSchema<Vet.objectOutput<S>, S>"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 3676,
                "path": "src/init.luau"
            }
        },
        {
            "name": "union",
            "desc": "Creates a schema that accepts a value matching any one of `options`, trying\nthem in order and keeping what the first match parsed to.\n\n```lua\nlocal Id = v.union({ v.string():min(1), v.number():integer() })\n\ntype Id = v.infer<typeof(Id)> --> string | number\n\nId:parse(\"player_1\") --> \"player_1\"\nId:parse(7)          --> 7\nId:parse(1.5)        --> errors: expected one of 2 options\n                     --  (option 1: expected string, got number;\n                     --   option 2: expected an integer, got 1.5)\n```\n\nLuau infers a mixed array literal as a union of its element types, which is\nexactly what a union needs, so the inferred output is the union of what each\noption parses to.",
            "params": [
                {
                    "name": "options",
                    "desc": "Schemas to try, in order",
                    "lua_type": "{ S }"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "UnionSchema<Vet.infer<S>>"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "errors": [
                {
                    "lua_type": "\"expected at least one option\"",
                    "desc": "Thrown when `options` is empty"
                }
            ],
            "source": {
                "line": 3706,
                "path": "src/init.luau"
            }
        },
        {
            "name": "readonly",
            "desc": "Marks a schema read-only, so a field using it is declared `read` in the\nrecord [Vet.object] parses to.\n\n```lua\nlocal PlayerData = v.object({\n\tid = v.readonly(v.number():integer()),\n\tname = v.string(),\n})\n\ntype PlayerData = v.infer<typeof(PlayerData)>\n--> { read id: number, name: string }\n```\n\nEvery schema also has a `:readonly()` method, which reads better at the end\nof a chain: `v.number():integer():readonly()`. Use this form when the schema\narrives from somewhere else and there is no chain to put it on.",
            "params": [
                {
                    "name": "schema",
                    "desc": "Schema the field is validated against",
                    "lua_type": "S"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "ReadonlySchema<Vet.infer<S>>"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 3735,
                "path": "src/init.luau"
            }
        },
        {
            "name": "writeonly",
            "desc": "Marks a schema write-only, so a field using it is declared `write` in the\nrecord [Vet.object] parses to.\n\n```lua\nlocal PlayerData = v.object({\n\tname = v.string(),\n\tsecretToken = v.writeonly(v.string():length(32)),\n})\n\ntype PlayerData = v.infer<typeof(PlayerData)>\n--> { name: string, write secretToken: string }\n```\n\nThe token is still validated on the way in and still present at runtime —\nwhat changes is that reading it back is an analysis error, so it cannot be\nlogged or replicated by accident.",
            "params": [
                {
                    "name": "schema",
                    "desc": "Schema the field is validated against",
                    "lua_type": "S"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "WriteonlySchema<Vet.infer<S>>"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 3767,
                "path": "src/init.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "Issue",
            "desc": "",
            "fields": [
                {
                    "name": "path",
                    "lua_type": "{ string | number }",
                    "desc": "Location of the failure inside the value"
                },
                {
                    "name": "message",
                    "lua_type": "string",
                    "desc": "Human-readable description of the failure"
                }
            ],
            "source": {
                "line": 24,
                "path": "src/init.luau"
            }
        },
        {
            "name": "Failure",
            "desc": "The unsuccessful arm of [Vet.Result]. Naming it lets a helper return a\nfailure that fits any `Result<T>` without widening `T` to `any`.",
            "lua_type": "{ success: false, issues: { Issue } }",
            "source": {
                "line": 36,
                "path": "src/init.luau"
            }
        },
        {
            "name": "Result<T>",
            "desc": "Returned by `safeParse`. Narrow it with `if result.success then` to get a\ntyped `result.value`.",
            "lua_type": "{ success: true, value: T } | Failure",
            "source": {
                "line": 45,
                "path": "src/init.luau"
            }
        },
        {
            "name": "infer<S>",
            "desc": "Extracts the type a schema parses to, so a schema can be the single source\nof truth for both validation and types.\n\n```lua\nlocal Username = v.string():min(3)\ntype Username = v.infer<typeof(Username)> --> string\n\nlocal Nickname = v.string():optional()\ntype Nickname = v.infer<typeof(Nickname)> --> string?\n```\n\nGiven a union of schemas, the result is the union of what each one parses\nto: `Vet.infer<StringSchema | NumberSchema>` is `string | number`.\n\nThis is a user-defined type function, so it runs during analysis and costs\nnothing at runtime. It requires a Luau version that supports user-defined\ntype functions.",
            "lua_type": "the value type produced by schema S",
            "source": {
                "line": 69,
                "path": "src/init.luau"
            }
        },
        {
            "name": "objectOutput<S>",
            "desc": "Maps `{ name: StringSchema, age: NumberSchema }` to `{ name: string, age:\nnumber }`. Used to type the result of `Vet.object`, so the shape you pass in\nis the only place the field types are written down.\n\nA field wrapped by [ReadonlySchema] or [WriteonlySchema] carries an `access`\nmarker, which becomes a `read` or `write` qualifier on the property it\nproduces:\n\n```lua\nlocal PlayerData = v.object({\n\tid = v.number():readonly(),\n\tname = v.string(),\n\tsecretToken = v.string():writeonly(),\n})\n\ntype PlayerData = v.infer<typeof(PlayerData)>\n--> { read id: number, name: string, write secretToken: string }\n```\n\nThe property walk duplicates the one in [Vet.infer] because a type function\ncannot call another type function.",
            "lua_type": "the record type a shape of schemas parses to",
            "source": {
                "line": 182,
                "path": "src/init.luau"
            }
        },
        {
            "name": "AnySchema<T>",
            "desc": "The shape every schema shares. Constructors that take another schema accept\nthis, so passing something that is not a schema is a type error at the call\nsite rather than a failure at parse time.",
            "lua_type": "{ parse: (self, unknown) -> T, safeParse: (self, unknown) -> Result<T> }",
            "source": {
                "line": 290,
                "path": "src/init.luau"
            }
        },
        {
            "name": "Access",
            "desc": "What a schema's `access` property holds. Every schema carries one, so a\nshape can be inspected without knowing which kind of schema a field is:\n\n```lua\nlocal Player = v.object({ id = v.number():readonly(), name = v.string() })\n\nPlayer.shape.id.access   --> \"read\"\nPlayer.shape.name.access --> \"readwrite\"\n```\n\nEach schema type declares the single value it can hold rather than this\nunion, which is what lets [Vet.objectOutput] read the marker during\nanalysis.",
            "lua_type": "\"read\" | \"write\" | \"readwrite\"",
            "source": {
                "line": 457,
                "path": "src/init.luau"
            }
        },
        {
            "name": "Color3Schema",
            "desc": "What [Vet.color3] returns.",
            "lua_type": "DatatypeSchema<Color3>",
            "source": {
                "line": 1815,
                "path": "src/init.luau"
            }
        },
        {
            "name": "UDim2Schema",
            "desc": "What [Vet.udim2] returns.",
            "lua_type": "DatatypeSchema<UDim2>",
            "source": {
                "line": 1823,
                "path": "src/init.luau"
            }
        },
        {
            "name": "UuidVersion",
            "desc": "Versions accepted by [Vet.uuid] and [Vet.guid]. Version 4 is random, 5 is\nname-based, 6 is reordered time, 7 is Unix epoch time, and 8 is custom.",
            "lua_type": "\"v4\" | \"v5\" | \"v6\" | \"v7\" | \"v8\"",
            "source": {
                "line": 3365,
                "path": "src/init.luau"
            }
        },
        {
            "name": "UuidOptions",
            "desc": "",
            "fields": [
                {
                    "name": "version",
                    "lua_type": "UuidVersion?",
                    "desc": "Version the identifier must declare"
                }
            ],
            "source": {
                "line": 3372,
                "path": "src/init.luau"
            }
        }
    ],
    "name": "Vet",
    "desc": "Lua-first schema validation with type inference.\n\n```lua\nlocal v = require(ReplicatedStorage.Packages.Vet)\n\nlocal Name = v.string():min(3):max(22)\n\nName:parse(\"kyle\") --> \"kyle\"\nName:parse(\"ky\")   --> errors\n```",
    "source": {
        "line": 17,
        "path": "src/init.luau"
    }
}