# Form Contracts SigilJS can define the structure of form values once, then enforce, transform, project, and prove form behavior from the same contract. Forms are one of the most common places where untrusted user data enters an application. A Sigil contract turns that untrusted input into trusted runtime data before any business logic sees it. --- ## Why forms need runtime contracts Form submissions arrive as plain objects. Fields may be missing, the wrong type, out of range, or contain values your application never expected. Browser validation helps but is not a security boundary — it runs in user-controlled code. A runtime contract validates the submission on the server (or in a trusted runtime context) independently of the browser. The same contract also provides metadata for the form UI, test fixtures, and documentation. --- ## Define a form contract ```js import { oneOf, optional, sigil } from '@weipertda/sigiljs'; const SignupForm = sigil.exact({ name: String, email: String, role: oneOf('admin', 'user'), age: optional(Number), }); ``` Use `sigil.exact()` for form contracts so unexpected fields (extra keys) are rejected at the boundary. --- ## Enforce form values ### parse() — throws on invalid input ```js const trusted = SignupForm.parse(formValues); // trusted.name, trusted.email, trusted.role are now verified ``` ### safeParse() — never throws ```js const result = SignupForm.safeParse(formValues); if (!result.success) { console.error(result.error.message); console.log('Failed field path:', result.error.path); } else { await createUser(result.data); } ``` `safeParse()` is the recommended form submission handler — it gives you the error without throwing. --- ## Transform form values Chain a transform to normalize values before enforcement: ```js const NormalizedSignup = SignupForm.transform((data) => ({ ...data, email: data.email.toLowerCase().trim(), name: data.name.trim(), })); const result = NormalizedSignup.safeParse(rawFormValues); ``` The transform runs on the validated data — so transforms see a trusted, typed value. --- ## Required and optional fields Required fields must be present and match their type. Optional fields can be absent or `undefined` — the constraint is not enforced if missing. ```js const ProfileForm = sigil.exact({ displayName: String, // required bio: optional(String), // optional }); ``` --- ## Literal unions as select options `oneOf(...)` defines an enum-like field that maps to a `