shape

package module
v0.5.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 4, 2026 License: MIT Imports: 15 Imported by: 0

README

shape/go

Go port of the shape schema-by-example validator. Your schema looks (almost) exactly like your data.

import "github.com/rjrodger/shape/go"

s := shape.MustShape(map[string]any{
	"port":  8080,          // optional, defaults to 8080, must be a number
	"host":  "localhost",   // optional, defaults to "localhost", must be a string
	"debug": shape.Boolean, // required, must be a boolean
})

out, err := s.Validate(map[string]any{"debug": true})
// out == map[string]any{"port": 8080, "host": "localhost", "debug": true}

The TypeScript implementation in ../ts is canonical: this port matches it for validation outcomes, produced values and exact error text, and a shared conformance corpus plus a differential harness keep it that way. The full documentation is in ../docs; this file is the Go surface in one place.

Install

go get github.com/rjrodger/shape/go

Requires Go 1.22+. The module has no dependencies.

Concepts

A schema is built from an example value. Literal values become optional with a default; sentinel tokens become required.

Sentinel tokens

Go cannot use predeclared types as runtime values, so the package exports sentinels for each kind:

Token Matches
shape.Any any value (the one token that does not require a value)
shape.String strings (not the empty string, unless Empty)
shape.Number any numeric kind (int*, uint*, float*)
shape.Integer a number with no fractional part
shape.Boolean booleans
shape.Object map[string]any—open, as a token
shape.Array []any (typed slices are accepted and converted)
shape.Function reflect.Func values
shape.Date time.Time values

If you prefer a dot-import without colliding with stdlib names, G-prefixed aliases are provided for every token and builder: GString, GNumber, GRequired, GMin, GPick, etc.

Absent versus null

Go has no undefined. A missing map key is absent—it may be defaulted or flagged required. An explicit nil value is a present null, a type error against a typed shape. At the top level Validate(nil) means "no value supplied" and fills defaults; pass shape.Null to mean a present null there.

Objects

Objects are closed by default—extra keys cause a validation error. An empty map[string]any{} is open. Use shape.Open(...) to allow unknown properties, or shape.Child(...) to declare a shape for unknown values.

shape.MustShape(shape.Open(map[string]any{"a": 1}))          // extra keys allowed
shape.MustShape(shape.Child(shape.Number, map[string]any{})) // every value must be a number

Go maps are unordered, so an object's keys are processed in alphabetical order: that fixes the order of multiple errors and how an object value is rendered inside a message. The produced value is unaffected.

Structs

A struct, or pointer to one, is accepted wherever an object is: it is read by its json tags (- hides, omitempty makes a zero value absent, embedded structs are promoted) into the map model, so it validates exactly as the map it encodes to. ValidateInto decodes the produced value back into a struct. A struct is also a spec by example, its fields the defaults and its shape tags the key expressions:

type Config struct {
	Host  string `shape:"Min(1)"`
	Port  int    `shape:"Min(1).Max(65535)"`
	Debug bool   `shape:"Boolean"` // required
}
s := shape.MustShape(Config{Host: "localhost", Port: 8080})

var c Config
err := s.ValidateInto(map[string]any{"Debug": true}, &c) // c.Port == 8080
Arrays

A single-element array is treated as "every element matches this shape":

shape.MustShape([]any{shape.Number}) // []number

Multiple elements form a tuple of fixed length. Use shape.Rest(...) to allow a tail beyond the tuple positions.

API

Compilation
shape.Shape(spec)                      // compile, returns (*Schema, error)
shape.ShapeWith(spec, shape.ShapeOptions{...})
shape.MustShape(spec)                  // panics on compile error
shape.MustShapeWith(spec, opts)
shape.Build(spec)                      // like Shape, but recursively expands string DSL
shape.Expr("String.Min(2).Max(10)")    // parse the string DSL into a *Node
shape.MustExpr(...)
shape.IsShape(v)                       // is v a *Schema?
Validation
out, err := s.Validate(input)         // returns the (defaults-injected) value plus *ValidationError
out, err := s.ValidateCtx(input, ctx) // pass a *shape.Context for custom validators
ok := s.Match(input)                  // bool, no errors collected
ok := s.Valid(input)                  // alias of Match
issues := s.Error(input)              // []FieldError, nil when valid
spec := s.Spec()                      // structural snapshot of the compiled schema
str := s.String()                     // debug rendering
j, _ := s.JSON()                      // the shape as declarative JSON
back, _ := shape.Build(j)             // and back: the same shape again
schema := s.JSONSchema()              // a JSON Schema (draft 2020-12), as map[string]any
spec, _ := shape.FromJSONSchema(doc)  // and back: a spec built from a JSON Schema
err := s.ValidateInto(input, &out)    // validate, then decode the result into a struct
std := s.Standard()                   // Standard Schema V1-style interface: Version, Vendor, Validate

Validate and Error never change their input. The value Validate returns is produced by copying on write: an object or array of the input that validates as it is comes back as itself, and one that changes (a default injected, a key renamed or dropped, a child produced as a different value) comes back as a copy, with the input left as it was. So the result may share structure with the input; take a copy before changing either if both are kept. (Before v0.4.0 every object and array was copied whether it changed or not.)

Injected defaults are deep-cloned, so two results never share a default's state. A validator attached to a node after Shape() has compiled it is not seen by the compile, so attach validators before compiling.

*ValidationError aggregates one or more FieldErrors, joined by newline in Error(); each carries Path, PathArr, Key, Type, Value, Why, Check, Mark, Args and Text. The message text is identical to the TypeScript implementation's.

Options

shape.ShapeOptions mirrors the TS options. Defaults shown:

shape.ShapeOptions{
	KeyExpr: shape.KeyExprOptions{Disable: false}, // "x: Min(1)" key parsing — on
	Meta:    shape.MetaOptions{Active: false, Suffix: "$$"},
	ValExpr: shape.ValExprOptions{Active: false, KeyMark: "$$"},
}

With key-expression parsing on (the default), object keys may carry inline builders, and the value is the example the builder works on:

shape.MustShape(map[string]any{
	"name: Min(1)":           shape.String,
	"tags: Max(10)":          []any{shape.String},
	"port: Optional(Number)": 8080, // optional, defaults to 8080
	`user: Pick(["id"])`:     map[string]any{"id": shape.Number, "name": shape.String},
})

Builders

All builders have a top-level form and, unless noted, a chainable method form on *Node. Most accept an optional spec argument that the builder narrows or wraps. The builder reference has the detail; the tables here list the Go signatures.

Required / optional / defaults
Builder Effect
Required(spec?) mark required (no default injection)
Optional(spec?) mark optional
Default(value, spec?) optional with an explicit default
Skip(spec?) optional, no default injection
Ignore(spec?) like Skip, and drop the value if anything in its subtree fails
Empty(spec?) allow the empty string for a String shape
Nullable(spec?) accept an explicit nil as the value
Fault(msg, spec?) override the structural error message of this node
Type / equality / coercion
Builder Effect
Type(kind, spec?) force a Kind, TypeToken, kind name or node's type on the node
Exact(values...) require equality with one of the listed literals (numbers by value, so Exact(1) matches 1.0; the rest by reflect.DeepEqual)
Never(spec?) always fails to match
Func(spec?) a function-typed value; optional of itself (the Function token is required)
Coerce(spec?) convert a string/number/bool to the node's kind first, where unambiguous
.Any(), .Integer(), .Date() chain shortcuts for the Any, Integer and Date tokens
String formats

Email, Url, Uuid, DateTime, Ip, Ipv4, Ipv6—each (spec?), each requiring a string in that format; bare, a required string.

Bounds
Builder Effect
Min(n, spec?) / Max(n, spec?) numeric value or collection length bounds (inclusive)
Above(n, spec?) / Below(n, spec?) strict bounds
Len(n, spec?) exact value or collection length
Custom checks and isolation
Builder Effect
Check(fn or *regexp.Regexp, spec?) custom predicate
Before(fn, spec?) run before structural type checks
After(fn, spec?) run after structural type checks
Catch(fallback, spec?) replace whatever fails inside with fallback, raising nothing
Transform(fn, spec?) replace a valid value with fn(value, state)
Describe(text, spec?) attach a description, read back with n.Meta()["description"]

Custom-check signature:

func(val any, update *shape.Update, state *shape.State) bool

A *regexp.Regexp anywhere in a spec is a string that must match it.

Composition
Builder Effect
One(shapes...) the first matching shape's output is used
Some(shapes...) at least one shape must match
All(shapes...) every shape must match
Discriminated(tag, branches) a tagged union: branches is a map[string]any keyed by tag value

All four are top-level only.

Objects / arrays
Builder Effect
Open(spec?) / Closed(spec?) allow / forbid unknown object properties
Child(child, spec?) default child shape for an Open object or for an array
Rest(child, spec?) tail-shape for arrays past tuple positions
Rename(name, spec?), RenameWith(name, opts, spec?) rename an object property after validation
Object algebra

Each returns a new node, leaving the source unchanged. names is a string, []string or []any.

Builder Effect
Pick(names, spec?) keep only the named properties (an unknown name is a fault)
Omit(names, spec?) drop the named properties
Partial(spec?) make every declared property optional (shallow)
Extend(extra, spec?) add the properties of extra; the base's openness and checks stay
References
Builder Effect
Define(name, spec?) name a shape so it can be referenced later
Refer(name, spec?) substitute the named shape at validation time
ReferWith(name, opts, spec?) opts.Fill substitutes even when the input value is missing
Misc
Builder Effect
Key(args...) replace the value with the validation key (or path slice)
Construction faults

A builder called wrongly—Discriminated without a branch, Pick of an unknown property—returns a node that fails at validation with the message TypeScript would have thrown, since a *Node cannot carry an error. In the string DSL, Expr returns the error.

Example: composition and error handling

s := shape.MustShape(map[string]any{
	"name":  shape.Min(1, shape.String),
	"age":   shape.Coerce(shape.Min(0, shape.Max(120, shape.Integer))),
	"email": shape.Email(),
	"role":  shape.Exact("admin", "user"),
	"tags":  shape.Optional([]any{shape.String}),
	"addr": shape.Open(map[string]any{
		"city": shape.String,
	}),
	"pet": shape.Discriminated("kind", map[string]any{
		"dog":  map[string]any{"bark": shape.Boolean},
		"fish": map[string]any{"fins": shape.Number},
	}),
})

out, err := s.Validate(input)
if verr, ok := err.(*shape.ValidationError); ok {
	for _, issue := range verr.Issues {
		fmt.Printf("%s [%s]: %s\n", issue.Path, issue.Why, issue.Text)
	}
}

Development

go build ./... && go vet ./... && go test -cover -count=1 .

The package is held at 100% statement coverage, and Go has no coverage pragma: anything new is covered by a test or removed. go test also runs the shared corpus in ../test/*.tsv; make diff from the repository root runs the differential harness against the TypeScript build. expr.go and node.go carry original-port formatting that is not gofmt-clean—leave their unrelated regions as they are; every other file is gofmt-clean.

See ../AGENTS.md for the parity rules and the change checklist, and PLAN.md for the original porting plan.

Version

const Version = "0.5.0"

Documentation

Index

Constants

View Source
const (
	WhyType          = "type"
	WhyRequired      = "required"
	WhyClosed        = "closed"
	WhyCheck         = "check"
	WhyOne           = "One"
	WhySome          = "Some"
	WhyAll           = "All"
	WhyExact         = "Exact"
	WhyMin           = "Min"
	WhyMax           = "Max"
	WhyAbove         = "Above"
	WhyBelow         = "Below"
	WhyLen           = "Len"
	WhyNever         = "never"
	WhyRegexp        = "regexp"
	WhyEmpty         = "empty"
	WhyEmail         = "Email"
	WhyUrl           = "Url"
	WhyUuid          = "Uuid"
	WhyDateTime      = "DateTime"
	WhyIp            = "Ip"
	WhyIpv4          = "Ipv4"
	WhyIpv6          = "Ipv6"
	WhyDiscriminated = "Discriminated"
)

Why codes mirror the TS implementation's why values.

View Source
const Version = "0.5.3"

Variables

View Source
var (
	GAny      = Any
	GString   = String
	GNumber   = Number
	GBoolean  = Boolean
	GObject   = Object
	GArray    = Array
	GFunction = Function
	GInteger  = Integer
	GDate     = Date
)

G-prefixed aliases. Provided for users who want to dot-import the package without colliding with stdlib builtins (e.g. String/Number/Boolean tokens).

View Source
var (
	Any      = TypeToken{/* contains filtered or unexported fields */}
	String   = TypeToken{/* contains filtered or unexported fields */}
	Number   = TypeToken{/* contains filtered or unexported fields */}
	Boolean  = TypeToken{/* contains filtered or unexported fields */}
	Object   = TypeToken{/* contains filtered or unexported fields */}
	Array    = TypeToken{/* contains filtered or unexported fields */}
	Function = TypeToken{/* contains filtered or unexported fields */}
	Integer  = TypeToken{/* contains filtered or unexported fields */} // a number with no fractional part
	Date     = TypeToken{/* contains filtered or unexported fields */} // a time.Time value
)

Sentinel tokens for required fields (TS constructor-literal equivalent).

View Source
var Null any = nullT{}

Null is an explicit present null. Go cannot tell a missing argument from a nil one, so Validate(nil) means "no value supplied" (JS undefined) and defaults fill, mirroring TS Shape(x)(). Validate(Null) means the value is present and null (JS null), which is a type error against a typed shape. Inside a map or slice a plain nil already reads as present-null, because the key or index exists; Null is accepted there too and means the same thing.

Functions

func FromJSONSchema added in v0.3.0

func FromJSONSchema(schema any) (any, error)

FromJSONSchema builds a spec from a JSON Schema document, as decoded by encoding/json (map[string]any, []any, float64, bool, string, nil). Compile it with Shape, or compose it further with the builders.

func IsShape

func IsShape(v any) bool

IsShape reports whether v is a *Schema produced by this package.

func MustFromJSONSchema added in v0.3.0

func MustFromJSONSchema(schema any) any

MustFromJSONSchema is FromJSONSchema, panicking on error.

Types

type Argu

type Argu struct {
	// contains filtered or unexported fields
}

Argu is a positional-arguments validator returned from MakeArgu.

func MakeArgu

func MakeArgu(name string) Argu

MakeArgu creates an Argu validator with the given namespace name. Use the returned value to validate positional arguments against an ordered spec.

Argu := MakeArgu("mylib")
argmap, err := Argu([]any{2, "x"}, "foo", map[string]any{
    "a": Number,
    "b": String,
})
// argmap == map[string]any{"a": 2, "b": "x"}

Spec values may be type tokens, literal defaults, or builder *Node values. Skip(spec) makes a slot optional with positional shifting; Rest(spec) tail- captures remaining args into a slice.

func (Argu) Partial

func (a Argu) Partial(whence string, spec map[string]any) func([]any) (map[string]any, error)

Partial returns a closure that can be invoked with arg lists. Useful for building reusable signature validators.

func (Argu) Validate

func (a Argu) Validate(args []any, whence string, spec map[string]any) (map[string]any, error)

Validate runs the positional-arg matcher.

type Context

type Context struct {
	Err    []FieldError
	Custom map[string]any
	Refs   map[string]*node
	Match  bool
	// contains filtered or unexported fields
}

Context flows through validation. Custom validators may read/write Custom for cross-property state, and Refs is used by Define/Refer.

type FieldError

type FieldError struct {
	Path    string         // dot-notation property path (e.g. "users.0.email")
	PathArr []any          // path as array: array indices as ints, keys as strings
	Key     string         // the immediate key/index that failed
	Type    Kind           // node kind that ran the check
	Value   any            // failing input value
	Why     string         // why-code (type, required, closed, check, ...)
	Mark    int            // numeric mark (mirrors TS marks 1010, 4000, ...)
	Text    string         // human-readable message
	Args    map[string]any // extra context for custom checks
	Check   string         // name of the failing check (TS ErrDesc.check)
	// contains filtered or unexported fields
}

FieldError captures rich information about a single validation failure.

func (FieldError) Error

func (e FieldError) Error() string

type KeyExprOptions

type KeyExprOptions struct {
	// Disable turns key-expression parsing off (default is on).
	Disable bool
}

KeyExprOptions controls key-expression parsing.

type Kind

type Kind string

Kind identifies a normalized schema/value kind.

const (
	KindAny      Kind = "any"
	KindString   Kind = "string"
	KindNumber   Kind = "number"
	KindBoolean  Kind = "boolean"
	KindObject   Kind = "object"
	KindArray    Kind = "array"
	KindNull     Kind = "null"
	KindNaN      Kind = "nan"
	KindFunction Kind = "function"
	KindNever    Kind = "never"
	KindCheck    Kind = "check"
	KindRegexp   Kind = "regexp"
	KindInteger  Kind = "integer"
	KindDate     Kind = "date"
	KindList     Kind = "list"
)

type MetaOptions

type MetaOptions struct {
	Active bool
	Suffix string // default "$$"
}

MetaOptions controls metadata sidecar keys (e.g. "x$$" providing meta for "x").

type Node

type Node struct {
	// contains filtered or unexported fields
}

Node is the publicly exposed compiled-spec wrapper. Builders return *Node so users can chain (e.g. shape.Min(2, shape.String).Required()).

func Above

func Above(above any, spec ...any) *Node

Above specifies a strict lower bound on value or length.

func After

func After(fn func(val any, update *Update, state *State) bool, spec ...any) *Node

After runs a custom validator after structural type checks.

func All

func All(shapes ...any) *Node

All requires the value to satisfy every shape.

func Before

func Before(fn func(val any, update *Update, state *State) bool, spec ...any) *Node

Before runs a custom validator before structural type checks.

func Below

func Below(below any, spec ...any) *Node

Below specifies a strict upper bound on value or length.

func Catch added in v0.2.0

func Catch(fallback any, spec ...any) *Node

Catch replaces whatever fails inside with the fallback, raising nothing.

func Check

func Check(check any, spec ...any) *Node

Check installs a custom validation. Accepts a function of (val, update, state) or a *regexp.Regexp.

func Child

func Child(child any, spec ...any) *Node

Child sets a default child shape for an object (Open object child) or array.

func Closed

func Closed(spec ...any) *Node

Closed forbids additional properties on object schemas.

func Coerce added in v0.2.0

func Coerce(spec ...any) *Node

Coerce converts the value to the node's kind where the conversion is unambiguous, before the type check: a decimal string to a number, "true"/"false"/"1"/"0" to a boolean, a number or boolean to a string, an ISO 8601 string or a time value to a Date. Anything else is left alone, so the usual type error speaks.

func DateTime added in v0.2.0

func DateTime(spec ...any) *Node

DateTime accepts a strict ISO 8601 / RFC 3339 date-time string. The value stays a string; Coerce(Date) is the one that produces a time.Time.

func Default

func Default(dval any, spec ...any) *Node

Default sets an explicit default value, optionally narrowing the shape.

func Define

func Define(name string, spec ...any) *Node

Define names the current node so a later Refer with the same name can clone it.

func Describe added in v0.2.0

func Describe(description string, spec ...any) *Node

Describe attaches a description to the node, read back from Meta().

func Discriminated added in v0.2.0

func Discriminated(tag string, branches map[string]any) *Node

Discriminated chooses the branch by the value of the tag property. An object-shaped branch without the tag property has it added, as the literal it is keyed by.

func Email added in v0.2.0

func Email(spec ...any) *Node

Email accepts a string in email address form: a dot-atom local part and a dotted domain (no quoted local parts or address literals).

func Empty

func Empty(spec ...any) *Node

Empty allows the empty string for a String shape.

func Exact

func Exact(vals ...any) *Node

Exact requires the value equal one of the provided literals.

func Expr

func Expr(src string) (*Node, error)

Expr parses a string DSL into a *Node spec, mirroring TS Shape.expr.

Supported tokens:

  • Builder names: Required, Optional, Min, Max, Above, Below, Len, Check, Open, Closed, Skip, Ignore, Empty, Default, Fault, Never, Type, Exact, One, Some, All, Child, Rest, Define, Refer, Rename, Func, Key.
  • Type tokens: String, Number, Boolean, Object, Array, Function, Any.
  • Literals: JSON values (numbers, strings, true, false, null) and undefined/NaN.
  • Regexp: /pattern/.
  • Method chaining via dot: "String.Min(2).Max(10)".
  • Comma-separated args inside parentheses: "Min(2, String)".

func Extend added in v0.2.0

func Extend(extra any, spec ...any) *Node

Extend adds the properties of extra, an object shape, to an object shape. The result is a new node; the source is unchanged.

func Fault

func Fault(msg string, spec ...any) *Node

Fault sets a custom error message used when this node's validation fails.

func Func

func Func(spec ...any) *Node

Func declares a function-typed value (best-effort: any reflect.Func value). It is a builder, not a type token, so it does not require a value of itself: TS Func() leaves the node optional, and { n: Func() } accepts an object without n. The Function token is the required form.

func GAbove

func GAbove(above any, spec ...any) *Node

func GAfter

func GAfter(fn func(any, *Update, *State) bool, spec ...any) *Node

func GAll

func GAll(shapes ...any) *Node

func GBefore

func GBefore(fn func(any, *Update, *State) bool, spec ...any) *Node

func GBelow

func GBelow(below any, spec ...any) *Node

func GCatch added in v0.2.0

func GCatch(fallback any, spec ...any) *Node

func GCheck

func GCheck(check any, spec ...any) *Node

func GChild

func GChild(child any, spec ...any) *Node

func GClosed

func GClosed(spec ...any) *Node

func GCoerce added in v0.2.0

func GCoerce(spec ...any) *Node

func GDateTime added in v0.2.0

func GDateTime(spec ...any) *Node

func GDefault

func GDefault(d any, spec ...any) *Node

func GDefine

func GDefine(name string, spec ...any) *Node

func GDescribe added in v0.2.0

func GDescribe(description string, spec ...any) *Node

func GDiscriminated added in v0.2.0

func GDiscriminated(tag string, branches map[string]any) *Node

func GEmail added in v0.2.0

func GEmail(spec ...any) *Node

func GEmpty

func GEmpty(spec ...any) *Node

func GExact

func GExact(vals ...any) *Node

func GExtend added in v0.2.0

func GExtend(extra any, spec ...any) *Node

func GFault

func GFault(msg string, spec ...any) *Node

func GFunc

func GFunc(spec ...any) *Node

func GIgnore

func GIgnore(spec ...any) *Node

func GIp added in v0.2.0

func GIp(spec ...any) *Node

func GIpv4 added in v0.2.0

func GIpv4(spec ...any) *Node

func GIpv6 added in v0.2.0

func GIpv6(spec ...any) *Node

func GKey

func GKey(args ...any) *Node

func GLen

func GLen(length int, spec ...any) *Node

func GMax

func GMax(max any, spec ...any) *Node

func GMin

func GMin(min any, spec ...any) *Node

func GNever

func GNever(spec ...any) *Node

func GNullable added in v0.2.0

func GNullable(spec ...any) *Node

G-prefixed aliases for the builders added since v10, for a dot-import.

func GOmit added in v0.2.0

func GOmit(names any, spec ...any) *Node

func GOne

func GOne(shapes ...any) *Node

func GOpen

func GOpen(spec ...any) *Node

func GOptional

func GOptional(spec ...any) *Node

func GPartial added in v0.2.0

func GPartial(spec ...any) *Node

func GPick added in v0.2.0

func GPick(names any, spec ...any) *Node

G-prefixed aliases, for a dot-import alongside other packages.

func GRefer

func GRefer(name string, spec ...any) *Node

func GRename

func GRename(name string, spec ...any) *Node

func GRequired

func GRequired(spec ...any) *Node

Builder aliases (functions, not vars, so they can be method-valued).

func GRest

func GRest(child any, spec ...any) *Node

func GSkip

func GSkip(spec ...any) *Node

func GSome

func GSome(shapes ...any) *Node

func GTransform added in v0.2.0

func GTransform(fn func(val any, state *State) any, spec ...any) *Node

func GType

func GType(kind any, spec ...any) *Node

func GUrl added in v0.2.0

func GUrl(spec ...any) *Node

func GUuid added in v0.2.0

func GUuid(spec ...any) *Node

func Ignore

func Ignore(spec ...any) *Node

Ignore behaves like Skip but also suppresses errors raised on the value.

func Ip added in v0.2.0

func Ip(spec ...any) *Node

Ip accepts an IPv4 or IPv6 address.

func Ipv4 added in v0.2.0

func Ipv4(spec ...any) *Node

Ipv4 accepts a dotted-quad IPv4 address.

func Ipv6 added in v0.2.0

func Ipv6(spec ...any) *Node

Ipv6 accepts an IPv6 address in RFC 4291 text form.

func Key

func Key(args ...any) *Node

Key replaces the value with the validation key (or path slice).

  • Key() → uses the immediate parent key as the value.
  • Key(depth) → reads `depth` levels up the path.
  • Key(depth, sep) → joins the path slice with sep into a string.

func Len

func Len(length int, spec ...any) *Node

Len requires an exact value or collection length.

func Max

func Max(max any, spec ...any) *Node

Max specifies a maximum value or length.

func Min

func Min(min any, spec ...any) *Node

func MustExpr

func MustExpr(src string) *Node

MustExpr is Expr that panics on error.

func Never

func Never(spec ...any) *Node

Never always fails to match.

func Nullable added in v0.2.0

func Nullable(spec ...any) *Node

Nullable accepts an explicit null as the value. Whether the value may be absent is still governed by Required/Optional.

func Omit added in v0.2.0

func Omit(names any, spec ...any) *Node

Omit drops the named properties of an object shape. names is a string or a list of strings. The result is a new node; the source is unchanged.

func One

func One(shapes ...any) *Node

One requires the value to satisfy exactly one of the given shapes.

func Open

func Open(spec ...any) *Node

Open allows additional properties on object schemas.

func Optional

func Optional(spec ...any) *Node

Optional marks the value as optional.

func Partial added in v0.2.0

func Partial(spec ...any) *Node

Partial makes every declared property of an object shape optional. The result is a new node; the source is unchanged.

func Pick added in v0.2.0

func Pick(names any, spec ...any) *Node

Pick keeps only the named properties of an object shape. names is a string or a list of strings. The result is a new node; the source is unchanged.

func Refer

func Refer(name string, spec ...any) *Node

Refer substitutes the named node at validation time.

func ReferWith

func ReferWith(name string, opts ReferOptions, spec ...any) *Node

ReferWith is Refer with explicit options.

func Rename

func Rename(name string, spec ...any) *Node

Rename renames a property after validation. Use only inside object child shapes.

func RenameWith

func RenameWith(name string, opts RenameOptions, spec ...any) *Node

RenameWith is Rename with explicit options (Keep, Claim).

func Required

func Required(spec ...any) *Node

Required marks the value as required. Single-arg form Required(spec) wraps an existing spec; zero-arg Required() yields a required Any.

func Rest

func Rest(child any, spec ...any) *Node

Rest declares a tail-shape for arrays past the tuple positions.

func Skip

func Skip(spec ...any) *Node

Skip marks a value as skippable: optional, no default injection.

func Some

func Some(shapes ...any) *Node

Some requires the value to satisfy at least one shape.

func Transform added in v0.2.0

func Transform(fn func(val any, state *State) any, spec ...any) *Node

Transform replaces a valid value with a function of it. An invalid one fails as it would have, with the same errors.

func Type

func Type(kind any, spec ...any) *Node

Type explicitly asserts a kind on the node.

func Url added in v0.2.0

func Url(spec ...any) *Node

Url accepts an absolute URL: scheme://host with optional user, port, path, query and fragment.

func Uuid added in v0.2.0

func Uuid(spec ...any) *Node

Uuid accepts a UUID in 8-4-4-4-12 hex form, any version.

func (*Node) Above

func (n *Node) Above(above any) *Node

Above (chained).

func (*Node) After

func (n *Node) After(fn func(val any, update *Update, state *State) bool) *Node

After (chained).

func (*Node) Any added in v0.2.0

func (n *Node) Any() *Node

Any (chained): the value may be anything.

func (*Node) Array added in v0.2.0

func (n *Node) Array() *Node

Array (chained).

func (*Node) Before

func (n *Node) Before(fn func(val any, update *Update, state *State) bool) *Node

Before (chained).

func (*Node) Below

func (n *Node) Below(below any) *Node

Below (chained).

func (*Node) Boolean added in v0.2.0

func (n *Node) Boolean() *Node

Boolean (chained).

func (*Node) Catch added in v0.2.0

func (n *Node) Catch(fallback any) *Node

Catch (chained).

func (*Node) Check

func (n *Node) Check(check any) *Node

Check (chained).

func (*Node) Child

func (n *Node) Child(child any) *Node

Child (chained).

func (*Node) Closed

func (n *Node) Closed() *Node

Closed (chained).

func (*Node) Coerce added in v0.2.0

func (n *Node) Coerce() *Node

Coerce (chained).

func (*Node) Date added in v0.2.0

func (n *Node) Date() *Node

Date (chained).

func (*Node) DateTime added in v0.2.0

func (n *Node) DateTime() *Node

DateTime (chained).

func (*Node) Default

func (n *Node) Default(dval any) *Node

Default (chained).

func (*Node) Define added in v0.2.0

func (n *Node) Define(name string) *Node

Define (chained): name this node so a later Refer can clone it.

func (*Node) Describe added in v0.2.0

func (n *Node) Describe(description string) *Node

Describe (chained).

func (*Node) Email added in v0.2.0

func (n *Node) Email() *Node

Email (chained).

func (*Node) Empty

func (n *Node) Empty() *Node

Empty (chained).

func (*Node) Exact

func (n *Node) Exact(vals ...any) *Node

Exact (chained).

func (*Node) Extend added in v0.2.0

func (n *Node) Extend(extra any) *Node

Extend (chained): returns a new node, leaving the receiver as it was.

func (*Node) Fault

func (n *Node) Fault(msg string) *Node

Fault (chained).

func (*Node) Func

func (n *Node) Func() *Node

Func (chained): the receiver's required state is kept.

func (*Node) Function added in v0.2.0

func (n *Node) Function() *Node

Function (chained).

func (*Node) Ignore

func (n *Node) Ignore() *Node

Ignore (chained).

func (*Node) Inner

func (n *Node) Inner() *node

Inner exposes the underlying private node for advanced introspection.

func (*Node) Integer added in v0.2.0

func (n *Node) Integer() *Node

Integer (chained).

func (*Node) Ip added in v0.2.0

func (n *Node) Ip() *Node

Ip (chained).

func (*Node) Ipv4 added in v0.2.0

func (n *Node) Ipv4() *Node

Ipv4 (chained).

func (*Node) Ipv6 added in v0.2.0

func (n *Node) Ipv6() *Node

Ipv6 (chained).

func (*Node) JSONSchema added in v0.2.0

func (n *Node) JSONSchema() map[string]any

JSONSchema renders a built node as a JSON Schema document.

func (*Node) Kind

func (n *Node) Kind() Kind

Kind returns the underlying type kind.

func (*Node) Len

func (n *Node) Len(length int) *Node

Len (chained).

func (*Node) Max

func (n *Node) Max(max any) *Node

Max (chained).

func (*Node) Meta added in v0.2.0

func (n *Node) Meta() map[string]any

Meta returns the node's metadata: sidecar keys, and Describe's description.

func (*Node) Min

func (n *Node) Min(min any) *Node

Min (chained).

func (*Node) Never

func (n *Node) Never() *Node

Never (chained).

func (*Node) Nullable added in v0.2.0

func (n *Node) Nullable() *Node

Nullable (chained).

func (*Node) Number added in v0.2.0

func (n *Node) Number() *Node

Number (chained).

func (*Node) Object added in v0.2.0

func (n *Node) Object() *Node

Object (chained).

func (*Node) Omit added in v0.2.0

func (n *Node) Omit(names any) *Node

Omit (chained): returns a new node, leaving the receiver as it was.

func (*Node) Open

func (n *Node) Open() *Node

Open (chained).

func (*Node) Optional

func (n *Node) Optional() *Node

Optional (chained).

func (*Node) Partial added in v0.2.0

func (n *Node) Partial() *Node

Partial (chained): returns a new node, leaving the receiver as it was.

func (*Node) Pick added in v0.2.0

func (n *Node) Pick(names any) *Node

Pick (chained): returns a new node, leaving the receiver as it was.

func (*Node) Refer added in v0.2.0

func (n *Node) Refer(name string) *Node

Refer (chained): substitute the named node at validation time.

func (*Node) Rename added in v0.2.0

func (n *Node) Rename(name string) *Node

Rename (chained): rename this property after validation.

func (*Node) Required

func (n *Node) Required() *Node

Required (chained) on a Node.

func (*Node) Rest

func (n *Node) Rest(child any) *Node

Rest (chained).

func (*Node) Skip

func (n *Node) Skip() *Node

Skip (chained).

func (*Node) Transform added in v0.2.0

func (n *Node) Transform(fn func(val any, state *State) any) *Node

Transform (chained).

func (*Node) Type added in v0.2.0

func (n *Node) Type(kind any) *Node

Type (chained): assert a kind, given a Kind, TypeToken, kind name or node.

func (*Node) Url added in v0.2.0

func (n *Node) Url() *Node

Url (chained).

func (*Node) Uuid added in v0.2.0

func (n *Node) Uuid() *Node

Uuid (chained).

type ReferOptions

type ReferOptions struct {
	// Fill substitutes even when the value is absent (not for self-recursion).
	Fill bool
	// Strict makes a name with no Define an error, rather than a Refer that
	// does nothing.
	Strict bool
}

ReferOptions controls Refer behaviour. Fill substitutes the referenced node even when the input value is missing/nil, allowing recursive structure.

type RenameOptions

type RenameOptions struct {
	Keep  bool
	Claim []string
}

RenameOptions controls Rename behaviour.

  • Keep: retain the original key in addition to writing under the new name.
  • Claim: list of alternative source keys to read from when the renamed key is missing on the input. Useful for migrating legacy property names.

type Schema

type Schema struct {
	// contains filtered or unexported fields
}

Schema is a compiled shape specification.

func Build

func Build(spec any) (*Schema, error)

Build reads the declarative JSON of a shape, what JSON() writes: every string is an expression (the example of a key expression is a value, so a string there is the string itself), and a "$$" key applies an expression to the object that holds it, with the "$$0", "$$1", ... sidecars beside it as the arguments an expression cannot spell inline.

func MustShape

func MustShape(spec any) *Schema

MustShape compiles a schema and panics if invalid.

func MustShapeWith

func MustShapeWith(spec any, opts ShapeOptions) *Schema

MustShapeWith is ShapeWith that panics on error.

func Shape

func Shape(spec any) (*Schema, error)

Shape compiles a schema-by-example specification with default options. Note: keyexpr is enabled by default — keys like "x: Min(1)" are parsed.

func ShapeWith

func ShapeWith(spec any, opts ShapeOptions) (*Schema, error)

ShapeWith compiles a schema-by-example specification with the given options.

func (*Schema) Error

func (s *Schema) Error(input any) []FieldError

Error returns the FieldErrors produced by validating input. Returns nil if the input is valid.

func (*Schema) JSON added in v0.5.1

func (s *Schema) JSON() (out any, err error)

JSON is the declarative JSON of the shape, which Build reads back.

func (*Schema) JSONSchema added in v0.2.0

func (s *Schema) JSONSchema() map[string]any

JSONSchema renders the schema as a JSON Schema document.

func (*Schema) Match

func (s *Schema) Match(input any) bool

Match reports whether input satisfies the schema, without mutating input or returning errors. Mirrors TS .match().

func (*Schema) Node

func (s *Schema) Node() *node

Node returns the underlying root node for advanced introspection.

func (*Schema) Spec

func (s *Schema) Spec() any

Spec returns a structural representation of the compiled schema.

func (*Schema) Standard added in v0.1.3

func (s *Schema) Standard() StandardSchema

Standard returns the Standard Schema V1-style interface for this schema. The returned Validate never panics; it reports failures as issues.

func (*Schema) String

func (s *Schema) String() string

String renders a debug representation of the schema.

func (*Schema) Valid

func (s *Schema) Valid(input any) bool

Valid is an alias of Match retained for API parity. Mirrors TS .valid().

func (*Schema) Validate

func (s *Schema) Validate(input any) (any, error)

Validate validates and normalizes input. Returns the produced (defaults injected) value plus a *ValidationError if any errors occurred.

func (*Schema) ValidateCtx

func (s *Schema) ValidateCtx(input any, ctx *Context) (any, error)

ValidateCtx is Validate with an explicit Context (custom validators may use it).

func (*Schema) ValidateInto added in v0.3.0

func (s *Schema) ValidateInto(input any, out any) error

ValidateInto validates input and fills out, a pointer to a struct (or to any value encoding/json can decode into), with the produced value.

type ShapeOptions

type ShapeOptions struct {
	KeyExpr KeyExprOptions
	Meta    MetaOptions
	ValExpr ValExprOptions
}

ShapeOptions configures schema compilation. Mirrors TS ShapeOptions.

Defaults:

  • KeyExpr.Active = true (interpret object keys like "x: Min(1)")
  • Meta.Active = false (sidecar metadata via "x$$" keys)
  • Meta.Suffix = "$$"
  • ValExpr.Active = false (string values become builder expressions)
  • ValExpr.KeyMark = "$$"

type StandardIssue added in v0.1.3

type StandardIssue struct {
	Message string
	Path    []any
}

StandardIssue mirrors a Standard Schema V1 issue: a human-readable message and the path to the offending value (array indices as ints, object keys as strings, matching FieldError.PathArr).

type StandardResult added in v0.1.3

type StandardResult struct {
	Value  any             // produced value (defaults injected) when Issues is empty
	Issues []StandardIssue // validation problems; empty on success
}

StandardResult is the outcome of StandardSchema.Validate: on success Value is set and Issues is empty; on failure Issues is populated. Mirrors the TS `~standard.validate()` result.

type StandardSchema added in v0.1.3

type StandardSchema struct {
	Version  int                            // always 1
	Vendor   string                         // always "shape"
	Validate func(input any) StandardResult // non-throwing validation
}

StandardSchema is the Standard Schema V1-style interface for a compiled shape.

type State

type State struct {
	Path    []string // path stack from root; current key at end
	PathArr []any    // path as array: array indices as ints, object keys as strings
	Key     string   // immediate key/index name
	Value   any      // current value being validated
	Node    *node    // current node
	Parent  any      // parent map/slice (for Rename and similar)
	Match   bool     // true when invoked via .Match (no mutation, no error report)
	Ctx     *Context // user/custom context
	// contains filtered or unexported fields
}

State is passed to custom validators and tracks the current validation cursor.

type TypeToken

type TypeToken struct {
	// contains filtered or unexported fields
}

TypeToken marks a required type in schema-by-example maps.

func (TypeToken) Kind

func (t TypeToken) Kind() Kind

type Update

type Update struct {
	Done    bool   // stop running further checks
	Why     string // why code on failure
	Mark    int    // numeric mark on failure
	Err     any    // string, FieldError, or []FieldError
	Val     any    // replacement value
	HasVal  bool   // true if Val should override
	Node    *node  // override node (used by Refer)
	Replace bool   // (compat marker, not currently consulted)
}

Update is the bag a custom validator fills in to influence validation.

type ValExprOptions

type ValExprOptions struct {
	Active  bool
	KeyMark string // default "$$"
}

ValExprOptions controls value-as-expression parsing.

type ValidationError

type ValidationError struct {
	Issues []FieldError
	// contains filtered or unexported fields
}

ValidationError aggregates one or more FieldErrors. A terse one, the collector of a Match, only counts them.

func (*ValidationError) Error

func (e *ValidationError) Error() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL