fabian-hiller / valibot

The modular and type safe schema library for validating structural data 🤖
https://valibot.dev
MIT License
5.88k stars 181 forks source link

`forward` not working when `transform` field throwing error #582

Closed amiftachulh closed 3 months ago

amiftachulh commented 3 months ago

Hi, I have this schema.

import * as v from "valibot";

export const RegisterSchema = v.object(
  {
    name: v.string([
      v.minLength(3, "Username must be at least 3 characters."),
      v.maxLength(32, "Username must be at most 32 characters."),
      v.regex(
        /^[a-zA-Z0-9_.]+$/,
        "Username must contain only letters, numbers, underscores, and dot."
      )
    ]),
    displayName: v.transform(
      v.string([
        v.toTrimmed(),
        v.maxLength(50, "Display name must be at most 50 characters.")
      ]),
      (input) => input || null
    ),
    email: v.string([
      v.email("Invalid email address.")
    ]),
    password: v.string([
      v.minLength(8, "Password must be at least 8 characters."),
      v.maxLength(32, "Password must be at most 32 characters."),
      v.regex(
        /^[a-zA-Z0-9!@#$%^&*()\-=_+[\]{}\\|;:'",./<>? ]+$/,
        "Password must contain only letters, numbers, and special characters."
      )
    ]),
    confirmPassword: v.string()
  },
  [
    v.forward(
      v.custom(input => input.password === input.confirmPassword, "Passwords do not match."),
      ["confirmPassword"]
    )
  ]
);

const input = {
  name: "john_doe",
  displayName: "This Long Name Will Throw Error Because It's Very Long",
  email: "john_doe@mail",
  password: "password123!@#$%",
  confirmPassword: "asd"
};

try {
  const result = v.parse(RegisterSchema, input);
  console.log("Validation passed:", result);
} catch (error) {
  console.error(v.flatten(error.issues).nested);
}

I write displayName like that to convert empty string to null. But it will produce this error.

{
  displayName: [ 'Display name must be at most 50 characters.' ],
  email: [ 'Invalid email address.' ]
}

If I remove the v.transform like this.

displayName: v.string([
  v.toTrimmed(),
  v.maxLength(50, "Display name must be at most 50 characters.")
]),

It will show the v.forward error. But I can't convert the empty string to null.

{
  displayName: [ 'Display name must be at most 50 characters.' ],
  email: [ 'Invalid email address.' ],
  confirmPassword: [ 'Passwords do not match.' ]
}

Is this the intended behavior? If yes, what is the workaround?

fabian-hiller commented 3 months ago

Yes, this is the intended behavior at the moment. The reason for this is that transform will mark your data as untyped if there are any issues. If we can't be sure that your data is typed, we can't run custom because we guarantee type safety for the first argument of the passed function. With the rewrite in #502, I will provide a fix for this problem soon.

amiftachulh commented 3 months ago

Cool, I'll wait for the fix

fabian-hiller commented 3 months ago

The fix will be a partialCheck function (not implemented yet) that allows you to select the fields you want to validate. As long as these fields are typed, Valibot will perform the validation.