@samatawy/checks
    Preparing search index...

    Checks API

    This package provides a fluent validation API built around a small set of composable check classes.

    Many flows are asynchronous. Any API that evaluates promised checks or loads binary data should be awaited before you build the final result.

    If you need stable codes or translated result text, see coded-results.md. The main API works fine without that layer.

    Use validateClass(input, ClassType, options?) when a class definition should drive validation directly.

    By default, it uses hybrid behavior:

    • explicit decorators are applied first
    • initialized class fields can still contribute inferred checks when a property has no explicit decorator shape yet

    Type-only property declarations such as name!: string or age?: number do not create runtime instance properties in JavaScript, so inference cannot see them unless decorators registered metadata for them. A default value such as name = '' or active = false does create a runtime property, which is why inference currently follows initialized fields.

    Example:

    import { required, string, type, validateClass } from '@samatawy/checks';

    class PersonDto {
    @required()
    @type.string()
    @string.minLength(2)
    name!: string;

    active = false;
    }

    const check = await validateClass({
    name: 'Ada',
    active: true,
    }, PersonDto, {
    noExtraFields: true,
    });

    const result = check.result({ language: 'en' });

    Notes:

    • pass { skip: 'inference' } for decorator-only behavior
    • pass { skip: 'decorators' } for inference-only behavior
    • inference only sees properties that exist on a constructed instance at runtime, which in practice means initialized class fields or nested object instances created by those initializers
    • nested matchesType(...) calls also accept the same options shape

    Use ObjectCheck when the input itself is an object or when a field should contain an object.

    Common methods:

    • ObjectCheck.for(data) starts a root object validation
    • notEmpty(options?) rejects empty objects
    • object(options?) asserts the value is an object and not an array
    • required(name, options?) starts a required field validation
    • optional(name) starts an optional field validation
    • conditional(name, condition, options?) requires a field only when the predicate returns true
    • noExtraFields(options?) flags undeclared keys in the final object result
    • check(fn) applies nested rules and aggregates results asynchronously
    • allOf(fn) applies a nested group of object rules where all returned checks must pass
    • anyOf(branches) applies alternative object branches where at least one branch must pass
    • oneOf(branches) applies alternative object branches where exactly one branch must pass
    • not(fn, options?) applies a negated object branch that must fail
    • isTrue(fn, options?) applies a custom object-level predicate and supports async predicates
    • result(options?) returns the current result or a formatted final result depending on the options you pass

    Example:

    import { ObjectCheck } from '@samatawy/checks';

    const check = await ObjectCheck.for(input)
    .notEmpty()
    .check(person => [
    person.required('name').string().minLength(2).maxLength(100),
    person.optional('age').number().atLeast(0).atMost(150),
    person.required('address').object().check(address => [
    address.required('street').string(),
    address.required('city').string()
    ])
    ]);

    const result = check.result({ language: 'en' });

    Composition example:

    import { ObjectCheck } from '@samatawy/checks';

    const input = {
    profile: {
    name: ' Ada ',
    age: '37'
    }
    };

    const check = await ObjectCheck.for(input)
    .check(root => [
    root.required('profile').object().anyOf([
    profile => [
    profile.required('name').string().trim().minLength(3)
    ],
    profile => [
    profile.required('age').number({ tolerant: true }).greaterThan(17)
    ]
    ])
    ]);

    const result = check.result({ language: 'en' });

    Notes:

    • allOf(fn) keeps the same callback shape as check(fn)
    • anyOf(...) and oneOf(...) accept an array of branch functions, not a single callback
    • valid anyOf(...) and oneOf(...) branches are replayed on the real object, so normal mutations such as trim() or tolerant parsing affect the original input consistently
    • not(...) evaluates its branch on isolated data and never replays mutations onto the original input

    FieldCheck bridges from an object field to a specific value type.

    Common methods:

    • required(options?)
    • allOf(fn) applies a group of field rules where all returned checks must pass
    • anyOf(branches) applies alternative field branches where at least one branch must pass
    • oneOf(branches) applies alternative field branches where exactly one branch must pass
    • not(fn, options?) applies a negated field branch that must fail
    • equals(value, options?) compares the field value using strict equality by default and supports lax matching with tolerant: true
    • object()
    • array()
    • file()
    • image()
    • string()
    • email()
    • url()
    • number()
    • date()
    • uuid()
    • ulid()
    • boolean()

    Notes:

    • file() returns Promise<FileCheck>
    • image() returns Promise<ImageCheck>
    • email() returns EmailCheck
    • url() returns UrlCheck
    • uuid() returns UUIDCheck
    • uuid(options?) accepts the same UUID version filter supported by UUIDCheck.version(...)
    • ulid() returns UUIDCheck in ULID mode
    • the other branching methods return synchronous check instances

    Field composition example:

    import { ObjectCheck } from '@samatawy/checks';

    const input = {
    value: '37'
    };

    const check = await ObjectCheck.for(input)
    .check(root => [
    root.required('value').anyOf([
    field => [field.number({ tolerant: true }).greaterThan(10)],
    field => [field.string().minLength(5)]
    ])
    ]);

    const result = check.result({ language: 'en' });

    Use ArrayCheck for array-specific validation.

    Common methods:

    • ArrayCheck.for(data) starts a root array validation
    • array(options?) ensures the value is an array
    • notEmpty(options?) rejects empty arrays
    • minLength(length, options?)
    • maxLength(length, options?)
    • noDuplicates(key?, options?) rejects duplicate primitive values, duplicate repeated object references, or duplicate object values by a selected object key
    • matchesType(ClassType, options?) validates each array element against one decorated class definition
    • check(fn) applies array-level checks and synthetic child results asynchronously
    • allOf(fn) applies a group of array rules where all returned checks must pass
    • anyOf(branches) applies alternative array branches where at least one branch must pass
    • oneOf(branches) applies alternative array branches where exactly one branch must pass
    • not(fn, options?) applies a negated array branch that must fail
    • checkEach(fn) validates each item using ArrayItemCheck and supports promised checks
    • contains(fn, options?) succeeds when a bounded number of items match one nested item rule without reporting non-matching item errors individually
    • isTrue(fn, options?) applies a custom predicate to the array value and supports async predicates
    • isTrueEach(fn, options?) runs a custom predicate on every item and supports async predicates
    • result(options?) returns the current result or a formatted final result depending on the options you pass

    Example:

    import { ObjectCheck } from '@samatawy/checks';

    const check = await ObjectCheck.for(input)
    .check(person => [
    person.optional('children').array().maxLength(10)
    .checkEach(child => [
    child.object(),
    child.required('name').string(),
    child.optional('age').number().atLeast(0).atMost(17)
    ])
    ]);

    const result = check.result({ flattened: true, language: 'en' });

    Composition example:

    import { ObjectCheck } from '@samatawy/checks';

    const input = {
    tags: [' Ada ', ' Bob ']
    };

    const check = await ObjectCheck.for(input)
    .check(root => [
    root.required('tags').array().anyOf([
    tags => [
    tags.checkEach(item => [item.string().trim().minLength(2)])
    ],
    tags => [tags.maxLength(1)]
    ])
    ]);

    const result = check.result({ language: 'en' });

    Notes:

    • contains(...) is the array-level “some items must match” helper; use it instead of checkEach(...) when non-matching items are expected and should not each produce their own error
    • matchesType(...) on ArrayCheck is shorthand for checkEach(item => [item.matchesType(...)])
    • item.array().matchesType(...) refers to a nested-array case where the current item value is itself an array and each nested element must match the class definition

    Represents one element in an array.

    Common methods:

    • object()
    • required(name, options?)
    • optional(name)
    • conditional(name, condition, options?)
    • array()
    • string()
    • number()
    • date()
    • boolean()
    • allOf(fn) applies a group of item rules where all returned checks must pass
    • anyOf(branches) applies alternative item branches where at least one branch must pass
    • oneOf(branches) applies alternative item branches where exactly one branch must pass
    • not(fn, options?) applies a negated item branch that must fail
    • equals(value, options?) compares the current item value using strict equality by default and supports lax matching with tolerant: true

    Item composition example:

    import { ObjectCheck } from '@samatawy/checks';

    const input = {
    values: [' Ada ']
    };

    const check = await ObjectCheck.for(input)
    .check(root => [
    root.required('values').array().checkEach(item => [
    item.anyOf([
    entry => [entry.string().trim().minLength(2)],
    entry => [entry.number().greaterThan(10)]
    ])
    ])
    ]);

    const result = check.result({ language: 'en' });

    Use FileCheck for binary or data-URL style inputs.

    Creation:

    • await FileCheck.for(key, data)
    • await field.file()

    Methods:

    • mimeType(expectedMime, options?)
    • notEmpty(options?)
    • minSize(minBytes, options?)
    • maxSize(maxBytes, options?)

    Supported inputs include Blob, File, Uint8Array, ArrayBuffer, Node Buffer, and data: URLs.

    ImageCheck extends FileCheck with image-specific validation.

    Creation:

    • await ImageCheck.for(key, data)
    • await field.image()

    Methods:

    • isImage(options?)
    • minWidth(minWidth, options?)
    • minHeight(minHeight, options?)
    • maxWidth(maxWidth, options?)
    • maxHeight(maxHeight, options?)

    StringCheck, EmailCheck, and UrlCheck share common string comparison methods through the internal StringBaseCheck.

    NumberCheck and DateCheck expose numeric and date comparison helpers. ValueCheck is the shared base for value-level fluent behavior and is usually not needed directly in application code.

    Composition helpers such as allOf(...), anyOf(...), oneOf(...), and not(...) are intentionally documented on ObjectCheck, FieldCheck, ArrayCheck, and ArrayItemCheck, where branching still happens before a final fixed type is chosen.

    Selected methods:

    • StringCheck.trim()
    • StringCheck.minLength(length, options?)
    • StringCheck.maxLength(length, options?)
    • StringCheck.equals(value, options?)
    • StringCheck.equalsOneOf(values, options?)
    • StringCheck.startsWith(prefix, options?)
    • StringCheck.endsWith(suffix, options?)
    • StringCheck.contains(substring, options?)
    • StringCheck.pattern(regex, options?)
    • StringCheck.uuid(options?)
    • StringCheck.ulid(options?)
    • StringCheck.email(options?)
    • StringCheck.url(options?)
    • StringCheck.isBase64(options?)
    • StringCheck.isSHA256(options?)
    • StringCheck.isMD5(options?)
    • StringCheck.isHexadecimal(options?)
    • StringCheck.isAlphanumeric(options?)
    • StringCheck.isAscii(options?)
    • StringCheck.hasMultibyte(options?)
    • StringCheck.hasUpperCase(minCount?, options?)
    • StringCheck.hasLowerCase(minCount?, options?)
    • StringCheck.hasDigit(minCount?, options?)
    • StringCheck.hasSpecialCharacter(minCount?, options?)
    • StringCheck.noSpecialCharacters(chars?, options?)
    • StringCheck.noSpaces(options?)
    • StringCheck.maxWords(count, options?)
    • UUIDCheck.version(version, options?)
    • UUIDCheck.isULID(options?)
    • NumberCheck.integer(options?)
    • NumberCheck.greaterThan(value, options?)
    • NumberCheck.atLeast(value, options?)
    • NumberCheck.atMost(value, options?)
    • DateCheck.after(value, options?)
    • DateCheck.before(value, options?)
    • DateCheck.sameDay(value, options?)
    • ValueCheck.isTrue(fn, options?)
    • ValueCheck.result(options?)

    uuid() validates immediately as soon as you enter the UUID checker, like email() and url(). Use field.uuid({ version }) or string().uuid({ version }) when you want the shortcut form, or uuid().version(4) / uuid().version([4, 7]) when you want to narrow an already-valid UUID to one version or a small allowed set.

    Use ulid() when the value must be a ULID. Like uuid(), it validates immediately when you enter the specialized checker, so field.ulid() and string().ulid() already perform the base ULID validity check.

    result(options?) is the main output API.

    For object and array checks:

    • result({ language }) returns the merged nested result tree
    • result({ language, catalog }) resolves coded messages with an explicit result catalog instead of ResultCatalog.global
    • result({ flattened: true, language }) returns flattened hints, warnings, and errors
    • result({ nested: true, language }) returns an input-shaped projection under input
    • result({ validated: 'partial', language }) returns a cloned validated value with invalid descendants removed and valid siblings preserved
    • result({ validated: 'strict', language }) returns a cloned validated value where any invalid descendant removes the whole parent branch
    • result({ raw: true, nested: true, flattened: true, language }) returns all projections at once

    For value-level checks:

    • result({ language }) returns the finalized single result
    • result() returns the current single result state without extra projections

    Example:

    const output = check.result({
    raw: true,
    nested: true,
    validated: 'partial',
    flattened: true,
    language: 'en'
    }) as any;

    console.log(output.raw);
    console.log(output.input);
    console.log(output.validated);
    console.log(output.errors);

    Notes:

    • catalog lets final result formatting resolve coded messages from a specific ResultCatalog; without it, the package uses ResultCatalog.global
    • validated uses the current normalized input value, so coercions or mutations performed by checks are reflected in the output
    • 'partial' is the default mode to prefer when you want to keep valid object fields or array items even if siblings fail
    • 'strict' is useful when a parent object or array should be considered unusable as soon as one descendant is invalid

    Represents one validation outcome.

    interface SingleResult {
    valid: boolean;
    field?: string | number | null | undefined;
    hint?: string | string[];
    warn?: string | string[];
    err?: string | string[];
    code?: string | number;
    }

    Adds nested and flattened result collections.

    interface ResultSet extends SingleResult {
    input?: any;
    results?: IResult[];
    hints?: string[];
    warnings?: string[];
    errors?: string[];
    }

    Use these to customize output when a check fails.

    interface CheckOptions {
    hint?: string | string[];
    warn?: string | string[];
    err?: string | string[];
    code?: string | number;
    catalog?: IResultCatalog;
    }

    Use inline hint, warn, or err for direct messages.

    Use code when you want the message level and translations to come from a ResultCatalog. If you do not pass catalog, the package uses ResultCatalog.global.

    Adds case sensitivity control.

    interface StringCheckOptions extends CheckOptions {
    case?: 'sensitive' | 'insensitive';
    }

    Controls how final output is shaped.

    interface ResultOptions {
    language?: string;
    catalog?: IResultCatalog;
    raw?: boolean;
    nested?: boolean;
    validated?: 'partial' | 'strict';
    flattened?: boolean;
    }

    Controls how validateClass(...) and matchesType(...) source their rules.

    interface ClassValidationOptions {
    noExtraFields?: boolean;
    noExtraFieldsOptions?: CheckOptions;
    result?: ResultOptions;
    skip?: 'decorators' | 'inference';
    }

    Notes:

    • omit skip to use hybrid behavior
    • use skip: 'inference' to require explicit decorators only
    • use skip: 'decorators' to ignore decorator metadata and rely on inferred checks from runtime instance properties, which usually means initialized class fields

    Install just the core package if you only need object, array, string, number, or date validation:

    npm install @samatawy/checks
    

    Install the optional peer dependencies when using file or image validation:

    npm install @samatawy/checks file-type probe-image-size