sveltekit-superforms icon indicating copy to clipboard operation
sveltekit-superforms copied to clipboard

Inconsistent `length` in textarea prevents superforms constraints as source of truth

Open fcrozatier opened this issue 2 years ago • 6 comments

  • [x ] Before posting an issue, read the FAQ at https://superforms.rocks/faq and search the previous issues.

Description As per HTML spec a textarea normalizes new lines to \n in the browser but to \r\n when the form is submitted. So if you insert new lines and use a naive max in your schema you can pass browser validation but fail server validation.

This is unwanted and can be confusing for users if you have a character counter.

As an alternative one can refine the string schema, but then the constraint doesn't know about the maxlength which has to be added manually?

Refs:

For historical reasons, the element's value is normalized in three different ways for three different purposes. The raw value is the value as it was originally set. It is not normalized. The API value is the value used in the value IDL attribute, textLength IDL attribute, and by the maxlength and minlength content attributes. It is normalized so that line breaks use U+000A LINE FEED (LF) characters. Finally, there is the value, as used in form submission and other processing models in this specification. It is normalized as for the API value, and in addition, if necessary given the element's wrap attribute, additional line breaks are inserted to wrap the text at the given width. https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element

As with all MIME transmissions, "CR LF" (i.e., `%0D%0A') is used to separate lines of data. https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4

If applicable, a MRE

repro repo

stackblitz https://stackblitz.com/edit/sveltekit-superforms-1-testing-xxfpiy?file=src%2Froutes%2F%2Bpage.server.ts,src%2Froutes%2F%2Bpage.svelte

fcrozatier avatar Aug 22 '23 10:08 fcrozatier

What do you suggest as a fix or workaround?

ciscoheat avatar Aug 22 '23 14:08 ciscoheat

Well one workaround is to not use max for textarea and use a refinement as in the repro here https://github.com/fcrozatier/superform-textarea/blob/main/src/routes/%2Bpage.server.ts#L9

But then you also have to add a maxlength manually.

So it's not really satisfying but at least there no browser/server validation mismatch that could confuse the end user.

Maybe there's a better approach?

fcrozatier avatar Aug 22 '23 15:08 fcrozatier

I don't know, since it works in general for input fields, which are the most common case, it's probably enough to make a note in the documentation about this.

ciscoheat avatar Aug 23 '23 15:08 ciscoheat

I don't know, since it works in general for input fields

Because we can't have new lines in an input field

Since this inconsistency is inevitable (html spec) and only concerns texteara because of new lines maybe it would make sense to have a .text validation in zod? I'll ask them

But for now yeah maybe a doc note is all we can do.

fcrozatier avatar Aug 24 '23 06:08 fcrozatier

Good idea, Zod is a better place for this. Thank you for making the effort! :)

ciscoheat avatar Aug 24 '23 20:08 ciscoheat

I was able to get a consistent behaviour by adding a text normalisation function, like so:

/** One has to take into account the \r characters added by the new line normalization, as per html specs */
const normalise = (text: string) => text.replaceAll('\r\n', '\n');

const schema = z.object({
  textarea: z.string().transform(normalise).pipe(z.string().max(5))
});

Except for Safari which correctly reports the textarea's length with LF for new lines, but incorrectly checks against maxLength using CRLF. Fixed by https://github.com/WebKit/WebKit/commit/2252898e2468994720be25176170e00bc7b26ca3 in Safari Technology Preview

mxdvl avatar Aug 26 '23 13:08 mxdvl