import type { Schema } from '../base.ts'; import { createIssue, failure, success, createParseMethods, SCHEMA_KIND } from '../base.ts'; import { optional } from '../utils/optional.ts'; import { nullable } from '../utils/nullable.ts'; const parseMethods = createParseMethods(); /** * Schema for validating number values. * Rejects NaN values. * * @example * ```typescript * const schema = s.number(); * const age = schema.parse(30); // 30 * schema.parse('30'); // throws ValidationError * schema.parse(NaN); // throws ValidationError * ``` */ export class NumberSchema implements Schema { readonly [SCHEMA_KIND] = 'NumberSchema'; description?: string; private _finite = false; private _min?: number; private _max?: number; readonly '~standard' = { version: 1 as const, vendor: 'agentuity', validate: (value: unknown) => { if (typeof value !== 'number' || Number.isNaN(value)) { return failure([createIssue(`Expected number, got ${typeof value}`)]); } if (this._finite && !Number.isFinite(value)) { return failure([createIssue('Expected finite number (not Infinity or -Infinity)')]); } if (this._min !== undefined && value < this._min) { return failure([createIssue(`Expected number >= ${this._min}, got ${value}`)]); } if (this._max !== undefined && value > this._max) { return failure([createIssue(`Expected number <= ${this._max}, got ${value}`)]); } return success(value); }, types: undefined as unknown as { input: number; output: number }, }; describe(description: string): this { this.description = description; return this; } /** * Require the number to be finite (not Infinity, -Infinity, or NaN). * * @example * ```typescript * const schema = s.number().finite(); * schema.parse(123); // 123 * schema.parse(Infinity); // throws ValidationError * schema.parse(-Infinity); // throws ValidationError * ``` */ finite(): NumberSchema { const clone = this._clone(); clone._finite = true; return clone; } /** * Set minimum value (inclusive). * * @example * ```typescript * const schema = s.number().min(0); * schema.parse(5); // 5 * schema.parse(-1); // throws ValidationError * ``` */ min(value: number): NumberSchema { const clone = this._clone(); clone._min = value; return clone; } /** * Set maximum value (inclusive). * * @example * ```typescript * const schema = s.number().max(100); * schema.parse(50); // 50 * schema.parse(101); // throws ValidationError * ``` */ max(value: number): NumberSchema { const clone = this._clone(); clone._max = value; return clone; } optional() { return optional(this); } nullable() { return nullable(this); } private _clone(): NumberSchema { const clone = new NumberSchema(); clone.description = this.description; clone._finite = this._finite; clone._min = this._min; clone._max = this._max; return clone; } parse = parseMethods.parse; safeParse = parseMethods.safeParse; } /** * Create a number schema. * * @example * ```typescript * const ageSchema = s.number().describe('User age'); * const age = ageSchema.parse(30); * * const finiteSchema = s.number().finite(); * finiteSchema.parse(123); // OK * finiteSchema.parse(Infinity); // throws * * const rangeSchema = s.number().min(0).max(100); * rangeSchema.parse(50); // OK * rangeSchema.parse(101); // throws * ``` */ export function number(): NumberSchema { return new NumberSchema(); }