Skip to content
LogoLogo

Runtime Validation

Derive schemas from the types you already use

Static types disappear before untrusted data reaches your program. VibeLang uses comptime to derive runtime validators and codecs from ordinary types.

Derive a Schema

Start with the application type:

import { comptime } from "vibelang:comptime"
 
type SignupRequest = {
  email: string
  age: number
}
 
const SignupRequestSchema = comptime(Schema.derive<SignupRequest>())

The compiler produces a runtime parser whose success type is SignupRequest. There is no second schema declaration to keep synchronized.

Parse Unknown Data

External input should remain unknown until it is validated:

function handleSignup(
  body: unknown,
): Result<User, ValidationError | CreateUserError> {
  const users = Users.context()
  const request = SignupRequestSchema.parse(body).unwrap()
  return users.create(request)
}

A validation problem is an Error in the Result. It composes with the same propagation, matching, and must-use rules as filesystem or domain failures.

Decode at a Boundary

Platform APIs can accept a type or derived schema directly:

const http = HttpClient.context()
const response = (await http.get("/account")).unwrap()
const account = (await response.json(Account)).unwrap()

The type argument asks the compiler or standard library to use the derived codec. Invalid JSON and structurally invalid data remain distinguishable failures when the API declares them separately.

Trusted Compiler Inputs

JSON imported by the compiler is different from runtime JSON:

import config from "./config.json" with { type: "json" }

The compiler has already parsed a known project file, so a handwritten runtime schema is not required merely to establish its shape. Add mode: "const" to the attributes when deeply readonly literal preservation is required.

Data received over HTTP, from a database, or from a user is still untrusted and should be parsed from unknown.

More Than Validation

The same type reflection can derive:

  • encoders and decoders
  • stable equality and hashing
  • durable boundary codecs
  • JSON Schema or OpenAPI descriptions
  • RPC bindings
  • form and configuration metadata

The derived artifact can be customized where a type alone does not carry enough policy. The type remains the structural source of truth.

Reifiable Types

Not every TypeScript type can become a runtime check. Structural data types, tagged unions, tuples, arrays, and supported refinements are natural candidates. Types involving arbitrary functions, erased nominal facts, or unconstrained any need an explicit codec or are rejected at a durable boundary.

The exact reification rules are specification work. Native casts must never use a type assertion to reinterpret memory unsafely.