ciscoheat / sveltekit-superforms

Making SvelteKit forms a pleasure to use!
https://superforms.rocks
MIT License
2.26k stars 67 forks source link

Unsupported valibot schema: file #452

Closed jdgamble555 closed 3 months ago

jdgamble555 commented 4 months ago

Description When creating a schema with file or blob, I get:

Error: Unsupported valibot schema: file

using something like:

export const photoSchema = v.object({
    photo: v.pipe(
        v.file('Please select an image file.'),
        v.mimeType(
            ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
            'Please select a JPEG, PNG, WEBP, or GIF file.'
        ),
        v.maxSize(1024 * 1024 * 10, 'Please select a file smaller than 10MB.')
    )
});

J

gcornut commented 4 months ago

file & blob must be added in the customSchemaConversion in https://github.com/ciscoheat/sveltekit-superforms/blob/1dcb881a61d8bf8074c639834a733652d3878506/src/lib/adapters/valibot.ts#L32

jdgamble555 commented 4 months ago

I don't understand. Does it not support all Valibot functions out of the box? What about mimeType, maxSize, etc?

ciscoheat commented 4 months ago

Files aren't supported by JSON Schema natively, so it needs to have an explicit custom schema generator. I'm quite sure this has worked before, but Valibot is a 0.x library, so not much to do. It'll be added in the next version.

ciscoheat commented 4 months ago

Until then, you can configure the adapter with that option:


valibot(schema, { customSchemaConversion: {
    custom: () => ({}),
    instance: () => ({}),
    file: () => ({}),
    blob: () => ({})
}});
jdgamble555 commented 4 months ago

What do you guys mean "not supported?" It is literally in the example on Valibot:

https://valibot.dev/api/file/#examples

I don't know what customSchemaConversion does. There are no examples in the docs. You guys show me the code, but I don't understand how to use it?

valibot(schema, { customSchemaConversion: {
    custom: () => ({}),
    instance: () => ({}),
    file: () => ({}),
    blob: () => ({})
}});

How would I use this?

Thanks,

J

ciscoheat commented 4 months ago

It's not as simple as using your favorite library and validate some data with it. Have you ever wondered how Superforms can conjure default values for schemas, create browser constraints, map errors to correct paths and transform posted form data into the correct types, for nine different validation libraries?

It can only be done consistently if there is a underlying unified data format for all of them.

That format is called JSON Schema. Using a validation schema to JSON Schema library, like the very well written one for Valibot, it's possible to handle the primitive types quite easily, those that can be directly serialized to JSON, but there are others like Date and, File of course, that cannot be directly converted to JSON Schema, because there is no standard for them.

So when we say "not supported", even though Valibot and many others validate files, you cannot directly convert the schema for file validation to JSON Schema. A custom schema conversion is required, that will say "here's how I want files to be handled".

Now, to solve your problem. You have used superValidate before, right?

const form = await superValidate(valibot(schema));

Solution: Add the option object you're wondering about as a second parameter to the valibot function. It will make files and blobs pass through the converter (but not pass through the validator, of course. I hope you know the difference by now.)

I don't know what customSchemaConversion does. There are no examples in the docs. You guys show me the code, but I don't understand how to use it?

That's how it is with open source software, everyone's time is limited, so when you don't pay with money, you pay with your time. Now I've spent plenty of time explaining this to you, so I hope you can pay it forward somehow.

jdgamble555 commented 4 months ago

Hi @ciscoheat,

I appreciate the basic explanation, but that is not my issue.

What I'm not understanding are two things.

1.) Why is this possible with zod but not valibot:

https://superforms.rocks/concepts/files

Should I use the image: optional(instance(File)) instead? This seems like a work-around. I'm assuming this means the core converter has not been added for valibot... yet?

2.) This still doesn't explain how to use this:

valibot(schema, { customSchemaConversion: {
    custom: () => ({}),
    instance: () => ({}),
    file: () => ({
        // what goes here?       
        }),
    blob: () => ({})
}});

This seems like something that should be in the docs. Where can I find how to use customSchemaConversion (a link, not asking anyone to write anything for me).

Thanks,

J

ciscoheat commented 4 months ago

1.) Why is this possible with zod but not valibot:

https://superforms.rocks/concepts/files

The Zod to JSON Schema library isn't as strict as the Valibot one. It converts File (or rather, instanceof) validators to an any type, which again isn't used in validation, but in the data conversion, when providing default values, etc.

The Valibot converter requires you to be more explicit, which in combination with 0.x versions can lead to these kind of issues.

Should I use the image: optional(instance(File)) instead? This seems like a work-around. I'm assuming this means the core converter has not been added for valibot... yet?

No, just add the option object as-is. It's just a matter of being explicit.

2.) This still doesn't explain how to use this:

valibot(schema, { customSchemaConversion: {
  custom: () => ({}),
  instance: () => ({}),
  file: () => ({
        // what goes here?       
        }),
  blob: () => ({})
}});

Nothing goes there, {} is the same as any in the JSON Schema definition, which is a bit confusing, but it represents a validator object without any validation. Read more about it here: https://json-schema.org/understanding-json-schema/basics

Again, the any type is not used in validation, but as metadata for the default values, constraints, etc, so it's safe to use. Validation itself is handled by Valibot, not by the JSON Schema.

This seems like something that should be in the docs. Where can I find how to use customSchemaConversion (a link, not asking anyone to write anything for me).

It's tied to the Valibot JSON Schema library, so: https://github.com/gcornut/valibot-json-schema?tab=readme-ov-file#custom-schema-conversion

In the next version, it won't be a problem anymore since I will add the conversions to the default options for the adapter. Until the next missing schema type comes up, of course... ;)

I could link to the respective JSON Schema converter for each adapter, but it's rare to configure them, and some are directly in the library code due to ESM/bundle issues, others only have a simple representation due to not being convertible to JSON Schema (see my blog post about this), so I'm not sure how to make the docs for that succinct and useful.

Thanks,

Thank you for sponsoring, it's very much appreciated!

ciscoheat commented 3 months ago

Should be working without any customSchemaConversion now in 2.17.0