Context: Working with tosijs-schema
You are an expert in tosijs-schema, a lightweight, schema-first, LLM-native replacement for Zod. Use this guide to generate correct code, migrate from Zod, and understand the library's design philosophy.
1. Core Philosophy & Design
- Schema-First: The primary artifact is a standard JSON Schema object.
tosijs-schemais a fluent API to generate these schemas. - LLM-Native: The generated schemas are optimized for LLM consumption (cleaner, flatter, fewer tokens than Zod-to-JSON-Schema adapters).
- Strict by Default: Objects automatically set
additionalProperties: falseand mark all keys asrequiredto satisfy OpenAI Structured Output requirements out-of-the-box. - Performance: Uses "Ghost Constraints" for expensive checks (like
maxPropertieson large objects) and a "prime-jump" strategy for validating large arrays in O(1) time. - Validation Separation: Separates "Is this valid?" (fast, boolean return) from "Why is it invalid?" (detailed debugging).
2. Basic Setup & Syntax
Imports
import { s, type Infer } from 'tosijs-schema'
Defining Schemas
Use the s proxy to define schemas. The syntax is property-based and chainable.
const UserSchema = s.object({
id: s.string.uuid, // Format shorthand
username: s.string.min(3).max(20), // Chainable constraints
email: s.email, // First-class email type (no .string wrapper needed)
age: s.integer.min(0).optional, // Optional integer
tags: s.array(s.string).min(1), // Array with constraints
role: s.enum(['admin', 'user']), // Enums
meta: s.record(s.string), // Record/Dictionary
})
Type Inference
Inference works similarly to Zod but exports Infer directly.
type User = Infer<typeof UserSchema>
Accessing the JSON Schema
You can access the raw JSON schema object via the .schema property.
console.log(UserSchema.schema)
// Outputs standard JSON Schema object: { type: "object", properties: { ... } }
3. Validation API
Crucial Difference from Zod:
tosijs-schemavalidation is optimized for speed and returns a boolean by default.- It does not throw errors or return a parsed object like Zod's
.parse().
const data = { ... };
// Fast validation (returns true/false)
if (UserSchema.validate(data)) {
// logic here
} else {
// Handle invalid data
}
4. Migration Guide (Zod vs. tosijs-schema)
| Feature | Zod (z) |
tosijs-schema (s) |
|---|---|---|
| String | z.string() |
s.string |
z.string().email() |
s.email (First-class citizen) |
|
| UUID | z.string().uuid() |
s.string.uuid or s.uuid |
| Optional | schema.optional() |
schema.optional (Property, not method) |
| Objects | z.object({...}) |
s.object({...}) |
| Strict Mode | z.object({...}).strict() |
Default (No method needed) |
| Arrays | z.array(schema) |
s.array(schema) |
| Enums | z.enum(['a', 'b']) |
s.enum(['a', 'b']) |
| Unions | z.union([a, b]) |
s.union([a, b]) |
| Inference | z.infer<typeof T> |
Infer<typeof T> |
| Metadata | .describe("...") |
.describe("...") / .title("...") |
5. Monadic Pipelines (M)
tosijs-schema includes a "Railway Oriented Programming" module for building type-safe tool chains. This is especially useful for AI Agents, ensuring that hallucinations or bad data are caught immediately at the source (Input vs Output) rather than cascading.
1. Guarded Functions (M.func)
Create functions that enforce schemas on both input and output.
import { M, s } from 'tosijs-schema'
// M.func(InputSchema, OutputSchema, Implementation)
const getSize = M.func(s.string, s.number, (str) => {
return str.length
})
// Usage:
const len = getSize('hello') // Returns 5
// getSize(123) // Throws SchemaError (Input mismatch)
2. Execution Contexts (new M)
Chain multiple functions together. The execution context handles error propagation automatically.
const pipeline = new M({
getSize,
isEven: M.func(s.number, s.boolean, (n) => n % 2 === 0),
})
const result = pipeline
.getSize('hello') // Output: 5
.isEven() // Input: 5 -> Output: false
.result() // Returns false | Error
// If any step fails schema validation, .result() returns the specific error.
6. Advanced Features
Ghost Constraints
Constraints that are computationally expensive (O(N)) are documented in the schema but skipped by the runtime validator for performance (O(1)).
- Example:
.max(n)on Objects/Records. minPropertiesis strictly validated.maxPropertiesis a "Ghost" constraint (documentation only).
Metadata & LLM Optimization
Use metadata methods to enrich schemas for LLMs or OpenAPI docs without affecting runtime validation.
const ApiKey = s.string
.min(32)
.describe("The user's secret API key") // standard JSON schema "description"
.title('API Key')
.default('sk-...')
Date Handling
tosijs-schema treats dates as strings with format validation, aligning with JSON transport.
const Timestamp = s.string.datetime // Validates ISO string format
6. Common Patterns & Gotchas
- Chaining Order: Primitives (like
s.string) start the chain. Constraints (.min()) and metadata (.describe()) follow. The.optionalflag can be placed anywhere in the chain but usually goes last for readability. - No Transformers: Unlike Zod,
tosijsis a pure validation/schema library. It does not "transform" data (e.g., string to number coercion) during validation. - Strict Objects: Remember that
s.object()disallows unknown keys by default. If you need a flexible object, uses.record()or explicitly allow additional properties if the API supports it (though strict is preferred for LLM outputs). - Tuples: Use
s.tuple([s.string, s.number])for fixed-length arrays.
7. Example: LLM Structured Output
When defining a response format for an LLM:
const ResponseSchema = s.object({
reasoning: s.string.describe('Step-by-step thinking process'),
final_answer: s.string.describe('The concise final answer'),
confidence: s.number
.min(0)
.max(1)
.describe('Confidence score between 0 and 1'),
})
// Pass to LLM
const jsonSchema = ResponseSchema.schema