diff --git a/docs/components/content/examples/FormExampleSuperstruct.vue b/docs/components/content/examples/FormExampleSuperstruct.vue
new file mode 100644
index 00000000..3887a413
--- /dev/null
+++ b/docs/components/content/examples/FormExampleSuperstruct.vue
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Submit
+
+
+
diff --git a/docs/content/2.components/form.md b/docs/content/2.components/form.md
index 53f4ba35..a39d0e32 100644
--- a/docs/content/2.components/form.md
+++ b/docs/content/2.components/form.md
@@ -8,13 +8,13 @@ links:
## Usage
-Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://github.com/fabian-hiller/valibot), or your own validation logic.
+Use the Form component to validate form data using schema libraries such as [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://github.com/fabian-hiller/valibot), [Superstruct](https://github.com/ianstormtaylor/superstruct), or your own validation logic.
It works with the [FormGroup](/components/form-group) component to display error messages around form elements automatically.
The form component requires two props:
- `state` - a reactive object holding the form's state.
-- `schema` - a schema object from a validation library like [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi) or [Valibot](https://github.com/fabian-hiller/valibot).
+- `schema` - a schema object from a validation library like [Yup](https://github.com/jquense/yup), [Zod](https://github.com/colinhacks/zod), [Joi](https://github.com/hapijs/joi), [Valibot](https://github.com/fabian-hiller/valibot) or [Superstruct](https://github.com/ianstormtaylor/superstruct).
::callout{icon="i-heroicons-light-bulb"}
Note that **no validation library is included** by default, so ensure you **install the one you need**.
@@ -52,6 +52,13 @@ Note that **no validation library is included** by default, so ensure you **inst
class: 'w-60'
---
::
+ ::component-example{label="Superstruct"}
+ ---
+ component: 'form-example-superstruct'
+ componentProps:
+ class: 'w-60'
+ ---
+ ::
::
## Custom validation
diff --git a/package.json b/package.json
index 68ecfbf2..8071afb4 100644
--- a/package.json
+++ b/package.json
@@ -66,6 +66,7 @@
"joi": "^17.13.3",
"nuxt": "^3.13.2",
"release-it": "^17.7.0",
+ "superstruct": "^2.0.2",
"unbuild": "^2.0.0",
"valibot": "^0.42.1",
"valibot30": "npm:valibot@0.30.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3b49a85b..06f58810 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -109,6 +109,9 @@ importers:
release-it:
specifier: ^17.7.0
version: 17.7.0(typescript@5.6.2)
+ superstruct:
+ specifier: ^2.0.2
+ version: 2.0.2
unbuild:
specifier: ^2.0.0
version: 2.0.0(typescript@5.6.2)(vue-tsc@2.1.6(typescript@5.6.2))
@@ -6078,6 +6081,10 @@ packages:
resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==}
engines: {node: '>=16'}
+ superstruct@2.0.2:
+ resolution: {integrity: sha512-uV+TFRZdXsqXTL2pRvujROjdZQ4RAlBUS5BTh9IGm+jTqQntYThciG/qu57Gs69yjnVUSqdxF9YLmSnpupBW9A==}
+ engines: {node: '>=14.0.0'}
+
supports-color@5.5.0:
resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
engines: {node: '>=4'}
@@ -14318,6 +14325,8 @@ snapshots:
dependencies:
copy-anything: 3.0.5
+ superstruct@2.0.2: {}
+
supports-color@5.5.0:
dependencies:
has-flag: 3.0.0
diff --git a/src/runtime/components/forms/Form.vue b/src/runtime/components/forms/Form.vue
index cf124a15..a61c60d1 100644
--- a/src/runtime/components/forms/Form.vue
+++ b/src/runtime/components/forms/Form.vue
@@ -13,6 +13,7 @@ import type { ObjectSchema as YupObjectSchema, ValidationError as YupError } fro
import type { BaseSchema as ValibotSchema30, BaseSchemaAsync as ValibotSchemaAsync30 } from 'valibot30'
import type { GenericSchema as ValibotSchema31, GenericSchemaAsync as ValibotSchemaAsync31, SafeParser as ValibotSafeParser31, SafeParserAsync as ValibotSafeParserAsync31 } from 'valibot31'
import type { GenericSchema as ValibotSchema, GenericSchemaAsync as ValibotSchemaAsync, SafeParser as ValibotSafeParser, SafeParserAsync as ValibotSafeParserAsync } from 'valibot'
+import type { Struct } from 'superstruct'
import type { FormError, FormEvent, FormEventType, FormSubmitEvent, FormErrorEvent, Form } from '../../types/form'
import { useId } from '#imports'
@@ -35,7 +36,7 @@ export default defineComponent({
| PropType
| PropType | ValibotSafeParserAsync31>
| PropType
- | PropType | ValibotSafeParserAsync>,
+ | PropType | ValibotSafeParserAsync> | PropType>,
default: undefined
},
state: {
@@ -88,6 +89,8 @@ export default defineComponent({
errs = errs.concat(await getJoiErrors(props.state, props.schema))
} else if (isValibotSchema(props.schema)) {
errs = errs.concat(await getValibotError(props.state, props.schema))
+ } else if (isSuperStructSchema(props.schema)) {
+ errs = errs.concat(await getSuperStructErrors(props.state, props.schema))
} else {
throw new Error('Form validation failed: Unsupported form schema')
}
@@ -195,6 +198,15 @@ function isYupError (error: any): error is YupError {
return error.inner !== undefined
}
+function isSuperStructSchema (schema: any): schema is Struct {
+ return (
+ 'schema' in schema &&
+ typeof schema.coercer === 'function' &&
+ typeof schema.validator === 'function' &&
+ typeof schema.refiner === 'function'
+ )
+}
+
async function getYupErrors (
state: any,
schema: YupObjectSchema
@@ -218,6 +230,18 @@ function isZodSchema (schema: any): schema is ZodSchema {
return schema.parse !== undefined
}
+async function getSuperStructErrors (state: any, schema: Struct): Promise {
+ const [err] = schema.validate(state)
+ if (err) {
+ const errors = err.failures()
+ return errors.map((error) => ({
+ message: error.message,
+ path: error.path.join('.')
+ }))
+ }
+ return []
+}
+
async function getZodErrors (
state: any,
schema: ZodSchema
@@ -259,6 +283,7 @@ async function getJoiErrors (
}
}
+
function isValibotSchema (schema: any): schema is ValibotSchema30 | ValibotSchemaAsync30 | ValibotSchema31 | ValibotSchemaAsync31 | ValibotSafeParser31 | ValibotSafeParserAsync31 | ValibotSchema | ValibotSchemaAsync | ValibotSafeParser | ValibotSafeParserAsync {
return '_parse' in schema || '_run' in schema || (typeof schema === 'function' && 'schema' in schema)
}