@samatawy/checks
    Preparing search index...

    Data Updates

    Use these patterns when validation depends on both incoming update data and an existing value.

    All update-aware checks start by supplying the previous value with updating(...).

    Update validation is patch-based. Validate the incoming input only, then let update-aware methods compare that input against the previous value from updating(...).

    Use updating(previous) at the root of the fluent chain when later checks need access to old values.

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

    const previous = {
    name: 'Ada',
    role: 'admin'
    };

    const input = {
    role: 'editor'
    };

    const check = await ObjectCheck.for(input)
    .updating(previous)
    .check(root => [
    root.optional('role').string()
    ]);

    const result = check.result({ flattened: true });

    If a field is omitted from the incoming patch, update-specific rules for that field do not run.

    Use canUpdate(...) when the allowed change depends on both the previous and new value.

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

    const check = await ObjectCheck.for({ role: 'editor' })
    .updating({ role: 'admin' })
    .check(root => [
    root.optional('role').string().canUpdate((oldValue, newValue) => {
    return !(oldValue === 'admin' && newValue !== 'admin');
    }, {
    err: 'Role cannot be downgraded from admin'
    })
    ]);

    const result = check.result({ flattened: true });

    That same pattern works for nested fields and array items because updating(...) flows down through child checkers.

    Use immutable() when a field or value may already exist but must not be changed by the current patch.

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

    const check = await ObjectCheck.for({ name: 'Grace' })
    .updating({ name: 'Ada' })
    .check(root => [
    root.optional('name').string().immutable()
    ]);

    const result = check.result({ flattened: true });

    Because update validation is patch-based, immutable() only runs when the current input supplies that field.

    Use canAdd(...) when each newly added item must satisfy a transition rule.

    Use canDelete(...) when each removed item must satisfy a removal rule.

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

    const check = await ObjectCheck.for({ tags: ['admin', 'editor'] })
    .updating({ tags: ['editor', 'viewer'] })
    .check(root => [
    root.required('tags').array()
    .canAdd((array, item) => item !== 'admin', {
    err: 'Admin tag cannot be added'
    })
    .then(tags => tags.canDelete((array, item) => item !== 'viewer', {
    err: 'Viewer tag cannot be removed'
    }))
    ]);

    const result = check.result({ flattened: true });

    canAdd(...) receives the current array and each added item.

    canDelete(...) receives the previous array and each deleted item.

    Both methods compare current and previous arrays with duplicate-aware matching, so repeated values are handled individually.

    Use ObjectFactory.update(...) when a class owns both validation and hydration for update workflows.

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

    class PersonDto {
    constructor(
    public readonly name: string,
    public readonly title?: string,
    ) {}

    static async validateUpdate(existing: PersonDto, input: unknown): Promise<ObjectCheck> {
    return ObjectCheck.for(input)
    .updating(existing)
    .check(root => [
    root.optional('name').string().immutable(),
    root.optional('title').string().canUpdate((oldValue, newValue) => {
    return !(oldValue === 'Lead' && newValue === 'Intern');
    }, {
    err: 'Lead title cannot be downgraded directly to Intern'
    })
    ]);
    }

    static updateFrom(existing: PersonDto, input: any): PersonDto {
    return new PersonDto(
    input.name ?? existing.name,
    input.title ?? existing.title,
    );
    }
    }

    const existing = new PersonDto('Ada', 'Lead');

    const updated = await ObjectFactory.update(
    existing,
    { title: 'Architect' },
    PersonDto,
    );

    if (!updated.valid) {
    console.log(updated.result({ flattened: true }));
    } else {
    console.log(updated.instance);
    }

    Use ObjectFactory.update(...) when you want the full factory result object.

    Use ObjectFactory.updateOrThrow(...) when invalid update input should raise an exception.

    const existing = new PersonDto('Ada', 'Lead');

    const updated = await ObjectFactory.updateOrThrow(existing, { title: 'Architect' }, PersonDto);

    Use ObjectFactory.updateOrErrors(...) when you want either { instance } or { errors } without throwing.

    const existing = new PersonDto('Ada', 'Lead');

    const updated = await ObjectFactory.updateOrErrors(existing, { name: 'Grace' }, PersonDto);

    if (updated.errors) {
    console.log(updated.errors);
    } else {
    console.log(updated.instance);
    }