ExisJS

exisjs/validator

The exisjs/validator module provides the tex schema builder, TexEngine (schemas compile once into plain JS checks), data transformation modifiers, and pagination helpers for request body, query, and parameter validation.


Module Exports

import {  tex,  TexBuilder,  TexEngine,  TexTransformedEngine,  TexDiscriminatedUnionEngine,  TexType,  ValidatorError,  paginate,  getPaginationSkip,} from 'exisjs/validator'
import type {  Infer,  ResolveSchema,  TexBaseOptions,  TexStringOptions,  TexNumberOptions,  TexBooleanOptions,  TexArrayOptions,  TexEnumOptions,  TexDateOptions,  TexFileOptions,  TexPasswordOptions,  TexObjectOptions,  TexUrlOptions,  SafeParseResult,  ValidationErrorDescriptor,  PaginationMeta,  PaginatedResult,} from 'exisjs/validator'

Schema Factory (tex)

The tex instance exposes factory functions for all standard primitives, complex structures, and specialized validators.

Primitive Validators

MethodReturn TypeOptions InterfaceDescription
tex.string(options?)TexType<string>TexStringOptionsString validation with trimming, HTML escaping, min/max/exact length, pattern (RegExp), masking, coercion, and injection checks.
tex.number(options?)TexType<number>TexNumberOptionsNumber validation with optional string coercion (coerce: true), whole numbers (int: true) and min/max limits.
tex.url(options?)TexType<string>TexUrlOptionsAbsolute URL; http / https only unless protocols lists others.
tex.boolean(options?)TexType<boolean>TexBooleanOptionsBoolean validation with string/numeric coercion ("true", "1" -> true).
tex.email(options?)TexType<string>TexStringOptionsStandard RFC 5322 email format validation.
tex.uuid(options?)TexType<string>{ version?: 1 | 4 }Validates UUID strings (v1 or v4).
tex.cuid(options?)TexType<string>TexBaseOptionsValidates CUID identifiers.
tex.date(options?)TexType<Date>TexDateOptionsValidates Date objects, ISO 8601 strings, or numeric timestamps (minDate, maxDate).
tex.password(options?)TexType<string>TexPasswordOptionsEnforces password complexity (uppercase, lowercase, numbers, symbols).
tex.creditCard(options?)TexType<string>{ mask?: boolean }Luhn-validated credit card numbers with optional masking.
tex.file(options?)TexType<ExisFile>TexFileOptionsMultipart file upload validation (maxSize, mimeTypes).
tex.enum(values, options?)TexType<T>TexEnumOptions<T>Validates against a fixed array or tuple of string literal values.
tex.literal(value)TexType<T>NoneValidates an exact primitive literal (string | number | boolean).
tex.record(schema)TexType<Record<string, V>>NoneValidates key-value dictionaries matching value schema V.
tex.union(schemas)TexType<T>NoneValidates against any one of the provided schemas.
tex.any()TexType<any>NoneAllows any arbitrary unconstrained value.

Explicit Coercion (tex.coerce)

The tex.coerce namespace provides explicit coercion primitives for query parameters, path variables, and form inputs:

MethodReturn TypeDescription
tex.coerce.number(options?)TexType<number>Coerces string and numeric input to number.
tex.coerce.boolean(options?)TexType<boolean>Coerces "true", "false", "1", "0" to boolean.
tex.coerce.date(options?)TexType<Date>Coerces ISO 8601 strings and timestamps to native Date.
tex.coerce.string(options?)TexType<string>Coerces numeric and primitive inputs to string.

Complex Structures

tex.object(shape, options?)

Validates an object against a structured field schema.

Options: strict (reject keys not in the schema), and optional, nullable, nullish for an object used as a field (the same as calling .optional(), .nullable() or .nullish()). Any other option throws, so a misspelled option in plain JavaScript is not silently ignored.

const UserSchema = tex.object({  id: tex.string(),  profile: tex.object({    bio: tex.string().optional(),    avatarUrl: tex.string().optional(),  }),  roles: tex.array(tex.enum(['admin', 'user', 'guest'] as const)),})

tex.discriminatedUnion(discriminator, variants)

Validates polymorphic object variants by inspecting a literal or enum discriminator property:

const ActionSchema = tex.discriminatedUnion('kind', [  tex.object({ kind: tex.literal('create'), name: tex.string() }),  tex.object({ kind: tex.literal('delete'), id: tex.uuid() }),])

tex.array(schema, options?)

Validates an array of items matching a schema or object shape shorthand.

const CartSchema = tex.object({  items: tex.array({    sku: tex.string(),    quantity: tex.number({ min: 1 }),  }, { min: 1, max: 50 }),})

tex.env(shape)

Constructs a boot-time environment parser that automatically enables coercion for all numeric, boolean, date, and array fields.

export const env = tex.env({  PORT: tex.number({ default: 3000 }),  HOST: tex.string({ default: '0.0.0.0' }),  DATABASE_URL: tex.string(),  CORS_ORIGINS: tex.array(tex.string()),}).parse(process.env)

Modifier Methods & Schema Chaining

All TexType instances provide fluent modifier and documentation methods:

MethodSignatureDescription
.optional()() => TexType<T | undefined>Marks the field as optional (permits undefined).
.nullable()() => TexType<T | null>Marks the field as nullable (permits null).
.nullish()() => TexType<T | null | undefined>Permits both null and undefined.
.default(val)(val: T | (() => T)) => TexType<T>Sets a static value or dynamic factory returned when input is omitted.
.describe(text)(description: string) => thisSets documentation description metadata for OpenAPI 3.1 generation.
.example(val)(example: any) => thisSets example value metadata for OpenAPI 3.1 generation.
.sanitize(...fns)(...fns: ((val: T) => T)[]) => thisExecutes synchronous transformation functions before validation.
.refine(fn, message)(fn: (val: T) => boolean, msg: string) => thisAdds custom validation logic evaluated after native validation.
.refineAsync(fn, message)(fn: (val: T) => Promise<boolean>, msg: string) => thisAdds asynchronous validation logic (e.g. database lookups).
.transform(fn)<Out>(fn: (val: T) => Out) => TexTransformed<Out>Applies a synchronous transformation to validated field data.
.toOpenApi()() => Record<string, any>Converts the type definition into an OpenAPI 3.1 schema object.

Every method returns a new schema and leaves the original unchanged, so one field schema can be reused in several objects.


TexEngine Schema Object Methods

TexEngine instances constructed via tex.object(), tex.env(), or derived builders provide composition and execution methods:

MethodSignatureDescription
optional()() => TexEngine<T | undefined>Used as a field of another object: the field may be left out.
nullable()() => TexEngine<T | null>Used as a field of another object: the field may be null.
nullish()() => TexEngine<T | null | undefined>Used as a field of another object: the field may be left out or null.
partial()() => TexEngine<Partial<T>>Returns a copy of the schema with all fields marked optional.
pick(keys)<K extends keyof T>(keys: K[]) => TexEngine<Pick<T, K>>Creates a new schema containing only the specified keys.
omit(keys)<K extends keyof T>(keys: K[]) => TexEngine<Omit<T, K>>Creates a new schema omitting the specified keys.
extend(shape)<E>(extension: E) => TexEngine<T & ResolveSchema<E>>Merges additional or overridden fields into the existing schema.
merge(other)<U>(other: TexEngine<U>) => TexEngine<T & U>Deeply merges two TexEngine schemas into a combined validator.
refine(fn, opts?)(fn: (data: T) => boolean, opts?: string | { message?, path? }) => TexEngine<T>A check across fields, run once every field is valid; the error is reported on path.
transform(fn)<Out>(fn: (data: T) => Out) => TexTransformedEngine<T, Out>Applies post-validation data reshaping to the entire schema output.
parse(data)(data: unknown) => TSynchronously parses and validates data, throwing ValidatorError on failure.
parseAsync(data)(data: unknown) => Promise<T>Asynchronously parses and validates data, evaluating async refinements.
safeParse(data)(data: unknown) => { success: true, data: T } | { success: false, errors, error: ValidatorError }Validates data without throwing; errors is the list of { path, message, code, ... }.
safeParseAsync(data)(data: unknown) => Promise<SafeParseResult<T>>safeParse that also runs async refinements.
toOpenApi()() => Record<string, any>Translates the schema into an OpenAPI 3.1 JSON schema representation.

TypeScript Type Extraction

Extract static TypeScript types from any schema using Infer<T> or ResolveSchema<T>:

import { tex, type Infer, type ResolveSchema } from 'exisjs/validator'
const CreateProductSchema = tex.object({  title: tex.string(),  price: tex.number({ min: 0 }),  tags: tex.array(tex.string()).optional(),})
// Using Infer:type CreateProductInput = Infer<typeof CreateProductSchema>
// Using ResolveSchema:type ProductDto = ResolveSchema<typeof CreateProductSchema>

Pagination Utilities

tex.pagination(options?)

Creates a pre-configured query string schema for paginated endpoints:

const paginationQuery = tex.pagination({  defaultLimit: 20,  maxLimit: 100,})

getPaginationSkip(query)

Calculates database offset values (skip, limit, page) from query inputs.

const { skip, limit, page } = getPaginationSkip({ page: 3, limit: 25 })// Result: skip = 50, limit = 25, page = 3

paginate(items, total, options)

Wraps an array of items into a standardized paginated response envelope:

const result = paginate(items, 250, { page: 1, limit: 20 })

Envelope Output Structure

export interface PaginatedResult<T> {  data: T[]  pagination: {    page: number    limit: number    total: number    totalPages: number    hasNextPage: boolean    hasPrevPage: boolean    hasNext: boolean    hasPrev: boolean  }}

Error Handling & Diagnostics

When validation fails, TexEngine raises a ValidatorError containing an array of structured issue descriptors:

export interface ValidationErrorDescriptor {  path: string      // Property key or nested path (e.g. "user.email", "items[0].sku")  message: string   // Descriptive validation error message  expected?: string // Expected constraint or format  received?: string // Received value type representation  code?: string     // Machine-readable error code (e.g. MISSING_REQUIRED_FIELD)}