diff --git a/package.json b/package.json index 1de36fff8e..c8da03ca74 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "devDependencies": { "@nuxt/eslint-config": "^1.0.0", "@nuxt/module-builder": "^0.8.4", + "@standard-schema/spec": "^1.0.0", "@nuxt/test-utils": "^3.15.4", "@release-it/conventional-changelog": "^10.0.0", "@vue/test-utils": "^2.4.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 768b9d38f4..72bc323dbd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,6 +95,9 @@ importers: '@release-it/conventional-changelog': specifier: ^10.0.0 version: 10.0.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.0.0)(release-it@18.1.2(@types/node@22.13.0)(typescript@5.6.3)) + '@standard-schema/spec': + specifier: ^1.0.0 + version: 1.0.0 '@vue/test-utils': specifier: ^2.4.6 version: 2.4.6 @@ -1678,6 +1681,9 @@ packages: '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@stylistic/eslint-plugin@3.0.1': resolution: {integrity: sha512-rQ3tcT5N2cynofJfbjUsnL4seoewTaOVBLyUEwtNldo7iNMPo3h/GUQk+Cl3iHEWwRxjq2wuH6q0FufQrbVL1A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -8547,6 +8553,8 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} + '@standard-schema/spec@1.0.0': {} + '@stylistic/eslint-plugin@3.0.1(eslint@9.19.0(jiti@2.4.2))(typescript@5.6.3)': dependencies: '@typescript-eslint/utils': 8.22.0(eslint@9.19.0(jiti@2.4.2))(typescript@5.6.3) diff --git a/src/runtime/components/forms/Form.vue b/src/runtime/components/forms/Form.vue index 2eb4c9642d..83f9874462 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 { StandardSchemaV1 } from '@standard-schema/spec' import type { Struct } from 'superstruct' import type { FormError, FormEvent, FormEventType, FormSubmitEvent, FormErrorEvent, Form, ValidateReturnSchema } from '../../types/form' import { useId } from '#imports' @@ -33,6 +34,7 @@ type Schema = PropType | PropType | ValibotSafeParserAsync31> | PropType | PropType | ValibotSafeParserAsync> | PropType> + | PropType export default defineComponent({ props: { @@ -220,6 +222,35 @@ function isZodSchema(schema: any): schema is ZodSchema { return schema.parse !== undefined } +export function isStandardSchema(schema: any): schema is StandardSchemaV1 { + return '~standard' in schema +} + +export async function validateStandardSchema( + state: any, + schema: StandardSchemaV1 +): Promise> { + const result = await schema['~standard'].validate(state) + + if (!result.issues || result.issues.length === 0) { + const output = ('value' in result ? result.value : null) + return { + errors: null, + result: output + } + } + + const errors = result.issues.map(issue => ({ + path: issue.path?.map(item => typeof item === 'object' ? item.key : item).join('.') || '', + message: issue.message + })) + + return { + errors, + result: null + } +} + async function validateValibotSchema( state: any, schema: ValibotSchema30 | ValibotSchemaAsync30 | ValibotSchema31 | ValibotSchemaAsync31 | ValibotSafeParser31 | ValibotSafeParserAsync31 | ValibotSchema | ValibotSchemaAsync | ValibotSafeParser | ValibotSafeParserAsync @@ -346,7 +377,9 @@ async function validateYupSchema( } function parseSchema(state: any, schema: Schema): Promise> { - if (isZodSchema(schema)) { + if (isStandardSchema(schema)) { + return validateStandardSchema(state, schema) + } else if (isZodSchema(schema)) { return validateZodSchema(state, schema) } else if (isJoiSchema(schema)) { return validateJoiSchema(state, schema)