@pyreon/validation 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"arktype.js","names":[],"sources":["../src/utils.ts","../src/arktype.ts"],"sourcesContent":["import type { ValidationError } from '@pyreon/form'\nimport type { ValidationIssue } from './types'\n\n/**\n * Convert an array of validation issues into a flat field → error record.\n * For nested paths like [\"address\", \"city\"], produces \"address.city\".\n * When multiple issues exist for the same path, the first message wins.\n */\nexport function issuesToRecord<TValues extends Record<string, unknown>>(\n issues: ValidationIssue[],\n): Partial<Record<keyof TValues, ValidationError>> {\n const errors = {} as Partial<Record<keyof TValues, ValidationError>>\n for (const issue of issues) {\n const key = issue.path as keyof TValues\n // First error per field wins\n if (errors[key] === undefined) {\n errors[key] = issue.message\n }\n }\n return errors\n}\n","import type {\n SchemaValidateFn,\n ValidateFn,\n ValidationError,\n} from '@pyreon/form'\nimport type { ValidationIssue } from './types'\nimport { issuesToRecord } from './utils'\n\n/**\n * Minimal ArkType-compatible interfaces so we don't require arktype as a hard dep.\n */\ninterface ArkError {\n path: PropertyKey[]\n message: string\n}\n\ninterface ArkErrors extends Array<ArkError> {\n summary: string\n}\n\n/**\n * Internal callable interface matching ArkType's Type.\n * Not exposed publicly — consumers pass their ArkType schema directly.\n */\ntype ArkTypeCallable = (data: unknown) => unknown\n\nfunction isArkErrors(result: unknown): result is ArkErrors {\n return Array.isArray(result) && 'summary' in (result as object)\n}\n\nfunction arkIssuesToGeneric(errors: ArkErrors): ValidationIssue[] {\n return errors.map((err) => ({\n path: err.path.map(String).join('.'),\n message: err.message,\n }))\n}\n\n/**\n * Create a form-level schema validator from an ArkType schema.\n *\n * Accepts any callable ArkType `Type` instance. The schema is duck-typed —\n * no ArkType import required.\n *\n * @example\n * import { type } from 'arktype'\n * import { arktypeSchema } from '@pyreon/validation/arktype'\n *\n * const schema = type({\n * email: 'string.email',\n * password: 'string >= 8',\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: arktypeSchema(schema),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function arktypeSchema<TValues extends Record<string, unknown>>(\n schema: ArkTypeCallable,\n): SchemaValidateFn<TValues> {\n return (values: TValues) => {\n try {\n const result = schema(values)\n if (!isArkErrors(result))\n return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(arkIssuesToGeneric(result))\n } catch (err) {\n return {\n '': err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from an ArkType schema.\n *\n * @example\n * import { type } from 'arktype'\n * import { arktypeField } from '@pyreon/validation/arktype'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: arktypeField(type('string.email')),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function arktypeField<T>(schema: ArkTypeCallable): ValidateFn<T> {\n return (value: T) => {\n try {\n const result = schema(value)\n if (!isArkErrors(result)) return undefined\n return result[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n"],"mappings":";;;;;;AAQA,SAAgB,eACd,QACiD;CACjD,MAAM,SAAS,EAAE;AACjB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,MAAM;AAElB,MAAI,OAAO,SAAS,OAClB,QAAO,OAAO,MAAM;;AAGxB,QAAO;;;;;ACOT,SAAS,YAAY,QAAsC;AACzD,QAAO,MAAM,QAAQ,OAAO,IAAI,aAAc;;AAGhD,SAAS,mBAAmB,QAAsC;AAChE,QAAO,OAAO,KAAK,SAAS;EAC1B,MAAM,IAAI,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI;EACpC,SAAS,IAAI;EACd,EAAE;;;;;;;;;;;;;;;;;;;;;;;AAwBL,SAAgB,cACd,QAC2B;AAC3B,SAAQ,WAAoB;AAC1B,MAAI;GACF,MAAM,SAAS,OAAO,OAAO;AAC7B,OAAI,CAAC,YAAY,OAAO,CACtB,QAAO,EAAE;AACX,UAAO,eAAwB,mBAAmB,OAAO,CAAC;WACnD,KAAK;AACZ,UAAO,EACL,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,EACrD;;;;;;;;;;;;;;;;;;;AAoBP,SAAgB,aAAgB,QAAwC;AACtE,SAAQ,UAAa;AACnB,MAAI;GACF,MAAM,SAAS,OAAO,MAAM;AAC5B,OAAI,CAAC,YAAY,OAAO,CAAE,QAAO;AACjC,UAAO,OAAO,IAAI;WACX,KAAK;AACZ,UAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI"}
1
+ {"version":3,"file":"arktype.js","names":[],"sources":["../src/utils.ts","../src/arktype.ts"],"sourcesContent":["import type { ValidationError } from \"@pyreon/form\"\nimport type { ValidationIssue } from \"./types\"\n\n/**\n * Convert an array of validation issues into a flat field → error record.\n * For nested paths like [\"address\", \"city\"], produces \"address.city\".\n * When multiple issues exist for the same path, the first message wins.\n */\nexport function issuesToRecord<TValues extends Record<string, unknown>>(\n issues: ValidationIssue[],\n): Partial<Record<keyof TValues, ValidationError>> {\n const errors = {} as Partial<Record<keyof TValues, ValidationError>>\n for (const issue of issues) {\n const key = issue.path as keyof TValues\n // First error per field wins\n if (errors[key] === undefined) {\n errors[key] = issue.message\n }\n }\n return errors\n}\n","import type { SchemaValidateFn, ValidateFn, ValidationError } from \"@pyreon/form\"\nimport type { ValidationIssue } from \"./types\"\nimport { issuesToRecord } from \"./utils\"\n\n/**\n * Minimal ArkType-compatible interfaces so we don't require arktype as a hard dep.\n */\ninterface ArkError {\n path: PropertyKey[]\n message: string\n}\n\ninterface ArkErrors extends Array<ArkError> {\n summary: string\n}\n\n/**\n * Internal callable interface matching ArkType's Type.\n * Not exposed publicly — consumers pass their ArkType schema directly.\n */\ntype ArkTypeCallable = (data: unknown) => unknown\n\nfunction isArkErrors(result: unknown): result is ArkErrors {\n return Array.isArray(result) && \"summary\" in (result as object)\n}\n\nfunction arkIssuesToGeneric(errors: ArkErrors): ValidationIssue[] {\n return errors.map((err) => ({\n path: err.path.map(String).join(\".\"),\n message: err.message,\n }))\n}\n\n/**\n * Create a form-level schema validator from an ArkType schema.\n *\n * Accepts any callable ArkType `Type` instance. The schema is duck-typed —\n * no ArkType import required.\n *\n * @example\n * import { type } from 'arktype'\n * import { arktypeSchema } from '@pyreon/validation/arktype'\n *\n * const schema = type({\n * email: 'string.email',\n * password: 'string >= 8',\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: arktypeSchema(schema),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function arktypeSchema<TValues extends Record<string, unknown>>(\n schema: ArkTypeCallable,\n): SchemaValidateFn<TValues> {\n return (values: TValues) => {\n try {\n const result = schema(values)\n if (!isArkErrors(result)) return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(arkIssuesToGeneric(result))\n } catch (err) {\n return {\n \"\": err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from an ArkType schema.\n *\n * @example\n * import { type } from 'arktype'\n * import { arktypeField } from '@pyreon/validation/arktype'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: arktypeField(type('string.email')),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function arktypeField<T>(schema: ArkTypeCallable): ValidateFn<T> {\n return (value: T) => {\n try {\n const result = schema(value)\n if (!isArkErrors(result)) return undefined\n return result[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n"],"mappings":";;;;;;AAQA,SAAgB,eACd,QACiD;CACjD,MAAM,SAAS,EAAE;AACjB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,MAAM;AAElB,MAAI,OAAO,SAAS,OAClB,QAAO,OAAO,MAAM;;AAGxB,QAAO;;;;;ACGT,SAAS,YAAY,QAAsC;AACzD,QAAO,MAAM,QAAQ,OAAO,IAAI,aAAc;;AAGhD,SAAS,mBAAmB,QAAsC;AAChE,QAAO,OAAO,KAAK,SAAS;EAC1B,MAAM,IAAI,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI;EACpC,SAAS,IAAI;EACd,EAAE;;;;;;;;;;;;;;;;;;;;;;;AAwBL,SAAgB,cACd,QAC2B;AAC3B,SAAQ,WAAoB;AAC1B,MAAI;GACF,MAAM,SAAS,OAAO,OAAO;AAC7B,OAAI,CAAC,YAAY,OAAO,CAAE,QAAO,EAAE;AACnC,UAAO,eAAwB,mBAAmB,OAAO,CAAC;WACnD,KAAK;AACZ,UAAO,EACL,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,EACrD;;;;;;;;;;;;;;;;;;;AAoBP,SAAgB,aAAgB,QAAwC;AACtE,SAAQ,UAAa;AACnB,MAAI;GACF,MAAM,SAAS,OAAO,MAAM;AAC5B,OAAI,CAAC,YAAY,OAAO,CAAE,QAAO;AACjC,UAAO,OAAO,IAAI;WACX,KAAK;AACZ,UAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI"}
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/utils.ts","../src/arktype.ts","../src/valibot.ts","../src/zod.ts"],"sourcesContent":["import type { ValidationError } from '@pyreon/form'\nimport type { ValidationIssue } from './types'\n\n/**\n * Convert an array of validation issues into a flat field → error record.\n * For nested paths like [\"address\", \"city\"], produces \"address.city\".\n * When multiple issues exist for the same path, the first message wins.\n */\nexport function issuesToRecord<TValues extends Record<string, unknown>>(\n issues: ValidationIssue[],\n): Partial<Record<keyof TValues, ValidationError>> {\n const errors = {} as Partial<Record<keyof TValues, ValidationError>>\n for (const issue of issues) {\n const key = issue.path as keyof TValues\n // First error per field wins\n if (errors[key] === undefined) {\n errors[key] = issue.message\n }\n }\n return errors\n}\n","import type {\n SchemaValidateFn,\n ValidateFn,\n ValidationError,\n} from '@pyreon/form'\nimport type { ValidationIssue } from './types'\nimport { issuesToRecord } from './utils'\n\n/**\n * Minimal ArkType-compatible interfaces so we don't require arktype as a hard dep.\n */\ninterface ArkError {\n path: PropertyKey[]\n message: string\n}\n\ninterface ArkErrors extends Array<ArkError> {\n summary: string\n}\n\n/**\n * Internal callable interface matching ArkType's Type.\n * Not exposed publicly — consumers pass their ArkType schema directly.\n */\ntype ArkTypeCallable = (data: unknown) => unknown\n\nfunction isArkErrors(result: unknown): result is ArkErrors {\n return Array.isArray(result) && 'summary' in (result as object)\n}\n\nfunction arkIssuesToGeneric(errors: ArkErrors): ValidationIssue[] {\n return errors.map((err) => ({\n path: err.path.map(String).join('.'),\n message: err.message,\n }))\n}\n\n/**\n * Create a form-level schema validator from an ArkType schema.\n *\n * Accepts any callable ArkType `Type` instance. The schema is duck-typed —\n * no ArkType import required.\n *\n * @example\n * import { type } from 'arktype'\n * import { arktypeSchema } from '@pyreon/validation/arktype'\n *\n * const schema = type({\n * email: 'string.email',\n * password: 'string >= 8',\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: arktypeSchema(schema),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function arktypeSchema<TValues extends Record<string, unknown>>(\n schema: ArkTypeCallable,\n): SchemaValidateFn<TValues> {\n return (values: TValues) => {\n try {\n const result = schema(values)\n if (!isArkErrors(result))\n return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(arkIssuesToGeneric(result))\n } catch (err) {\n return {\n '': err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from an ArkType schema.\n *\n * @example\n * import { type } from 'arktype'\n * import { arktypeField } from '@pyreon/validation/arktype'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: arktypeField(type('string.email')),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function arktypeField<T>(schema: ArkTypeCallable): ValidateFn<T> {\n return (value: T) => {\n try {\n const result = schema(value)\n if (!isArkErrors(result)) return undefined\n return result[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n","import type {\n SchemaValidateFn,\n ValidateFn,\n ValidationError,\n} from '@pyreon/form'\nimport type { ValidationIssue } from './types'\nimport { issuesToRecord } from './utils'\n\n/**\n * Minimal Valibot-compatible interfaces so we don't require valibot as a hard dep.\n */\ninterface ValibotPathItem {\n key: string | number\n}\n\ninterface ValibotIssue {\n path?: ValibotPathItem[]\n message: string\n}\n\ninterface ValibotSafeParseResult {\n success: boolean\n output?: unknown\n issues?: ValibotIssue[]\n}\n\n/**\n * Any function that takes (schema, input, ...rest) and returns a parse result.\n * Valibot's safeParse/safeParseAsync have generic constraints on the schema\n * parameter that can't be expressed without importing Valibot types. We accept\n * any callable and cast internally.\n */\n// biome-ignore lint/complexity/noBannedTypes: must accept any valibot parse function\ntype GenericSafeParseFn = Function\n\nfunction valibotIssuesToGeneric(issues: ValibotIssue[]): ValidationIssue[] {\n return issues.map((issue) => ({\n path: issue.path?.map((p) => String(p.key)).join('.') ?? '',\n message: issue.message,\n }))\n}\n\ntype InternalParseFn = (\n schema: unknown,\n input: unknown,\n) => ValibotSafeParseResult | Promise<ValibotSafeParseResult>\n\n/**\n * Create a form-level schema validator from a Valibot schema.\n *\n * Valibot uses standalone functions rather than methods, so you must pass\n * the `safeParseAsync` (or `safeParse`) function from valibot.\n *\n * @example\n * import * as v from 'valibot'\n * import { valibotSchema } from '@pyreon/validation/valibot'\n *\n * const schema = v.object({\n * email: v.pipe(v.string(), v.email()),\n * password: v.pipe(v.string(), v.minLength(8)),\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: valibotSchema(schema, v.safeParseAsync),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function valibotSchema<TValues extends Record<string, unknown>>(\n schema: unknown,\n safeParseFn: GenericSafeParseFn,\n): SchemaValidateFn<TValues> {\n const parse = safeParseFn as InternalParseFn\n return async (values: TValues) => {\n try {\n const result = await parse(schema, values)\n if (result.success)\n return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(\n valibotIssuesToGeneric(result.issues ?? []),\n )\n } catch (err) {\n return {\n '': err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from a Valibot schema.\n *\n * @example\n * import * as v from 'valibot'\n * import { valibotField } from '@pyreon/validation/valibot'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: valibotField(v.pipe(v.string(), v.email('Invalid email')), v.safeParseAsync),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function valibotField<T>(\n schema: unknown,\n safeParseFn: GenericSafeParseFn,\n): ValidateFn<T> {\n const parse = safeParseFn as InternalParseFn\n return async (value: T) => {\n try {\n const result = await parse(schema, value)\n if (result.success) return undefined\n return result.issues?.[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n","import type {\n SchemaValidateFn,\n ValidateFn,\n ValidationError,\n} from '@pyreon/form'\nimport type { ValidationIssue } from './types'\nimport { issuesToRecord } from './utils'\n\n/**\n * Minimal Zod-compatible interfaces so we don't require zod as a hard dep.\n * These match Zod v3's public API surface.\n */\ninterface ZodIssue {\n path: PropertyKey[]\n message: string\n}\n\n/**\n * Duck-typed Zod schema interface — works with both Zod v3 and v4.\n * Inlines the result shape to avoid version-specific type mismatches.\n */\ninterface ZodSchema<T = unknown> {\n safeParse(data: unknown): {\n success: boolean\n data?: T\n error?: { issues: ZodIssue[] }\n }\n safeParseAsync(\n data: unknown,\n ): Promise<{ success: boolean; data?: T; error?: { issues: ZodIssue[] } }>\n}\n\nfunction zodIssuesToGeneric(issues: ZodIssue[]): ValidationIssue[] {\n return issues.map((issue) => ({\n path: issue.path.map(String).join('.'),\n message: issue.message,\n }))\n}\n\n/**\n * Create a form-level schema validator from a Zod schema.\n * Supports both sync and async Zod schemas (uses `safeParseAsync`).\n *\n * @example\n * import { z } from 'zod'\n * import { zodSchema } from '@pyreon/validation/zod'\n *\n * const schema = z.object({\n * email: z.string().email(),\n * password: z.string().min(8),\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: zodSchema(schema),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function zodSchema<TValues extends Record<string, unknown>>(\n schema: ZodSchema<TValues>,\n): SchemaValidateFn<TValues> {\n return async (values: TValues) => {\n try {\n const result = await schema.safeParseAsync(values)\n if (result.success)\n return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(zodIssuesToGeneric(result.error!.issues))\n } catch (err) {\n return {\n '': err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from a Zod schema.\n * Supports both sync and async Zod refinements.\n *\n * @example\n * import { z } from 'zod'\n * import { zodField } from '@pyreon/validation/zod'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: zodField(z.string().email('Invalid email')),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function zodField<T>(schema: ZodSchema<T>): ValidateFn<T> {\n return async (value: T) => {\n try {\n const result = await schema.safeParseAsync(value)\n if (result.success) return undefined\n return result.error!.issues[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/utils.ts","../src/arktype.ts","../src/valibot.ts","../src/zod.ts"],"sourcesContent":["import type { ValidationError } from \"@pyreon/form\"\nimport type { ValidationIssue } from \"./types\"\n\n/**\n * Convert an array of validation issues into a flat field → error record.\n * For nested paths like [\"address\", \"city\"], produces \"address.city\".\n * When multiple issues exist for the same path, the first message wins.\n */\nexport function issuesToRecord<TValues extends Record<string, unknown>>(\n issues: ValidationIssue[],\n): Partial<Record<keyof TValues, ValidationError>> {\n const errors = {} as Partial<Record<keyof TValues, ValidationError>>\n for (const issue of issues) {\n const key = issue.path as keyof TValues\n // First error per field wins\n if (errors[key] === undefined) {\n errors[key] = issue.message\n }\n }\n return errors\n}\n","import type { SchemaValidateFn, ValidateFn, ValidationError } from \"@pyreon/form\"\nimport type { ValidationIssue } from \"./types\"\nimport { issuesToRecord } from \"./utils\"\n\n/**\n * Minimal ArkType-compatible interfaces so we don't require arktype as a hard dep.\n */\ninterface ArkError {\n path: PropertyKey[]\n message: string\n}\n\ninterface ArkErrors extends Array<ArkError> {\n summary: string\n}\n\n/**\n * Internal callable interface matching ArkType's Type.\n * Not exposed publicly — consumers pass their ArkType schema directly.\n */\ntype ArkTypeCallable = (data: unknown) => unknown\n\nfunction isArkErrors(result: unknown): result is ArkErrors {\n return Array.isArray(result) && \"summary\" in (result as object)\n}\n\nfunction arkIssuesToGeneric(errors: ArkErrors): ValidationIssue[] {\n return errors.map((err) => ({\n path: err.path.map(String).join(\".\"),\n message: err.message,\n }))\n}\n\n/**\n * Create a form-level schema validator from an ArkType schema.\n *\n * Accepts any callable ArkType `Type` instance. The schema is duck-typed —\n * no ArkType import required.\n *\n * @example\n * import { type } from 'arktype'\n * import { arktypeSchema } from '@pyreon/validation/arktype'\n *\n * const schema = type({\n * email: 'string.email',\n * password: 'string >= 8',\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: arktypeSchema(schema),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function arktypeSchema<TValues extends Record<string, unknown>>(\n schema: ArkTypeCallable,\n): SchemaValidateFn<TValues> {\n return (values: TValues) => {\n try {\n const result = schema(values)\n if (!isArkErrors(result)) return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(arkIssuesToGeneric(result))\n } catch (err) {\n return {\n \"\": err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from an ArkType schema.\n *\n * @example\n * import { type } from 'arktype'\n * import { arktypeField } from '@pyreon/validation/arktype'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: arktypeField(type('string.email')),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function arktypeField<T>(schema: ArkTypeCallable): ValidateFn<T> {\n return (value: T) => {\n try {\n const result = schema(value)\n if (!isArkErrors(result)) return undefined\n return result[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n","import type { SchemaValidateFn, ValidateFn, ValidationError } from \"@pyreon/form\"\nimport type { ValidationIssue } from \"./types\"\nimport { issuesToRecord } from \"./utils\"\n\n/**\n * Minimal Valibot-compatible interfaces so we don't require valibot as a hard dep.\n */\ninterface ValibotPathItem {\n key: string | number\n}\n\ninterface ValibotIssue {\n path?: ValibotPathItem[]\n message: string\n}\n\ninterface ValibotSafeParseResult {\n success: boolean\n output?: unknown\n issues?: ValibotIssue[]\n}\n\n/**\n * Any function that takes (schema, input, ...rest) and returns a parse result.\n * Valibot's safeParse/safeParseAsync have generic constraints on the schema\n * parameter that can't be expressed without importing Valibot types. We accept\n * any callable and cast internally.\n */\n// biome-ignore lint/complexity/noBannedTypes: must accept any valibot parse function\ntype GenericSafeParseFn = Function\n\nfunction valibotIssuesToGeneric(issues: ValibotIssue[]): ValidationIssue[] {\n return issues.map((issue) => ({\n path: issue.path?.map((p) => String(p.key)).join(\".\") ?? \"\",\n message: issue.message,\n }))\n}\n\ntype InternalParseFn = (\n schema: unknown,\n input: unknown,\n) => ValibotSafeParseResult | Promise<ValibotSafeParseResult>\n\n/**\n * Create a form-level schema validator from a Valibot schema.\n *\n * Valibot uses standalone functions rather than methods, so you must pass\n * the `safeParseAsync` (or `safeParse`) function from valibot.\n *\n * @example\n * import * as v from 'valibot'\n * import { valibotSchema } from '@pyreon/validation/valibot'\n *\n * const schema = v.object({\n * email: v.pipe(v.string(), v.email()),\n * password: v.pipe(v.string(), v.minLength(8)),\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: valibotSchema(schema, v.safeParseAsync),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function valibotSchema<TValues extends Record<string, unknown>>(\n schema: unknown,\n safeParseFn: GenericSafeParseFn,\n): SchemaValidateFn<TValues> {\n const parse = safeParseFn as InternalParseFn\n return async (values: TValues) => {\n try {\n const result = await parse(schema, values)\n if (result.success) return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(valibotIssuesToGeneric(result.issues ?? []))\n } catch (err) {\n return {\n \"\": err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from a Valibot schema.\n *\n * @example\n * import * as v from 'valibot'\n * import { valibotField } from '@pyreon/validation/valibot'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: valibotField(v.pipe(v.string(), v.email('Invalid email')), v.safeParseAsync),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function valibotField<T>(schema: unknown, safeParseFn: GenericSafeParseFn): ValidateFn<T> {\n const parse = safeParseFn as InternalParseFn\n return async (value: T) => {\n try {\n const result = await parse(schema, value)\n if (result.success) return undefined\n return result.issues?.[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n","import type { SchemaValidateFn, ValidateFn, ValidationError } from \"@pyreon/form\"\nimport type { ValidationIssue } from \"./types\"\nimport { issuesToRecord } from \"./utils\"\n\n/**\n * Minimal Zod-compatible interfaces so we don't require zod as a hard dep.\n * These match Zod v3's public API surface.\n */\ninterface ZodIssue {\n path: PropertyKey[]\n message: string\n}\n\n/**\n * Duck-typed Zod schema interface — works with both Zod v3 and v4.\n * Inlines the result shape to avoid version-specific type mismatches.\n */\ninterface ZodSchema<T = unknown> {\n safeParse(data: unknown): {\n success: boolean\n data?: T\n error?: { issues: ZodIssue[] }\n }\n safeParseAsync(\n data: unknown,\n ): Promise<{ success: boolean; data?: T; error?: { issues: ZodIssue[] } }>\n}\n\nfunction zodIssuesToGeneric(issues: ZodIssue[]): ValidationIssue[] {\n return issues.map((issue) => ({\n path: issue.path.map(String).join(\".\"),\n message: issue.message,\n }))\n}\n\n/**\n * Create a form-level schema validator from a Zod schema.\n * Supports both sync and async Zod schemas (uses `safeParseAsync`).\n *\n * @example\n * import { z } from 'zod'\n * import { zodSchema } from '@pyreon/validation/zod'\n *\n * const schema = z.object({\n * email: z.string().email(),\n * password: z.string().min(8),\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: zodSchema(schema),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function zodSchema<TValues extends Record<string, unknown>>(\n schema: ZodSchema<TValues>,\n): SchemaValidateFn<TValues> {\n return async (values: TValues) => {\n try {\n const result = await schema.safeParseAsync(values)\n if (result.success) return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(zodIssuesToGeneric(result.error!.issues))\n } catch (err) {\n return {\n \"\": err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from a Zod schema.\n * Supports both sync and async Zod refinements.\n *\n * @example\n * import { z } from 'zod'\n * import { zodField } from '@pyreon/validation/zod'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: zodField(z.string().email('Invalid email')),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function zodField<T>(schema: ZodSchema<T>): ValidateFn<T> {\n return async (value: T) => {\n try {\n const result = await schema.safeParseAsync(value)\n if (result.success) return undefined\n return result.error!.issues[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"arktype2.d.ts","names":[],"sources":["../../../src/arktype.ts"],"mappings":";;;;;AAIqB;;KAoBhB,eAAA,IAAmB,IAAA;;;AAkCxB;;;;;;;;;;;;;;;;;AAgCA;;iBAhCgB,aAAA,iBAA8B,MAAA,kBAAA,CAC5C,MAAA,EAAQ,eAAA,GACP,gBAAA,CAAiB,OAAA;;;;;;;;;;;;;;;;iBA8BJ,YAAA,GAAA,CAAgB,MAAA,EAAQ,eAAA,GAAkB,UAAA,CAAW,CAAA"}
1
+ {"version":3,"file":"arktype2.d.ts","names":[],"sources":["../../../src/arktype.ts"],"mappings":";;;;;AAAiF;;KAoB5E,eAAA,IAAmB,IAAA;;;AAkCxB;;;;;;;;;;;;;;;;;AA+BA;;iBA/BgB,aAAA,iBAA8B,MAAA,kBAAA,CAC5C,MAAA,EAAQ,eAAA,GACP,gBAAA,CAAiB,OAAA;;;;;;;;;;;;;;;;iBA6BJ,YAAA,GAAA,CAAgB,MAAA,EAAQ,eAAA,GAAkB,UAAA,CAAW,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/arktype.ts","../../../src/types.ts","../../../src/utils.ts","../../../src/valibot.ts","../../../src/zod.ts"],"mappings":";;;;;AAIqB;;KAoBhB,eAAA,IAAmB,IAAA;;;AAkCxB;;;;;;;;;;;;;;;;;AAgCA;;iBAhCgB,aAAA,iBAA8B,MAAA,kBAAA,CAC5C,MAAA,EAAQ,eAAA,GACP,kBAAA,CAAiB,OAAA;;;;;;;;;;;;;;;;iBA8BJ,YAAA,GAAA,CAAgB,MAAA,EAAQ,eAAA,GAAkB,YAAA,CAAW,CAAA;;;AAtFhD;;;;AAAA,UCSJ,eAAA;ED6CD;EC3Cd,IAAA;ED2C2B;ECzC3B,OAAA;AAAA;;;;;KAOU,aAAA,6BAA0C,MAAA,mBACpD,MAAA,EAAQ,OAAA,KACL,gBAAA,CAAiB,OAAA;;;;;KAMV,YAAA,gBAA4B,MAAA,EAAQ,OAAA,KAAY,UAAA,CAAW,CAAA;;;;AD5BlD;;;;iBEIL,cAAA,iBAA+B,MAAA,kBAAA,CAC7C,MAAA,EAAQ,eAAA,KACP,OAAA,CAAQ,MAAA,OAAa,OAAA,EAAS,iBAAA;;;;;AFNZ;;;;KG6BhB,kBAAA,GAAqB,QAAA;AHyB1B;;;;;;;;;;;;;;;;;AAgCA;;;;AAhCA,iBGUgB,aAAA,iBAA8B,MAAA,kBAAA,CAC5C,MAAA,WACA,WAAA,EAAa,kBAAA,GACZ,kBAAA,CAAiB,OAAA;;;;;;;;;;;;;;AF1DpB;;iBE2FgB,YAAA,GAAA,CACd,MAAA,WACA,WAAA,EAAa,kBAAA,GACZ,YAAA,CAAW,CAAA;;;;;AHvGO;;UIQX,QAAA;EACR,IAAA,EAAM,WAAA;EACN,OAAA;AAAA;;;;;UAOQ,SAAA;EACR,SAAA,CAAU,IAAA;IACR,OAAA;IACA,IAAA,GAAO,CAAA;IACP,KAAA;MAAU,MAAA,EAAQ,QAAA;IAAA;EAAA;EAEpB,cAAA,CACE,IAAA,YACC,OAAA;IAAU,OAAA;IAAkB,IAAA,GAAO,CAAA;IAAG,KAAA;MAAU,MAAA,EAAQ,QAAA;IAAA;EAAA;AAAA;;;;;;;;;;;;;;;AHhB7D;;;;;iBG6CgB,SAAA,iBAA0B,MAAA,kBAAA,CACxC,MAAA,EAAQ,SAAA,CAAU,OAAA,IACjB,kBAAA,CAAiB,OAAA;;;;;;;;;;;;;;;;;iBA+BJ,QAAA,GAAA,CAAY,MAAA,EAAQ,SAAA,CAAU,CAAA,IAAK,YAAA,CAAW,CAAA"}
1
+ {"version":3,"file":"index2.d.ts","names":[],"sources":["../../../src/arktype.ts","../../../src/types.ts","../../../src/utils.ts","../../../src/valibot.ts","../../../src/zod.ts"],"mappings":";;;;;AAAiF;;KAoB5E,eAAA,IAAmB,IAAA;;;AAkCxB;;;;;;;;;;;;;;;;;AA+BA;;iBA/BgB,aAAA,iBAA8B,MAAA,kBAAA,CAC5C,MAAA,EAAQ,eAAA,GACP,kBAAA,CAAiB,OAAA;;;;;;;;;;;;;;;;iBA6BJ,YAAA,GAAA,CAAgB,MAAA,EAAQ,eAAA,GAAkB,YAAA,CAAW,CAAA;;;AArFY;;;;AAAA,UCShE,eAAA;ED6CD;EC3Cd,IAAA;ED2C2B;ECzC3B,OAAA;AAAA;;;;;KAOU,aAAA,6BAA0C,MAAA,mBACpD,MAAA,EAAQ,OAAA,KACL,gBAAA,CAAiB,OAAA;;;;;KAMV,YAAA,gBAA4B,MAAA,EAAQ,OAAA,KAAY,UAAA,CAAW,CAAA;;;;AD5BU;;;;iBEQjE,cAAA,iBAA+B,MAAA,kBAAA,CAC7C,MAAA,EAAQ,eAAA,KACP,OAAA,CAAQ,MAAA,OAAa,OAAA,EAAS,iBAAA;;;;;AFVgD;;;;KG6B5E,kBAAA,GAAqB,QAAA;AHyB1B;;;;;;;;;;;;;;;;;AA+BA;;;;AA/BA,iBGUgB,aAAA,iBAA8B,MAAA,kBAAA,CAC5C,MAAA,WACA,WAAA,EAAa,kBAAA,GACZ,kBAAA,CAAiB,OAAA;;;;;;;;;;;;;;AF1DpB;;iBEwFgB,YAAA,GAAA,CAAgB,MAAA,WAAiB,WAAA,EAAa,kBAAA,GAAqB,YAAA,CAAW,CAAA;;;;;AHjGb;;UIQvE,QAAA;EACR,IAAA,EAAM,WAAA;EACN,OAAA;AAAA;;;;;UAOQ,SAAA;EACR,SAAA,CAAU,IAAA;IACR,OAAA;IACA,IAAA,GAAO,CAAA;IACP,KAAA;MAAU,MAAA,EAAQ,QAAA;IAAA;EAAA;EAEpB,cAAA,CACE,IAAA,YACC,OAAA;IAAU,OAAA;IAAkB,IAAA,GAAO,CAAA;IAAG,KAAA;MAAU,MAAA,EAAQ,QAAA;IAAA;EAAA;AAAA;;;;;;;;;;;;;;;AHhB7D;;;;;iBG6CgB,SAAA,iBAA0B,MAAA,kBAAA,CACxC,MAAA,EAAQ,SAAA,CAAU,OAAA,IACjB,kBAAA,CAAiB,OAAA;;;;;;;;;;;;;;;;;iBA8BJ,QAAA,GAAA,CAAY,MAAA,EAAQ,SAAA,CAAU,CAAA,IAAK,YAAA,CAAW,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"valibot2.d.ts","names":[],"sources":["../../../src/valibot.ts"],"mappings":";;;;;AAIqB;;;;KA6BhB,kBAAA,GAAqB,QAAA;AAmC1B;;;;;;;;;;;;;;;;;;AAoCA;;;AApCA,iBAAgB,aAAA,iBAA8B,MAAA,kBAAA,CAC5C,MAAA,WACA,WAAA,EAAa,kBAAA,GACZ,gBAAA,CAAiB,OAAA;;;;;;;;;;;;;;;;iBAiCJ,YAAA,GAAA,CACd,MAAA,WACA,WAAA,EAAa,kBAAA,GACZ,UAAA,CAAW,CAAA"}
1
+ {"version":3,"file":"valibot2.d.ts","names":[],"sources":["../../../src/valibot.ts"],"mappings":";;;;;AAAiF;;;;KA6B5E,kBAAA,GAAqB,QAAA;AAmC1B;;;;;;;;;;;;;;;;;;AAiCA;;;AAjCA,iBAAgB,aAAA,iBAA8B,MAAA,kBAAA,CAC5C,MAAA,WACA,WAAA,EAAa,kBAAA,GACZ,gBAAA,CAAiB,OAAA;;;;;;;;;;;;;;;;iBA8BJ,YAAA,GAAA,CAAgB,MAAA,WAAiB,WAAA,EAAa,kBAAA,GAAqB,UAAA,CAAW,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"zod2.d.ts","names":[],"sources":["../../../src/zod.ts"],"mappings":";;;;;AAIqB;;UAQX,QAAA;EACR,IAAA,EAAM,WAAA;EACN,OAAA;AAAA;;;;AAAO;UAOC,SAAA;EACR,SAAA,CAAU,IAAA;IACR,OAAA;IACA,IAAA,GAAO,CAAA;IACP,KAAA;MAAU,MAAA,EAAQ,QAAA;IAAA;EAAA;EAEpB,cAAA,CACE,IAAA,YACC,OAAA;IAAU,OAAA;IAAkB,IAAA,GAAO,CAAA;IAAG,KAAA;MAAU,MAAA,EAAQ,QAAA;IAAA;EAAA;AAAA;;;;;;;;;;;;;;AA6B7D;;;;;;iBAAgB,SAAA,iBAA0B,MAAA,kBAAA,CACxC,MAAA,EAAQ,SAAA,CAAU,OAAA,IACjB,gBAAA,CAAiB,OAAA;;;;;;;;;;;;;AA+BpB;;;;iBAAgB,QAAA,GAAA,CAAY,MAAA,EAAQ,SAAA,CAAU,CAAA,IAAK,UAAA,CAAW,CAAA"}
1
+ {"version":3,"file":"zod2.d.ts","names":[],"sources":["../../../src/zod.ts"],"mappings":";;;;;AAAiF;;UAQvE,QAAA;EACR,IAAA,EAAM,WAAA;EACN,OAAA;AAAA;;;;AAAO;UAOC,SAAA;EACR,SAAA,CAAU,IAAA;IACR,OAAA;IACA,IAAA,GAAO,CAAA;IACP,KAAA;MAAU,MAAA,EAAQ,QAAA;IAAA;EAAA;EAEpB,cAAA,CACE,IAAA,YACC,OAAA;IAAU,OAAA;IAAkB,IAAA,GAAO,CAAA;IAAG,KAAA;MAAU,MAAA,EAAQ,QAAA;IAAA;EAAA;AAAA;;;;;;;;;;;;;;AA6B7D;;;;;;iBAAgB,SAAA,iBAA0B,MAAA,kBAAA,CACxC,MAAA,EAAQ,SAAA,CAAU,OAAA,IACjB,gBAAA,CAAiB,OAAA;;;;;;;;;;;;;AA8BpB;;;;iBAAgB,QAAA,GAAA,CAAY,MAAA,EAAQ,SAAA,CAAU,CAAA,IAAK,UAAA,CAAW,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"valibot.js","names":[],"sources":["../src/utils.ts","../src/valibot.ts"],"sourcesContent":["import type { ValidationError } from '@pyreon/form'\nimport type { ValidationIssue } from './types'\n\n/**\n * Convert an array of validation issues into a flat field → error record.\n * For nested paths like [\"address\", \"city\"], produces \"address.city\".\n * When multiple issues exist for the same path, the first message wins.\n */\nexport function issuesToRecord<TValues extends Record<string, unknown>>(\n issues: ValidationIssue[],\n): Partial<Record<keyof TValues, ValidationError>> {\n const errors = {} as Partial<Record<keyof TValues, ValidationError>>\n for (const issue of issues) {\n const key = issue.path as keyof TValues\n // First error per field wins\n if (errors[key] === undefined) {\n errors[key] = issue.message\n }\n }\n return errors\n}\n","import type {\n SchemaValidateFn,\n ValidateFn,\n ValidationError,\n} from '@pyreon/form'\nimport type { ValidationIssue } from './types'\nimport { issuesToRecord } from './utils'\n\n/**\n * Minimal Valibot-compatible interfaces so we don't require valibot as a hard dep.\n */\ninterface ValibotPathItem {\n key: string | number\n}\n\ninterface ValibotIssue {\n path?: ValibotPathItem[]\n message: string\n}\n\ninterface ValibotSafeParseResult {\n success: boolean\n output?: unknown\n issues?: ValibotIssue[]\n}\n\n/**\n * Any function that takes (schema, input, ...rest) and returns a parse result.\n * Valibot's safeParse/safeParseAsync have generic constraints on the schema\n * parameter that can't be expressed without importing Valibot types. We accept\n * any callable and cast internally.\n */\n// biome-ignore lint/complexity/noBannedTypes: must accept any valibot parse function\ntype GenericSafeParseFn = Function\n\nfunction valibotIssuesToGeneric(issues: ValibotIssue[]): ValidationIssue[] {\n return issues.map((issue) => ({\n path: issue.path?.map((p) => String(p.key)).join('.') ?? '',\n message: issue.message,\n }))\n}\n\ntype InternalParseFn = (\n schema: unknown,\n input: unknown,\n) => ValibotSafeParseResult | Promise<ValibotSafeParseResult>\n\n/**\n * Create a form-level schema validator from a Valibot schema.\n *\n * Valibot uses standalone functions rather than methods, so you must pass\n * the `safeParseAsync` (or `safeParse`) function from valibot.\n *\n * @example\n * import * as v from 'valibot'\n * import { valibotSchema } from '@pyreon/validation/valibot'\n *\n * const schema = v.object({\n * email: v.pipe(v.string(), v.email()),\n * password: v.pipe(v.string(), v.minLength(8)),\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: valibotSchema(schema, v.safeParseAsync),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function valibotSchema<TValues extends Record<string, unknown>>(\n schema: unknown,\n safeParseFn: GenericSafeParseFn,\n): SchemaValidateFn<TValues> {\n const parse = safeParseFn as InternalParseFn\n return async (values: TValues) => {\n try {\n const result = await parse(schema, values)\n if (result.success)\n return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(\n valibotIssuesToGeneric(result.issues ?? []),\n )\n } catch (err) {\n return {\n '': err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from a Valibot schema.\n *\n * @example\n * import * as v from 'valibot'\n * import { valibotField } from '@pyreon/validation/valibot'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: valibotField(v.pipe(v.string(), v.email('Invalid email')), v.safeParseAsync),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function valibotField<T>(\n schema: unknown,\n safeParseFn: GenericSafeParseFn,\n): ValidateFn<T> {\n const parse = safeParseFn as InternalParseFn\n return async (value: T) => {\n try {\n const result = await parse(schema, value)\n if (result.success) return undefined\n return result.issues?.[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n"],"mappings":";;;;;;AAQA,SAAgB,eACd,QACiD;CACjD,MAAM,SAAS,EAAE;AACjB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,MAAM;AAElB,MAAI,OAAO,SAAS,OAClB,QAAO,OAAO,MAAM;;AAGxB,QAAO;;;;;ACgBT,SAAS,uBAAuB,QAA2C;AACzE,QAAO,OAAO,KAAK,WAAW;EAC5B,MAAM,MAAM,MAAM,KAAK,MAAM,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI;EACzD,SAAS,MAAM;EAChB,EAAE;;;;;;;;;;;;;;;;;;;;;;;AA6BL,SAAgB,cACd,QACA,aAC2B;CAC3B,MAAM,QAAQ;AACd,QAAO,OAAO,WAAoB;AAChC,MAAI;GACF,MAAM,SAAS,MAAM,MAAM,QAAQ,OAAO;AAC1C,OAAI,OAAO,QACT,QAAO,EAAE;AACX,UAAO,eACL,uBAAuB,OAAO,UAAU,EAAE,CAAC,CAC5C;WACM,KAAK;AACZ,UAAO,EACL,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,EACrD;;;;;;;;;;;;;;;;;;;AAoBP,SAAgB,aACd,QACA,aACe;CACf,MAAM,QAAQ;AACd,QAAO,OAAO,UAAa;AACzB,MAAI;GACF,MAAM,SAAS,MAAM,MAAM,QAAQ,MAAM;AACzC,OAAI,OAAO,QAAS,QAAO;AAC3B,UAAO,OAAO,SAAS,IAAI;WACpB,KAAK;AACZ,UAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI"}
1
+ {"version":3,"file":"valibot.js","names":[],"sources":["../src/utils.ts","../src/valibot.ts"],"sourcesContent":["import type { ValidationError } from \"@pyreon/form\"\nimport type { ValidationIssue } from \"./types\"\n\n/**\n * Convert an array of validation issues into a flat field → error record.\n * For nested paths like [\"address\", \"city\"], produces \"address.city\".\n * When multiple issues exist for the same path, the first message wins.\n */\nexport function issuesToRecord<TValues extends Record<string, unknown>>(\n issues: ValidationIssue[],\n): Partial<Record<keyof TValues, ValidationError>> {\n const errors = {} as Partial<Record<keyof TValues, ValidationError>>\n for (const issue of issues) {\n const key = issue.path as keyof TValues\n // First error per field wins\n if (errors[key] === undefined) {\n errors[key] = issue.message\n }\n }\n return errors\n}\n","import type { SchemaValidateFn, ValidateFn, ValidationError } from \"@pyreon/form\"\nimport type { ValidationIssue } from \"./types\"\nimport { issuesToRecord } from \"./utils\"\n\n/**\n * Minimal Valibot-compatible interfaces so we don't require valibot as a hard dep.\n */\ninterface ValibotPathItem {\n key: string | number\n}\n\ninterface ValibotIssue {\n path?: ValibotPathItem[]\n message: string\n}\n\ninterface ValibotSafeParseResult {\n success: boolean\n output?: unknown\n issues?: ValibotIssue[]\n}\n\n/**\n * Any function that takes (schema, input, ...rest) and returns a parse result.\n * Valibot's safeParse/safeParseAsync have generic constraints on the schema\n * parameter that can't be expressed without importing Valibot types. We accept\n * any callable and cast internally.\n */\n// biome-ignore lint/complexity/noBannedTypes: must accept any valibot parse function\ntype GenericSafeParseFn = Function\n\nfunction valibotIssuesToGeneric(issues: ValibotIssue[]): ValidationIssue[] {\n return issues.map((issue) => ({\n path: issue.path?.map((p) => String(p.key)).join(\".\") ?? \"\",\n message: issue.message,\n }))\n}\n\ntype InternalParseFn = (\n schema: unknown,\n input: unknown,\n) => ValibotSafeParseResult | Promise<ValibotSafeParseResult>\n\n/**\n * Create a form-level schema validator from a Valibot schema.\n *\n * Valibot uses standalone functions rather than methods, so you must pass\n * the `safeParseAsync` (or `safeParse`) function from valibot.\n *\n * @example\n * import * as v from 'valibot'\n * import { valibotSchema } from '@pyreon/validation/valibot'\n *\n * const schema = v.object({\n * email: v.pipe(v.string(), v.email()),\n * password: v.pipe(v.string(), v.minLength(8)),\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: valibotSchema(schema, v.safeParseAsync),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function valibotSchema<TValues extends Record<string, unknown>>(\n schema: unknown,\n safeParseFn: GenericSafeParseFn,\n): SchemaValidateFn<TValues> {\n const parse = safeParseFn as InternalParseFn\n return async (values: TValues) => {\n try {\n const result = await parse(schema, values)\n if (result.success) return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(valibotIssuesToGeneric(result.issues ?? []))\n } catch (err) {\n return {\n \"\": err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from a Valibot schema.\n *\n * @example\n * import * as v from 'valibot'\n * import { valibotField } from '@pyreon/validation/valibot'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: valibotField(v.pipe(v.string(), v.email('Invalid email')), v.safeParseAsync),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function valibotField<T>(schema: unknown, safeParseFn: GenericSafeParseFn): ValidateFn<T> {\n const parse = safeParseFn as InternalParseFn\n return async (value: T) => {\n try {\n const result = await parse(schema, value)\n if (result.success) return undefined\n return result.issues?.[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n"],"mappings":";;;;;;AAQA,SAAgB,eACd,QACiD;CACjD,MAAM,SAAS,EAAE;AACjB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,MAAM;AAElB,MAAI,OAAO,SAAS,OAClB,QAAO,OAAO,MAAM;;AAGxB,QAAO;;;;;ACYT,SAAS,uBAAuB,QAA2C;AACzE,QAAO,OAAO,KAAK,WAAW;EAC5B,MAAM,MAAM,MAAM,KAAK,MAAM,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,IAAI,IAAI;EACzD,SAAS,MAAM;EAChB,EAAE;;;;;;;;;;;;;;;;;;;;;;;AA6BL,SAAgB,cACd,QACA,aAC2B;CAC3B,MAAM,QAAQ;AACd,QAAO,OAAO,WAAoB;AAChC,MAAI;GACF,MAAM,SAAS,MAAM,MAAM,QAAQ,OAAO;AAC1C,OAAI,OAAO,QAAS,QAAO,EAAE;AAC7B,UAAO,eAAwB,uBAAuB,OAAO,UAAU,EAAE,CAAC,CAAC;WACpE,KAAK;AACZ,UAAO,EACL,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,EACrD;;;;;;;;;;;;;;;;;;;AAoBP,SAAgB,aAAgB,QAAiB,aAAgD;CAC/F,MAAM,QAAQ;AACd,QAAO,OAAO,UAAa;AACzB,MAAI;GACF,MAAM,SAAS,MAAM,MAAM,QAAQ,MAAM;AACzC,OAAI,OAAO,QAAS,QAAO;AAC3B,UAAO,OAAO,SAAS,IAAI;WACpB,KAAK;AACZ,UAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI"}
package/lib/zod.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"zod.js","names":[],"sources":["../src/utils.ts","../src/zod.ts"],"sourcesContent":["import type { ValidationError } from '@pyreon/form'\nimport type { ValidationIssue } from './types'\n\n/**\n * Convert an array of validation issues into a flat field → error record.\n * For nested paths like [\"address\", \"city\"], produces \"address.city\".\n * When multiple issues exist for the same path, the first message wins.\n */\nexport function issuesToRecord<TValues extends Record<string, unknown>>(\n issues: ValidationIssue[],\n): Partial<Record<keyof TValues, ValidationError>> {\n const errors = {} as Partial<Record<keyof TValues, ValidationError>>\n for (const issue of issues) {\n const key = issue.path as keyof TValues\n // First error per field wins\n if (errors[key] === undefined) {\n errors[key] = issue.message\n }\n }\n return errors\n}\n","import type {\n SchemaValidateFn,\n ValidateFn,\n ValidationError,\n} from '@pyreon/form'\nimport type { ValidationIssue } from './types'\nimport { issuesToRecord } from './utils'\n\n/**\n * Minimal Zod-compatible interfaces so we don't require zod as a hard dep.\n * These match Zod v3's public API surface.\n */\ninterface ZodIssue {\n path: PropertyKey[]\n message: string\n}\n\n/**\n * Duck-typed Zod schema interface — works with both Zod v3 and v4.\n * Inlines the result shape to avoid version-specific type mismatches.\n */\ninterface ZodSchema<T = unknown> {\n safeParse(data: unknown): {\n success: boolean\n data?: T\n error?: { issues: ZodIssue[] }\n }\n safeParseAsync(\n data: unknown,\n ): Promise<{ success: boolean; data?: T; error?: { issues: ZodIssue[] } }>\n}\n\nfunction zodIssuesToGeneric(issues: ZodIssue[]): ValidationIssue[] {\n return issues.map((issue) => ({\n path: issue.path.map(String).join('.'),\n message: issue.message,\n }))\n}\n\n/**\n * Create a form-level schema validator from a Zod schema.\n * Supports both sync and async Zod schemas (uses `safeParseAsync`).\n *\n * @example\n * import { z } from 'zod'\n * import { zodSchema } from '@pyreon/validation/zod'\n *\n * const schema = z.object({\n * email: z.string().email(),\n * password: z.string().min(8),\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: zodSchema(schema),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function zodSchema<TValues extends Record<string, unknown>>(\n schema: ZodSchema<TValues>,\n): SchemaValidateFn<TValues> {\n return async (values: TValues) => {\n try {\n const result = await schema.safeParseAsync(values)\n if (result.success)\n return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(zodIssuesToGeneric(result.error!.issues))\n } catch (err) {\n return {\n '': err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from a Zod schema.\n * Supports both sync and async Zod refinements.\n *\n * @example\n * import { z } from 'zod'\n * import { zodField } from '@pyreon/validation/zod'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: zodField(z.string().email('Invalid email')),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function zodField<T>(schema: ZodSchema<T>): ValidateFn<T> {\n return async (value: T) => {\n try {\n const result = await schema.safeParseAsync(value)\n if (result.success) return undefined\n return result.error!.issues[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n"],"mappings":";;;;;;AAQA,SAAgB,eACd,QACiD;CACjD,MAAM,SAAS,EAAE;AACjB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,MAAM;AAElB,MAAI,OAAO,SAAS,OAClB,QAAO,OAAO,MAAM;;AAGxB,QAAO;;;;;ACaT,SAAS,mBAAmB,QAAuC;AACjE,QAAO,OAAO,KAAK,WAAW;EAC5B,MAAM,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI;EACtC,SAAS,MAAM;EAChB,EAAE;;;;;;;;;;;;;;;;;;;;;AAsBL,SAAgB,UACd,QAC2B;AAC3B,QAAO,OAAO,WAAoB;AAChC,MAAI;GACF,MAAM,SAAS,MAAM,OAAO,eAAe,OAAO;AAClD,OAAI,OAAO,QACT,QAAO,EAAE;AACX,UAAO,eAAwB,mBAAmB,OAAO,MAAO,OAAO,CAAC;WACjE,KAAK;AACZ,UAAO,EACL,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,EACrD;;;;;;;;;;;;;;;;;;;;AAqBP,SAAgB,SAAY,QAAqC;AAC/D,QAAO,OAAO,UAAa;AACzB,MAAI;GACF,MAAM,SAAS,MAAM,OAAO,eAAe,MAAM;AACjD,OAAI,OAAO,QAAS,QAAO;AAC3B,UAAO,OAAO,MAAO,OAAO,IAAI;WACzB,KAAK;AACZ,UAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI"}
1
+ {"version":3,"file":"zod.js","names":[],"sources":["../src/utils.ts","../src/zod.ts"],"sourcesContent":["import type { ValidationError } from \"@pyreon/form\"\nimport type { ValidationIssue } from \"./types\"\n\n/**\n * Convert an array of validation issues into a flat field → error record.\n * For nested paths like [\"address\", \"city\"], produces \"address.city\".\n * When multiple issues exist for the same path, the first message wins.\n */\nexport function issuesToRecord<TValues extends Record<string, unknown>>(\n issues: ValidationIssue[],\n): Partial<Record<keyof TValues, ValidationError>> {\n const errors = {} as Partial<Record<keyof TValues, ValidationError>>\n for (const issue of issues) {\n const key = issue.path as keyof TValues\n // First error per field wins\n if (errors[key] === undefined) {\n errors[key] = issue.message\n }\n }\n return errors\n}\n","import type { SchemaValidateFn, ValidateFn, ValidationError } from \"@pyreon/form\"\nimport type { ValidationIssue } from \"./types\"\nimport { issuesToRecord } from \"./utils\"\n\n/**\n * Minimal Zod-compatible interfaces so we don't require zod as a hard dep.\n * These match Zod v3's public API surface.\n */\ninterface ZodIssue {\n path: PropertyKey[]\n message: string\n}\n\n/**\n * Duck-typed Zod schema interface — works with both Zod v3 and v4.\n * Inlines the result shape to avoid version-specific type mismatches.\n */\ninterface ZodSchema<T = unknown> {\n safeParse(data: unknown): {\n success: boolean\n data?: T\n error?: { issues: ZodIssue[] }\n }\n safeParseAsync(\n data: unknown,\n ): Promise<{ success: boolean; data?: T; error?: { issues: ZodIssue[] } }>\n}\n\nfunction zodIssuesToGeneric(issues: ZodIssue[]): ValidationIssue[] {\n return issues.map((issue) => ({\n path: issue.path.map(String).join(\".\"),\n message: issue.message,\n }))\n}\n\n/**\n * Create a form-level schema validator from a Zod schema.\n * Supports both sync and async Zod schemas (uses `safeParseAsync`).\n *\n * @example\n * import { z } from 'zod'\n * import { zodSchema } from '@pyreon/validation/zod'\n *\n * const schema = z.object({\n * email: z.string().email(),\n * password: z.string().min(8),\n * })\n *\n * const form = useForm({\n * initialValues: { email: '', password: '' },\n * schema: zodSchema(schema),\n * onSubmit: (values) => { ... },\n * })\n */\nexport function zodSchema<TValues extends Record<string, unknown>>(\n schema: ZodSchema<TValues>,\n): SchemaValidateFn<TValues> {\n return async (values: TValues) => {\n try {\n const result = await schema.safeParseAsync(values)\n if (result.success) return {} as Partial<Record<keyof TValues, ValidationError>>\n return issuesToRecord<TValues>(zodIssuesToGeneric(result.error!.issues))\n } catch (err) {\n return {\n \"\": err instanceof Error ? err.message : String(err),\n } as Partial<Record<keyof TValues, ValidationError>>\n }\n }\n}\n\n/**\n * Create a single-field validator from a Zod schema.\n * Supports both sync and async Zod refinements.\n *\n * @example\n * import { z } from 'zod'\n * import { zodField } from '@pyreon/validation/zod'\n *\n * const form = useForm({\n * initialValues: { email: '' },\n * validators: {\n * email: zodField(z.string().email('Invalid email')),\n * },\n * onSubmit: (values) => { ... },\n * })\n */\nexport function zodField<T>(schema: ZodSchema<T>): ValidateFn<T> {\n return async (value: T) => {\n try {\n const result = await schema.safeParseAsync(value)\n if (result.success) return undefined\n return result.error!.issues[0]?.message\n } catch (err) {\n return err instanceof Error ? err.message : String(err)\n }\n }\n}\n"],"mappings":";;;;;;AAQA,SAAgB,eACd,QACiD;CACjD,MAAM,SAAS,EAAE;AACjB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,MAAM,MAAM;AAElB,MAAI,OAAO,SAAS,OAClB,QAAO,OAAO,MAAM;;AAGxB,QAAO;;;;;ACST,SAAS,mBAAmB,QAAuC;AACjE,QAAO,OAAO,KAAK,WAAW;EAC5B,MAAM,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,IAAI;EACtC,SAAS,MAAM;EAChB,EAAE;;;;;;;;;;;;;;;;;;;;;AAsBL,SAAgB,UACd,QAC2B;AAC3B,QAAO,OAAO,WAAoB;AAChC,MAAI;GACF,MAAM,SAAS,MAAM,OAAO,eAAe,OAAO;AAClD,OAAI,OAAO,QAAS,QAAO,EAAE;AAC7B,UAAO,eAAwB,mBAAmB,OAAO,MAAO,OAAO,CAAC;WACjE,KAAK;AACZ,UAAO,EACL,IAAI,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,EACrD;;;;;;;;;;;;;;;;;;;;AAqBP,SAAgB,SAAY,QAAqC;AAC/D,QAAO,OAAO,UAAa;AACzB,MAAI;GACF,MAAM,SAAS,MAAM,OAAO,eAAe,MAAM;AACjD,OAAI,OAAO,QAAS,QAAO;AAC3B,UAAO,OAAO,MAAO,OAAO,IAAI;WACzB,KAAK;AACZ,UAAO,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI"}
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@pyreon/validation",
3
- "version": "0.10.0",
3
+ "version": "0.11.1",
4
4
  "description": "Schema validation adapters for Pyreon forms (Zod, Valibot, ArkType)",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/pyreon/fundamentals.git",
9
- "directory": "packages/validation"
8
+ "url": "https://github.com/pyreon/pyreon.git",
9
+ "directory": "packages/fundamentals/validation"
10
10
  },
11
11
  "homepage": "https://github.com/pyreon/fundamentals/tree/main/packages/validation#readme",
12
12
  "bugs": {
13
- "url": "https://github.com/pyreon/fundamentals/issues"
13
+ "url": "https://github.com/pyreon/pyreon/issues"
14
14
  },
15
15
  "publishConfig": {
16
16
  "access": "public"
@@ -52,10 +52,11 @@
52
52
  "build": "vl_rolldown_build",
53
53
  "dev": "vl_rolldown_build-watch",
54
54
  "test": "vitest run",
55
- "typecheck": "tsc --noEmit"
55
+ "typecheck": "tsc --noEmit",
56
+ "lint": "biome check ."
56
57
  },
57
58
  "peerDependencies": {
58
- "@pyreon/form": "^0.10.0"
59
+ "@pyreon/form": "^0.11.1"
59
60
  },
60
61
  "peerDependenciesMeta": {
61
62
  "zod": {
@@ -67,5 +68,15 @@
67
68
  "arktype": {
68
69
  "optional": true
69
70
  }
71
+ },
72
+ "devDependencies": {
73
+ "@happy-dom/global-registrator": "^20.8.3",
74
+ "@pyreon/core": "^0.11.1",
75
+ "@pyreon/form": "^0.11.1",
76
+ "@pyreon/reactivity": "^0.11.1",
77
+ "@pyreon/runtime-dom": "^0.11.1",
78
+ "zod": "^4.3.6",
79
+ "valibot": "^1.2.0",
80
+ "arktype": "^2.2.0"
70
81
  }
71
82
  }
package/src/arktype.ts CHANGED
@@ -1,10 +1,6 @@
1
- import type {
2
- SchemaValidateFn,
3
- ValidateFn,
4
- ValidationError,
5
- } from '@pyreon/form'
6
- import type { ValidationIssue } from './types'
7
- import { issuesToRecord } from './utils'
1
+ import type { SchemaValidateFn, ValidateFn, ValidationError } from "@pyreon/form"
2
+ import type { ValidationIssue } from "./types"
3
+ import { issuesToRecord } from "./utils"
8
4
 
9
5
  /**
10
6
  * Minimal ArkType-compatible interfaces so we don't require arktype as a hard dep.
@@ -25,12 +21,12 @@ interface ArkErrors extends Array<ArkError> {
25
21
  type ArkTypeCallable = (data: unknown) => unknown
26
22
 
27
23
  function isArkErrors(result: unknown): result is ArkErrors {
28
- return Array.isArray(result) && 'summary' in (result as object)
24
+ return Array.isArray(result) && "summary" in (result as object)
29
25
  }
30
26
 
31
27
  function arkIssuesToGeneric(errors: ArkErrors): ValidationIssue[] {
32
28
  return errors.map((err) => ({
33
- path: err.path.map(String).join('.'),
29
+ path: err.path.map(String).join("."),
34
30
  message: err.message,
35
31
  }))
36
32
  }
@@ -62,12 +58,11 @@ export function arktypeSchema<TValues extends Record<string, unknown>>(
62
58
  return (values: TValues) => {
63
59
  try {
64
60
  const result = schema(values)
65
- if (!isArkErrors(result))
66
- return {} as Partial<Record<keyof TValues, ValidationError>>
61
+ if (!isArkErrors(result)) return {} as Partial<Record<keyof TValues, ValidationError>>
67
62
  return issuesToRecord<TValues>(arkIssuesToGeneric(result))
68
63
  } catch (err) {
69
64
  return {
70
- '': err instanceof Error ? err.message : String(err),
65
+ "": err instanceof Error ? err.message : String(err),
71
66
  } as Partial<Record<keyof TValues, ValidationError>>
72
67
  }
73
68
  }
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { arktypeField, arktypeSchema } from './arktype'
1
+ export { arktypeField, arktypeSchema } from "./arktype"
2
2
  export type {
3
3
  FieldAdapter,
4
4
  SchemaAdapter,
@@ -6,7 +6,7 @@ export type {
6
6
  ValidateFn,
7
7
  ValidationError,
8
8
  ValidationIssue,
9
- } from './types'
10
- export { issuesToRecord } from './utils'
11
- export { valibotField, valibotSchema } from './valibot'
12
- export { zodField, zodSchema } from './zod'
9
+ } from "./types"
10
+ export { issuesToRecord } from "./utils"
11
+ export { valibotField, valibotSchema } from "./valibot"
12
+ export { zodField, zodSchema } from "./zod"
@@ -1 +1 @@
1
- import '@happy-dom/global-registrator'
1
+ import "@happy-dom/global-registrator"
@@ -1,12 +1,12 @@
1
- import { useForm } from '@pyreon/form'
2
- import { mount } from '@pyreon/runtime-dom'
3
- import { type } from 'arktype'
4
- import * as v from 'valibot'
5
- import { z } from 'zod'
6
- import { arktypeField, arktypeSchema } from '../arktype'
7
- import { issuesToRecord } from '../utils'
8
- import { valibotField, valibotSchema } from '../valibot'
9
- import { zodField, zodSchema } from '../zod'
1
+ import { useForm } from "@pyreon/form"
2
+ import { mount } from "@pyreon/runtime-dom"
3
+ import { type } from "arktype"
4
+ import * as v from "valibot"
5
+ import { z } from "zod"
6
+ import { arktypeField, arktypeSchema } from "../arktype"
7
+ import { issuesToRecord } from "../utils"
8
+ import { valibotField, valibotSchema } from "../valibot"
9
+ import { zodField, zodSchema } from "../zod"
10
10
 
11
11
  // ─── Helpers ──────────────────────────────────────────────────────────────────
12
12
 
@@ -17,7 +17,7 @@ function Capture<T>({ fn }: { fn: () => T }) {
17
17
 
18
18
  function mountWith<T>(fn: () => T): { result: T; unmount: () => void } {
19
19
  let result: T | undefined
20
- const el = document.createElement('div')
20
+ const el = document.createElement("div")
21
21
  document.body.appendChild(el)
22
22
  const unmount = mount(
23
23
  <Capture
@@ -38,85 +38,85 @@ function mountWith<T>(fn: () => T): { result: T; unmount: () => void } {
38
38
 
39
39
  // ─── issuesToRecord ──────────────────────────────────────────────────────────
40
40
 
41
- describe('issuesToRecord', () => {
42
- it('converts issues to a flat record', () => {
41
+ describe("issuesToRecord", () => {
42
+ it("converts issues to a flat record", () => {
43
43
  const result = issuesToRecord([
44
- { path: 'email', message: 'Required' },
45
- { path: 'password', message: 'Too short' },
44
+ { path: "email", message: "Required" },
45
+ { path: "password", message: "Too short" },
46
46
  ])
47
- expect(result).toEqual({ email: 'Required', password: 'Too short' })
47
+ expect(result).toEqual({ email: "Required", password: "Too short" })
48
48
  })
49
49
 
50
- it('first error per field wins', () => {
50
+ it("first error per field wins", () => {
51
51
  const result = issuesToRecord([
52
- { path: 'email', message: 'Required' },
53
- { path: 'email', message: 'Invalid format' },
52
+ { path: "email", message: "Required" },
53
+ { path: "email", message: "Invalid format" },
54
54
  ])
55
- expect(result).toEqual({ email: 'Required' })
55
+ expect(result).toEqual({ email: "Required" })
56
56
  })
57
57
 
58
- it('returns empty object for no issues', () => {
58
+ it("returns empty object for no issues", () => {
59
59
  expect(issuesToRecord([])).toEqual({})
60
60
  })
61
61
  })
62
62
 
63
63
  // ─── Zod Adapter ─────────────────────────────────────────────────────────────
64
64
 
65
- describe('zodSchema', () => {
65
+ describe("zodSchema", () => {
66
66
  const schema = z.object({
67
- email: z.string().email('Invalid email'),
68
- password: z.string().min(8, 'Min 8 chars'),
67
+ email: z.string().email("Invalid email"),
68
+ password: z.string().min(8, "Min 8 chars"),
69
69
  })
70
70
 
71
- it('returns empty record for valid data', async () => {
71
+ it("returns empty record for valid data", async () => {
72
72
  const validate = zodSchema(schema)
73
- const result = await validate({ email: 'a@b.com', password: '12345678' })
73
+ const result = await validate({ email: "a@b.com", password: "12345678" })
74
74
  expect(result).toEqual({})
75
75
  })
76
76
 
77
- it('returns field errors for invalid data', async () => {
77
+ it("returns field errors for invalid data", async () => {
78
78
  const validate = zodSchema(schema)
79
- const result = await validate({ email: 'bad', password: 'short' })
80
- expect(result.email).toBe('Invalid email')
81
- expect(result.password).toBe('Min 8 chars')
79
+ const result = await validate({ email: "bad", password: "short" })
80
+ expect(result.email).toBe("Invalid email")
81
+ expect(result.password).toBe("Min 8 chars")
82
82
  })
83
83
 
84
- it('returns error for single invalid field', async () => {
84
+ it("returns error for single invalid field", async () => {
85
85
  const validate = zodSchema(schema)
86
- const result = await validate({ email: 'a@b.com', password: 'short' })
86
+ const result = await validate({ email: "a@b.com", password: "short" })
87
87
  expect(result.email).toBeUndefined()
88
- expect(result.password).toBe('Min 8 chars')
88
+ expect(result.password).toBe("Min 8 chars")
89
89
  })
90
90
  })
91
91
 
92
- describe('zodField', () => {
93
- it('returns undefined for valid value', async () => {
94
- const validate = zodField(z.string().email('Invalid email'))
95
- expect(await validate('a@b.com', {})).toBeUndefined()
92
+ describe("zodField", () => {
93
+ it("returns undefined for valid value", async () => {
94
+ const validate = zodField(z.string().email("Invalid email"))
95
+ expect(await validate("a@b.com", {})).toBeUndefined()
96
96
  })
97
97
 
98
- it('returns error message for invalid value', async () => {
99
- const validate = zodField(z.string().email('Invalid email'))
100
- expect(await validate('bad', {})).toBe('Invalid email')
98
+ it("returns error message for invalid value", async () => {
99
+ const validate = zodField(z.string().email("Invalid email"))
100
+ expect(await validate("bad", {})).toBe("Invalid email")
101
101
  })
102
102
 
103
- it('works with number schemas', async () => {
104
- const validate = zodField(z.number().min(0, 'Must be positive'))
105
- expect(await validate(-1, {})).toBe('Must be positive')
103
+ it("works with number schemas", async () => {
104
+ const validate = zodField(z.number().min(0, "Must be positive"))
105
+ expect(await validate(-1, {})).toBe("Must be positive")
106
106
  expect(await validate(5, {})).toBeUndefined()
107
107
  })
108
108
  })
109
109
 
110
- describe('zod + useForm integration', () => {
111
- it('validates form with zod schema', async () => {
110
+ describe("zod + useForm integration", () => {
111
+ it("validates form with zod schema", async () => {
112
112
  const schema = z.object({
113
- email: z.string().email('Invalid email'),
114
- password: z.string().min(8, 'Min 8 chars'),
113
+ email: z.string().email("Invalid email"),
114
+ password: z.string().min(8, "Min 8 chars"),
115
115
  })
116
116
 
117
117
  const { result: form, unmount } = mountWith(() =>
118
118
  useForm({
119
- initialValues: { email: '', password: '' },
119
+ initialValues: { email: "", password: "" },
120
120
  schema: zodSchema(schema),
121
121
  onSubmit: () => {
122
122
  /* noop */
@@ -126,18 +126,18 @@ describe('zod + useForm integration', () => {
126
126
 
127
127
  const valid = await form.validate()
128
128
  expect(valid).toBe(false)
129
- expect(form.fields.email.error()).toBe('Invalid email')
130
- expect(form.fields.password.error()).toBe('Min 8 chars')
129
+ expect(form.fields.email.error()).toBe("Invalid email")
130
+ expect(form.fields.password.error()).toBe("Min 8 chars")
131
131
  unmount()
132
132
  })
133
133
 
134
- it('validates with field-level zod validators', async () => {
134
+ it("validates with field-level zod validators", async () => {
135
135
  const { result: form, unmount } = mountWith(() =>
136
136
  useForm({
137
- initialValues: { email: '', age: 0 },
137
+ initialValues: { email: "", age: 0 },
138
138
  validators: {
139
- email: zodField(z.string().email('Invalid')),
140
- age: zodField(z.number().min(18, 'Must be 18+')),
139
+ email: zodField(z.string().email("Invalid")),
140
+ age: zodField(z.number().min(18, "Must be 18+")),
141
141
  },
142
142
  onSubmit: () => {
143
143
  /* noop */
@@ -147,52 +147,52 @@ describe('zod + useForm integration', () => {
147
147
 
148
148
  const valid = await form.validate()
149
149
  expect(valid).toBe(false)
150
- expect(form.fields.email.error()).toBe('Invalid')
151
- expect(form.fields.age.error()).toBe('Must be 18+')
150
+ expect(form.fields.email.error()).toBe("Invalid")
151
+ expect(form.fields.age.error()).toBe("Must be 18+")
152
152
  unmount()
153
153
  })
154
154
  })
155
155
 
156
156
  // ─── Valibot Adapter ─────────────────────────────────────────────────────────
157
157
 
158
- describe('valibotSchema', () => {
158
+ describe("valibotSchema", () => {
159
159
  const schema = v.object({
160
- email: v.pipe(v.string(), v.email('Invalid email')),
161
- password: v.pipe(v.string(), v.minLength(8, 'Min 8 chars')),
160
+ email: v.pipe(v.string(), v.email("Invalid email")),
161
+ password: v.pipe(v.string(), v.minLength(8, "Min 8 chars")),
162
162
  })
163
163
 
164
- it('returns empty record for valid data', async () => {
164
+ it("returns empty record for valid data", async () => {
165
165
  const validate = valibotSchema(schema, v.safeParseAsync)
166
- const result = await validate({ email: 'a@b.com', password: '12345678' })
166
+ const result = await validate({ email: "a@b.com", password: "12345678" })
167
167
  expect(result).toEqual({})
168
168
  })
169
169
 
170
- it('returns field errors for invalid data', async () => {
170
+ it("returns field errors for invalid data", async () => {
171
171
  const validate = valibotSchema(schema, v.safeParseAsync)
172
- const result = await validate({ email: 'bad', password: 'short' })
173
- expect(result.email).toBe('Invalid email')
174
- expect(result.password).toBe('Min 8 chars')
172
+ const result = await validate({ email: "bad", password: "short" })
173
+ expect(result.email).toBe("Invalid email")
174
+ expect(result.password).toBe("Min 8 chars")
175
175
  })
176
176
 
177
- it('works with sync safeParse', async () => {
177
+ it("works with sync safeParse", async () => {
178
178
  const validate = valibotSchema(schema, v.safeParse)
179
- const result = await validate({ email: 'bad', password: 'short' })
180
- expect(result.email).toBe('Invalid email')
179
+ const result = await validate({ email: "bad", password: "short" })
180
+ expect(result.email).toBe("Invalid email")
181
181
  })
182
182
 
183
- it('handles issues without path', async () => {
183
+ it("handles issues without path", async () => {
184
184
  // Simulate a safeParse function that returns issues without path
185
185
  const mockSafeParse = async () => ({
186
186
  success: false as const,
187
- issues: [{ message: 'Schema-level error' }],
187
+ issues: [{ message: "Schema-level error" }],
188
188
  })
189
189
  const validate = valibotSchema({}, mockSafeParse)
190
190
  const result = await validate({})
191
191
  // Issue without path maps to empty string key
192
- expect(result['' as keyof typeof result]).toBe('Schema-level error')
192
+ expect(result["" as keyof typeof result]).toBe("Schema-level error")
193
193
  })
194
194
 
195
- it('handles result with undefined issues array', async () => {
195
+ it("handles result with undefined issues array", async () => {
196
196
  const mockSafeParse = async () => ({
197
197
  success: false as const,
198
198
  // issues is undefined
@@ -203,42 +203,36 @@ describe('valibotSchema', () => {
203
203
  })
204
204
  })
205
205
 
206
- describe('valibotField', () => {
207
- it('returns undefined for valid value', async () => {
208
- const validate = valibotField(
209
- v.pipe(v.string(), v.email('Invalid email')),
210
- v.safeParseAsync,
211
- )
212
- expect(await validate('a@b.com', {})).toBeUndefined()
206
+ describe("valibotField", () => {
207
+ it("returns undefined for valid value", async () => {
208
+ const validate = valibotField(v.pipe(v.string(), v.email("Invalid email")), v.safeParseAsync)
209
+ expect(await validate("a@b.com", {})).toBeUndefined()
213
210
  })
214
211
 
215
- it('returns error message for invalid value', async () => {
216
- const validate = valibotField(
217
- v.pipe(v.string(), v.email('Invalid email')),
218
- v.safeParseAsync,
219
- )
220
- expect(await validate('bad', {})).toBe('Invalid email')
212
+ it("returns error message for invalid value", async () => {
213
+ const validate = valibotField(v.pipe(v.string(), v.email("Invalid email")), v.safeParseAsync)
214
+ expect(await validate("bad", {})).toBe("Invalid email")
221
215
  })
222
216
 
223
- it('handles result with undefined issues', async () => {
217
+ it("handles result with undefined issues", async () => {
224
218
  const mockSafeParse = async () => ({
225
219
  success: false as const,
226
220
  })
227
221
  const validate = valibotField({}, mockSafeParse)
228
- expect(await validate('x', {})).toBeUndefined()
222
+ expect(await validate("x", {})).toBeUndefined()
229
223
  })
230
224
  })
231
225
 
232
- describe('valibot + useForm integration', () => {
233
- it('validates form with valibot schema', async () => {
226
+ describe("valibot + useForm integration", () => {
227
+ it("validates form with valibot schema", async () => {
234
228
  const schema = v.object({
235
- email: v.pipe(v.string(), v.email('Invalid email')),
236
- password: v.pipe(v.string(), v.minLength(8, 'Min 8 chars')),
229
+ email: v.pipe(v.string(), v.email("Invalid email")),
230
+ password: v.pipe(v.string(), v.minLength(8, "Min 8 chars")),
237
231
  })
238
232
 
239
233
  const { result: form, unmount } = mountWith(() =>
240
234
  useForm({
241
- initialValues: { email: '', password: '' },
235
+ initialValues: { email: "", password: "" },
242
236
  schema: valibotSchema(schema, v.safeParseAsync),
243
237
  onSubmit: () => {
244
238
  /* noop */
@@ -248,198 +242,198 @@ describe('valibot + useForm integration', () => {
248
242
 
249
243
  const valid = await form.validate()
250
244
  expect(valid).toBe(false)
251
- expect(form.fields.email.error()).toBe('Invalid email')
252
- expect(form.fields.password.error()).toBe('Min 8 chars')
245
+ expect(form.fields.email.error()).toBe("Invalid email")
246
+ expect(form.fields.password.error()).toBe("Min 8 chars")
253
247
  unmount()
254
248
  })
255
249
  })
256
250
 
257
251
  // ─── ArkType Adapter ─────────────────────────────────────────────────────────
258
252
 
259
- describe('arktypeSchema', () => {
253
+ describe("arktypeSchema", () => {
260
254
  const schema = type({
261
- email: 'string.email',
262
- password: 'string >= 8',
255
+ email: "string.email",
256
+ password: "string >= 8",
263
257
  })
264
258
 
265
- it('returns empty record for valid data', async () => {
259
+ it("returns empty record for valid data", async () => {
266
260
  const validate = arktypeSchema(schema)
267
- const result = await validate({ email: 'a@b.com', password: '12345678' })
261
+ const result = await validate({ email: "a@b.com", password: "12345678" })
268
262
  expect(result).toEqual({})
269
263
  })
270
264
 
271
- it('returns field errors for invalid data', async () => {
265
+ it("returns field errors for invalid data", async () => {
272
266
  const validate = arktypeSchema(schema)
273
- const result = await validate({ email: 'bad', password: 'short' })
267
+ const result = await validate({ email: "bad", password: "short" })
274
268
  expect(result.email).toBeDefined()
275
269
  expect(result.password).toBeDefined()
276
270
  })
277
271
  })
278
272
 
279
- describe('arktypeField', () => {
280
- it('returns undefined for valid value', async () => {
281
- const validate = arktypeField(type('string.email'))
282
- expect(await validate('a@b.com', {})).toBeUndefined()
273
+ describe("arktypeField", () => {
274
+ it("returns undefined for valid value", async () => {
275
+ const validate = arktypeField(type("string.email"))
276
+ expect(await validate("a@b.com", {})).toBeUndefined()
283
277
  })
284
278
 
285
- it('returns error message for invalid value', async () => {
286
- const validate = arktypeField(type('string.email'))
287
- const result = await validate('bad', {})
279
+ it("returns error message for invalid value", async () => {
280
+ const validate = arktypeField(type("string.email"))
281
+ const result = await validate("bad", {})
288
282
  expect(result).toBeDefined()
289
- expect(typeof result).toBe('string')
283
+ expect(typeof result).toBe("string")
290
284
  })
291
285
  })
292
286
 
293
- describe('zodSchema catch branch', () => {
294
- it('captures Error when safeParseAsync throws an Error', async () => {
287
+ describe("zodSchema catch branch", () => {
288
+ it("captures Error when safeParseAsync throws an Error", async () => {
295
289
  const throwingSchema = {
296
290
  safeParseAsync: () => {
297
- throw new Error('Zod schema exploded')
291
+ throw new Error("Zod schema exploded")
298
292
  },
299
293
  safeParse: () => {
300
- throw new Error('Zod schema exploded')
294
+ throw new Error("Zod schema exploded")
301
295
  },
302
296
  }
303
297
  const validate = zodSchema(throwingSchema as any)
304
- const result = await validate({ email: '', password: '' })
305
- expect(result['' as keyof typeof result]).toBe('Zod schema exploded')
298
+ const result = await validate({ email: "", password: "" })
299
+ expect(result["" as keyof typeof result]).toBe("Zod schema exploded")
306
300
  })
307
301
 
308
- it('captures non-Error when safeParseAsync throws a string', async () => {
302
+ it("captures non-Error when safeParseAsync throws a string", async () => {
309
303
  const throwingSchema = {
310
304
  safeParseAsync: () => {
311
- throw 'raw string error'
305
+ throw "raw string error"
312
306
  },
313
307
  safeParse: () => {
314
- throw 'raw string error'
308
+ throw "raw string error"
315
309
  },
316
310
  }
317
311
  const validate = zodSchema(throwingSchema as any)
318
- const result = await validate({ email: '', password: '' })
319
- expect(result['' as keyof typeof result]).toBe('raw string error')
312
+ const result = await validate({ email: "", password: "" })
313
+ expect(result["" as keyof typeof result]).toBe("raw string error")
320
314
  })
321
315
  })
322
316
 
323
- describe('zodField catch branch', () => {
324
- it('captures Error when safeParseAsync throws an Error', async () => {
317
+ describe("zodField catch branch", () => {
318
+ it("captures Error when safeParseAsync throws an Error", async () => {
325
319
  const throwingSchema = {
326
320
  safeParseAsync: () => {
327
- throw new Error('Zod field exploded')
321
+ throw new Error("Zod field exploded")
328
322
  },
329
323
  safeParse: () => {
330
- throw new Error('Zod field exploded')
324
+ throw new Error("Zod field exploded")
331
325
  },
332
326
  }
333
327
  const validate = zodField(throwingSchema as any)
334
- const result = await validate('test', {})
335
- expect(result).toBe('Zod field exploded')
328
+ const result = await validate("test", {})
329
+ expect(result).toBe("Zod field exploded")
336
330
  })
337
331
 
338
- it('captures non-Error when safeParseAsync throws a string', async () => {
332
+ it("captures non-Error when safeParseAsync throws a string", async () => {
339
333
  const throwingSchema = {
340
334
  safeParseAsync: () => {
341
- throw 'raw zod field error'
335
+ throw "raw zod field error"
342
336
  },
343
337
  safeParse: () => {
344
- throw 'raw zod field error'
338
+ throw "raw zod field error"
345
339
  },
346
340
  }
347
341
  const validate = zodField(throwingSchema as any)
348
- const result = await validate('test', {})
349
- expect(result).toBe('raw zod field error')
342
+ const result = await validate("test", {})
343
+ expect(result).toBe("raw zod field error")
350
344
  })
351
345
  })
352
346
 
353
- describe('valibotSchema catch branch', () => {
354
- it('captures Error when safeParse function throws an Error', async () => {
347
+ describe("valibotSchema catch branch", () => {
348
+ it("captures Error when safeParse function throws an Error", async () => {
355
349
  const throwingParse = () => {
356
- throw new Error('Valibot schema exploded')
350
+ throw new Error("Valibot schema exploded")
357
351
  }
358
352
  const validate = valibotSchema({}, throwingParse)
359
- const result = await validate({ email: '', password: '' })
360
- expect(result['' as keyof typeof result]).toBe('Valibot schema exploded')
353
+ const result = await validate({ email: "", password: "" })
354
+ expect(result["" as keyof typeof result]).toBe("Valibot schema exploded")
361
355
  })
362
356
 
363
- it('captures non-Error when safeParse function throws a string', async () => {
357
+ it("captures non-Error when safeParse function throws a string", async () => {
364
358
  const throwingParse = () => {
365
- throw 'raw valibot schema error'
359
+ throw "raw valibot schema error"
366
360
  }
367
361
  const validate = valibotSchema({}, throwingParse)
368
- const result = await validate({ email: '', password: '' })
369
- expect(result['' as keyof typeof result]).toBe('raw valibot schema error')
362
+ const result = await validate({ email: "", password: "" })
363
+ expect(result["" as keyof typeof result]).toBe("raw valibot schema error")
370
364
  })
371
365
  })
372
366
 
373
- describe('valibotField catch branch', () => {
374
- it('captures Error when safeParse function throws an Error', async () => {
367
+ describe("valibotField catch branch", () => {
368
+ it("captures Error when safeParse function throws an Error", async () => {
375
369
  const throwingParse = () => {
376
- throw new Error('Valibot field exploded')
370
+ throw new Error("Valibot field exploded")
377
371
  }
378
372
  const validate = valibotField({}, throwingParse)
379
- const result = await validate('test', {})
380
- expect(result).toBe('Valibot field exploded')
373
+ const result = await validate("test", {})
374
+ expect(result).toBe("Valibot field exploded")
381
375
  })
382
376
 
383
- it('captures non-Error when safeParse function throws a string', async () => {
377
+ it("captures non-Error when safeParse function throws a string", async () => {
384
378
  const throwingParse = () => {
385
- throw 'raw valibot field error'
379
+ throw "raw valibot field error"
386
380
  }
387
381
  const validate = valibotField({}, throwingParse)
388
- const result = await validate('test', {})
389
- expect(result).toBe('raw valibot field error')
382
+ const result = await validate("test", {})
383
+ expect(result).toBe("raw valibot field error")
390
384
  })
391
385
  })
392
386
 
393
- describe('arktypeSchema catch branch', () => {
394
- it('captures Error when schema function throws an Error', async () => {
387
+ describe("arktypeSchema catch branch", () => {
388
+ it("captures Error when schema function throws an Error", async () => {
395
389
  const throwingSchema = () => {
396
- throw new Error('ArkType schema exploded')
390
+ throw new Error("ArkType schema exploded")
397
391
  }
398
392
  const validate = arktypeSchema(throwingSchema)
399
- const result = await validate({ email: '', password: '' })
400
- expect(result['' as keyof typeof result]).toBe('ArkType schema exploded')
393
+ const result = await validate({ email: "", password: "" })
394
+ expect(result["" as keyof typeof result]).toBe("ArkType schema exploded")
401
395
  })
402
396
 
403
- it('captures non-Error when schema function throws a string', async () => {
397
+ it("captures non-Error when schema function throws a string", async () => {
404
398
  const throwingSchema = () => {
405
- throw 'raw arktype schema error'
399
+ throw "raw arktype schema error"
406
400
  }
407
401
  const validate = arktypeSchema(throwingSchema)
408
- const result = await validate({ email: '', password: '' })
409
- expect(result['' as keyof typeof result]).toBe('raw arktype schema error')
402
+ const result = await validate({ email: "", password: "" })
403
+ expect(result["" as keyof typeof result]).toBe("raw arktype schema error")
410
404
  })
411
405
  })
412
406
 
413
- describe('arktypeField catch branch', () => {
414
- it('captures Error when schema function throws an Error', async () => {
407
+ describe("arktypeField catch branch", () => {
408
+ it("captures Error when schema function throws an Error", async () => {
415
409
  const throwingSchema = () => {
416
- throw new Error('ArkType field exploded')
410
+ throw new Error("ArkType field exploded")
417
411
  }
418
412
  const validate = arktypeField(throwingSchema)
419
- const result = await validate('test', {})
420
- expect(result).toBe('ArkType field exploded')
413
+ const result = await validate("test", {})
414
+ expect(result).toBe("ArkType field exploded")
421
415
  })
422
416
 
423
- it('captures non-Error when schema function throws a string', async () => {
417
+ it("captures non-Error when schema function throws a string", async () => {
424
418
  const throwingSchema = () => {
425
- throw 'raw arktype field error'
419
+ throw "raw arktype field error"
426
420
  }
427
421
  const validate = arktypeField(throwingSchema)
428
- const result = await validate('test', {})
429
- expect(result).toBe('raw arktype field error')
422
+ const result = await validate("test", {})
423
+ expect(result).toBe("raw arktype field error")
430
424
  })
431
425
  })
432
426
 
433
- describe('arktype + useForm integration', () => {
434
- it('validates form with arktype schema', async () => {
427
+ describe("arktype + useForm integration", () => {
428
+ it("validates form with arktype schema", async () => {
435
429
  const schema = type({
436
- email: 'string.email',
437
- password: 'string >= 8',
430
+ email: "string.email",
431
+ password: "string >= 8",
438
432
  })
439
433
 
440
434
  const { result: form, unmount } = mountWith(() =>
441
435
  useForm({
442
- initialValues: { email: '', password: '' },
436
+ initialValues: { email: "", password: "" },
443
437
  schema: arktypeSchema(schema),
444
438
  onSubmit: () => {
445
439
  /* noop */
package/src/types.ts CHANGED
@@ -1,8 +1,4 @@
1
- import type {
2
- SchemaValidateFn,
3
- ValidateFn,
4
- ValidationError,
5
- } from '@pyreon/form'
1
+ import type { SchemaValidateFn, ValidateFn, ValidationError } from "@pyreon/form"
6
2
 
7
3
  /** Re-export form types for convenience. */
8
4
  export type { SchemaValidateFn, ValidateFn, ValidationError }
package/src/utils.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { ValidationError } from '@pyreon/form'
2
- import type { ValidationIssue } from './types'
1
+ import type { ValidationError } from "@pyreon/form"
2
+ import type { ValidationIssue } from "./types"
3
3
 
4
4
  /**
5
5
  * Convert an array of validation issues into a flat field → error record.
package/src/valibot.ts CHANGED
@@ -1,10 +1,6 @@
1
- import type {
2
- SchemaValidateFn,
3
- ValidateFn,
4
- ValidationError,
5
- } from '@pyreon/form'
6
- import type { ValidationIssue } from './types'
7
- import { issuesToRecord } from './utils'
1
+ import type { SchemaValidateFn, ValidateFn, ValidationError } from "@pyreon/form"
2
+ import type { ValidationIssue } from "./types"
3
+ import { issuesToRecord } from "./utils"
8
4
 
9
5
  /**
10
6
  * Minimal Valibot-compatible interfaces so we don't require valibot as a hard dep.
@@ -35,7 +31,7 @@ type GenericSafeParseFn = Function
35
31
 
36
32
  function valibotIssuesToGeneric(issues: ValibotIssue[]): ValidationIssue[] {
37
33
  return issues.map((issue) => ({
38
- path: issue.path?.map((p) => String(p.key)).join('.') ?? '',
34
+ path: issue.path?.map((p) => String(p.key)).join(".") ?? "",
39
35
  message: issue.message,
40
36
  }))
41
37
  }
@@ -74,14 +70,11 @@ export function valibotSchema<TValues extends Record<string, unknown>>(
74
70
  return async (values: TValues) => {
75
71
  try {
76
72
  const result = await parse(schema, values)
77
- if (result.success)
78
- return {} as Partial<Record<keyof TValues, ValidationError>>
79
- return issuesToRecord<TValues>(
80
- valibotIssuesToGeneric(result.issues ?? []),
81
- )
73
+ if (result.success) return {} as Partial<Record<keyof TValues, ValidationError>>
74
+ return issuesToRecord<TValues>(valibotIssuesToGeneric(result.issues ?? []))
82
75
  } catch (err) {
83
76
  return {
84
- '': err instanceof Error ? err.message : String(err),
77
+ "": err instanceof Error ? err.message : String(err),
85
78
  } as Partial<Record<keyof TValues, ValidationError>>
86
79
  }
87
80
  }
@@ -102,10 +95,7 @@ export function valibotSchema<TValues extends Record<string, unknown>>(
102
95
  * onSubmit: (values) => { ... },
103
96
  * })
104
97
  */
105
- export function valibotField<T>(
106
- schema: unknown,
107
- safeParseFn: GenericSafeParseFn,
108
- ): ValidateFn<T> {
98
+ export function valibotField<T>(schema: unknown, safeParseFn: GenericSafeParseFn): ValidateFn<T> {
109
99
  const parse = safeParseFn as InternalParseFn
110
100
  return async (value: T) => {
111
101
  try {
package/src/zod.ts CHANGED
@@ -1,10 +1,6 @@
1
- import type {
2
- SchemaValidateFn,
3
- ValidateFn,
4
- ValidationError,
5
- } from '@pyreon/form'
6
- import type { ValidationIssue } from './types'
7
- import { issuesToRecord } from './utils'
1
+ import type { SchemaValidateFn, ValidateFn, ValidationError } from "@pyreon/form"
2
+ import type { ValidationIssue } from "./types"
3
+ import { issuesToRecord } from "./utils"
8
4
 
9
5
  /**
10
6
  * Minimal Zod-compatible interfaces so we don't require zod as a hard dep.
@@ -32,7 +28,7 @@ interface ZodSchema<T = unknown> {
32
28
 
33
29
  function zodIssuesToGeneric(issues: ZodIssue[]): ValidationIssue[] {
34
30
  return issues.map((issue) => ({
35
- path: issue.path.map(String).join('.'),
31
+ path: issue.path.map(String).join("."),
36
32
  message: issue.message,
37
33
  }))
38
34
  }
@@ -62,12 +58,11 @@ export function zodSchema<TValues extends Record<string, unknown>>(
62
58
  return async (values: TValues) => {
63
59
  try {
64
60
  const result = await schema.safeParseAsync(values)
65
- if (result.success)
66
- return {} as Partial<Record<keyof TValues, ValidationError>>
61
+ if (result.success) return {} as Partial<Record<keyof TValues, ValidationError>>
67
62
  return issuesToRecord<TValues>(zodIssuesToGeneric(result.error!.issues))
68
63
  } catch (err) {
69
64
  return {
70
- '': err instanceof Error ? err.message : String(err),
65
+ "": err instanceof Error ? err.message : String(err),
71
66
  } as Partial<Record<keyof TValues, ValidationError>>
72
67
  }
73
68
  }