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
| Method | Return Type | Options Interface | Description |
|---|---|---|---|
tex.string(options?) | TexType<string> | TexStringOptions | String validation with trimming, HTML escaping, min/max/exact length, pattern (RegExp), masking, coercion, and injection checks. |
tex.number(options?) | TexType<number> | TexNumberOptions | Number validation with optional string coercion (coerce: true), whole numbers (int: true) and min/max limits. |
tex.url(options?) | TexType<string> | TexUrlOptions | Absolute URL; http / https only unless protocols lists others. |
tex.boolean(options?) | TexType<boolean> | TexBooleanOptions | Boolean validation with string/numeric coercion ("true", "1" -> true). |
tex.email(options?) | TexType<string> | TexStringOptions | Standard RFC 5322 email format validation. |
tex.uuid(options?) | TexType<string> | { version?: 1 | 4 } | Validates UUID strings (v1 or v4). |
tex.cuid(options?) | TexType<string> | TexBaseOptions | Validates CUID identifiers. |
tex.date(options?) | TexType<Date> | TexDateOptions | Validates Date objects, ISO 8601 strings, or numeric timestamps (minDate, maxDate). |
tex.password(options?) | TexType<string> | TexPasswordOptions | Enforces 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> | TexFileOptions | Multipart 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> | None | Validates an exact primitive literal (string | number | boolean). |
tex.record(schema) | TexType<Record<string, V>> | None | Validates key-value dictionaries matching value schema V. |
tex.union(schemas) | TexType<T> | None | Validates against any one of the provided schemas. |
tex.any() | TexType<any> | None | Allows any arbitrary unconstrained value. |
Explicit Coercion (tex.coerce)
The tex.coerce namespace provides explicit coercion primitives for query parameters, path variables, and form inputs:
| Method | Return Type | Description |
|---|---|---|
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:
| Method | Signature | Description |
|---|---|---|
.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) => this | Sets documentation description metadata for OpenAPI 3.1 generation. |
.example(val) | (example: any) => this | Sets example value metadata for OpenAPI 3.1 generation. |
.sanitize(...fns) | (...fns: ((val: T) => T)[]) => this | Executes synchronous transformation functions before validation. |
.refine(fn, message) | (fn: (val: T) => boolean, msg: string) => this | Adds custom validation logic evaluated after native validation. |
.refineAsync(fn, message) | (fn: (val: T) => Promise<boolean>, msg: string) => this | Adds 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:
| Method | Signature | Description |
|---|---|---|
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) => T | Synchronously 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 = 3paginate(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)}