UNPKG

ascertain

Version:

0-Deps, simple, fast, for browser and node js object schema validator

375 lines (374 loc) 12.7 kB
/** * Abstract base class for schema operators. * * Provides a common constructor that enforces having at least one schema. * * @template T - The type of data the operator validates. * @abstract * @internal */ declare abstract class Operator<T> { readonly schemas: Schema<T>[]; constructor(schemas: Schema<T>[]); } /** * Symbol for validating object keys against a schema. */ export declare const $keys: unique symbol; /** * Symbol for validating object values against a schema. */ export declare const $values: unique symbol; /** * Symbol for enforcing strict object validation (no extra properties allowed). */ export declare const $strict: unique symbol; /** * Represents a schema for validating data. * * Schemas can be defined for various data types, including objects, arrays, and primitives. * * @template T - The type of data the schema validates. */ export type Schema<T> = T extends Record<string | number | symbol, unknown> ? { [K in keyof T]?: Schema<T[K]> | unknown; } & { [$keys]?: Schema<keyof T>; } & { [$values]?: Schema<T[keyof T]>; } & { [$strict]?: boolean; } : T extends Array<infer A> ? Schema<A>[] | unknown : unknown; declare class Or<T> extends Operator<T> { } /** * Operator for validating data against any of the provided schemas (logical OR). * * Creates a schema that accepts data matching any one of the provided schemas. * This is useful for creating union types or alternative validation paths. * * @template T - The type of data the operator validates. * @param schemas - Multiple schemas where at least one must match the data. * @returns A schema that validates data against any of the provided schemas. * * @example * ```typescript * import { or, ascertain } from 'ascertain'; * * // Create a schema that accepts either a string or number * const stringOrNumber = or(String, Number); * * ascertain(stringOrNumber, "hello", "value"); // ✓ Valid * ascertain(stringOrNumber, 42, "value"); // ✓ Valid * ascertain(stringOrNumber, true, "value"); // ✗ Throws error * * // Union of literal values * const statusSchema = or('pending', 'completed', 'failed'); * ascertain(statusSchema, 'pending', "status"); // ✓ Valid * * // Complex schema combinations * const userIdSchema = or(Number, { id: Number, temp: Boolean }); * ascertain(userIdSchema, 123, "userId"); // ✓ Valid * ascertain(userIdSchema, { id: 456, temp: true }, "userId"); // ✓ Valid * ``` */ export declare const or: <T>(...schemas: Schema<T>[]) => Or<T>; declare class And<T> extends Operator<T> { } /** * Operator for validating data against all provided schemas (logical AND). * * Creates a schema that requires data to match every one of the provided schemas. * This is useful for combining multiple validation requirements or adding constraints. * * @template T - The type of data the operator validates. * @param schemas - Multiple schemas that all must match the data. * @returns A schema that validates data against all of the provided schemas. * * @example * ```typescript * import { and, ascertain } from 'ascertain'; * * // Combine object schema with additional constraints * const userSchema = and( * { name: String, age: Number }, * { age: Number } // Additional constraint * ); * * ascertain(userSchema, { name: "John", age: 25 }, "user"); // ✓ Valid * * // Ensure an object is both a Date and has specific methods * const validDateSchema = and(Date, { toISOString: Function }); * ascertain(validDateSchema, new Date(), "date"); // ✓ Valid * * // Multiple validation layers * const positiveNumberSchema = and(Number, (n: number) => n > 0); * ascertain(positiveNumberSchema, 42, "count"); // ✓ Valid * ascertain(positiveNumberSchema, -5, "count"); // ✗ Throws error * ``` */ export declare const and: <T>(...schemas: Schema<T>[]) => And<T>; declare class Optional<T> extends Operator<T> { constructor(schema: Schema<T>); } /** * Operator for making a schema optional (nullable). * * Creates a schema that accepts the provided schema or null/undefined values. * This is useful for optional object properties or nullable fields. * * @template T - The type of data the operator validates. * @param schema - The schema to make optional. * @returns A schema that validates data against the provided schema or accepts null/undefined. * * @example * ```typescript * import { optional, ascertain } from 'ascertain'; * * // Optional string field * const userSchema = { * name: String, * nickname: optional(String), * age: Number * }; * * // All of these are valid * ascertain(userSchema, { * name: "John", * nickname: "Johnny", * age: 30 * }, "user"); // ✓ Valid * * ascertain(userSchema, { * name: "Jane", * nickname: null, * age: 25 * }, "user"); // ✓ Valid * * ascertain(userSchema, { * name: "Bob", * age: 35 * // nickname is undefined * }, "user"); // ✓ Valid * * // Optional complex objects * const profileSchema = { * id: Number, * settings: optional({ * theme: String, * notifications: Boolean * }) * }; * ``` */ export declare const optional: <T>(schema: Schema<T>) => Optional<T>; declare class Tuple<T> extends Operator<T> { } /** * Operator for validating data against a fixed-length tuple of schemas. * * Creates a schema that validates arrays with a specific length and type for each position. * This is useful for coordinate pairs, RGB values, or any fixed-structure data. * * @template T - The type of data the operator validates (a tuple of types). * @param schemas - Schemas for each position in the tuple, in order. * @returns A schema that validates data as a tuple with the specified structure. * * @example * ```typescript * import { tuple, ascertain } from 'ascertain'; * * // 2D coordinate tuple * const pointSchema = tuple(Number, Number); * ascertain(pointSchema, [10, 20], "point"); // ✓ Valid * ascertain(pointSchema, [1.5, 2.7], "point"); // ✓ Valid * ascertain(pointSchema, [10], "point"); // ✗ Throws error (too short) * ascertain(pointSchema, [10, 20, 30], "point"); // ✗ Throws error (too long) * * // RGB color tuple * const colorSchema = tuple(Number, Number, Number); * ascertain(colorSchema, [255, 128, 0], "color"); // ✓ Valid * * // Mixed type tuple * const userInfoSchema = tuple(String, Number, Boolean); * ascertain(userInfoSchema, ["Alice", 25, true], "userInfo"); // ✓ Valid * * // Nested tuple * const lineSchema = tuple( * tuple(Number, Number), // start point * tuple(Number, Number) // end point * ); * ascertain(lineSchema, [[0, 0], [10, 10]], "line"); // ✓ Valid * ``` */ export declare const tuple: <T>(...schemas: Schema<T>[]) => Tuple<T>; export declare const fromBase64: (value: string) => string; export declare const asError: <T>(message: string) => T; export declare const as: { /** * Attempts to convert a value to a string. * * @param value - The value to convert. * @returns The value as a string, or a TypeError if not a string. */ string: (value: string | undefined) => string; /** * Attempts to convert a value to a number. * * @param value - The value to convert (expected to be a string representation of a number). * @returns The value as a number, or a TypeError if not a valid number. */ number: (value: string | undefined) => number; /** * Attempts to convert a value to a Date object. * * @param value - The value to convert (expected to be a string representation of a date). * @returns The value as a Date object, or a TypeError if not a valid date. */ date: (value: string | undefined) => Date; /** * Attempts to convert a value to a time duration in milliseconds. * * @param value - The value to convert (e.g., "5s" for 5 seconds). * @param conversionFactor - Optional factor to divide the result by (default is 1). * @returns The time duration in milliseconds, or a TypeError if the format is invalid. */ time: (value: string | undefined, conversionFactor?: number) => number; /** * Attempts to convert a value to a boolean. * * @param value - The boolean like value to convert (e.g., "true", "1", "enabled"). * @returns The value as a boolean, or a TypeError if it could not be converted to a boolean. */ boolean: (value: string | undefined) => boolean; /** * Attempts to convert a string into an array of strings by splitting it using the given delimiter. * * @param value - The string value to attempt to split into an array. * @param delimiter - The character or string used to separate elements in the input string. * @returns An array of strings if the conversion is successful, or a TypeError if the value is not a string. */ array: (value: string | undefined, delimiter: string) => string[]; /** * Attempts to parse a JSON string into a JavaScript object. * * @template T - The expected type of the parsed JSON object. * @param value - The JSON string to attempt to parse. * @returns The parsed JSON object if successful, or a TypeError if the value is not valid JSON. */ json: <T = object>(value: string | undefined) => T; /** * Attempts to decode a base64-encoded string. * * @param value - The base64-encoded string to attempt to decode. * @returns The decoded string if successful, or a TypeError if the value is not valid base64. */ base64: (value: string | undefined) => string; }; /** * Compiles a schema into a validation function. * * This function takes a schema definition and generates a JavaScript function * that can be used to validate data against the schema. * * @template T - The type of data the schema validates. * @param schema - The schema to compile. * @param rootName - A name for the root of the data structure (used in error messages). * @returns A validation function that takes data as input and throws a TypeError if the data does not conform to the schema. * * @example * ```typescript * import { compile, optional, and, or } from 'ascertain'; * * const userSchema = { * name: String, * age: Number, * email: optional(String), * role: or('admin', 'user', 'guest') * }; * * const validateUser = compile(userSchema, 'User'); * * // Valid data - no error thrown * validateUser({ * name: 'John Doe', * age: 30, * email: 'john@example.com', * role: 'user' * }); * * // Invalid data - throws TypeError * try { * validateUser({ * name: 123, // Invalid: should be string * age: 'thirty' // Invalid: should be number * }); * } catch (error) { * console.error(error.message); // Detailed validation errors * } * ``` */ export declare const compile: <T>(schema: Schema<T>, rootName: string) => (data: T) => any; /** * Asserts that data conforms to a given schema. * * This function is a convenient wrapper around `compile`. It compiles the schema * and immediately validates the provided data against it. * * @template T - The type of data the schema validates. * @param schema - The schema to validate against. * @param data - The data to validate. * @param rootName - A name for the root of the data structure (used in error messages, defaults to '[root]'). * @throws `{TypeError}` If the data does not conform to the schema. * * @example * ```typescript * import { ascertain, optional, and, or } from 'ascertain'; * * const userSchema = { * name: String, * age: Number, * email: optional(String), * active: Boolean * }; * * const userData = { * name: 'Alice', * age: 25, * email: 'alice@example.com', * active: true * }; * * // Validate data - throws if invalid, otherwise continues silently * ascertain(userSchema, userData, 'UserData'); * console.log('User data is valid!'); * * // Example with invalid data * try { * ascertain(userSchema, { * name: 'Bob', * age: 'twenty-five', // Invalid: should be number * active: true * }, 'UserData'); * } catch (error) { * console.error('Validation failed:', error.message); * } * ``` * * @example * ```typescript * // Array validation * const numbersSchema = [Number]; * const numbers = [1, 2, 3, 4, 5]; * * ascertain(numbersSchema, numbers, 'Numbers'); * * // Tuple validation * const coordinateSchema = tuple(Number, Number); * const point = [10, 20]; * * ascertain(coordinateSchema, point, 'Point'); * ``` */ export declare const ascertain: <T>(schema: Schema<T>, data: T, rootName?: string) => void; export {};