@pyreon/validation 0.11.5 → 0.11.6

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.
package/README.md CHANGED
@@ -11,9 +11,9 @@ bun add @pyreon/validation
11
11
  ## Quick Start
12
12
 
13
13
  ```ts
14
- import { z } from "zod"
15
- import { useForm } from "@pyreon/form"
16
- import { zodSchema } from "@pyreon/validation"
14
+ import { z } from 'zod'
15
+ import { useForm } from '@pyreon/form'
16
+ import { zodSchema } from '@pyreon/validation'
17
17
 
18
18
  const schema = z.object({
19
19
  email: z.string().email(),
@@ -21,7 +21,7 @@ const schema = z.object({
21
21
  })
22
22
 
23
23
  const form = useForm({
24
- initialValues: { email: "", age: 0 },
24
+ initialValues: { email: '', age: 0 },
25
25
  schema: zodSchema(schema),
26
26
  onSubmit: async (values) => console.log(values),
27
27
  })
@@ -35,9 +35,9 @@ Each adapter comes in two flavors: **schema-level** (validates the whole form) a
35
35
 
36
36
  Create a form-level schema validator from a Zod schema. Uses `safeParseAsync` internally — supports both sync and async refinements. Duck-typed to work with Zod v3 and v4.
37
37
 
38
- | Parameter | Type | Description |
39
- | --- | --- | --- |
40
- | `schema` | Zod schema | Any Zod object schema with `safeParse`/`safeParseAsync` |
38
+ | Parameter | Type | Description |
39
+ | --------- | ---------- | ------------------------------------------------------- |
40
+ | `schema` | Zod schema | Any Zod object schema with `safeParse`/`safeParseAsync` |
41
41
 
42
42
  **Returns:** `SchemaValidateFn<TValues>`
43
43
 
@@ -57,9 +57,9 @@ const form = useForm({
57
57
 
58
58
  Create a single-field validator from a Zod schema. Returns the first error message on failure.
59
59
 
60
- | Parameter | Type | Description |
61
- | --- | --- | --- |
62
- | `schema` | Zod schema | Any Zod schema (string, number, etc.) |
60
+ | Parameter | Type | Description |
61
+ | --------- | ---------- | ------------------------------------- |
62
+ | `schema` | Zod schema | Any Zod schema (string, number, etc.) |
63
63
 
64
64
  **Returns:** `ValidateFn<T>`
65
65
 
@@ -77,10 +77,10 @@ const form = useForm({
77
77
 
78
78
  Create a form-level schema validator from a Valibot schema. Valibot uses standalone functions, so you must pass the parse function.
79
79
 
80
- | Parameter | Type | Description |
81
- | --- | --- | --- |
82
- | `schema` | Valibot schema | Any Valibot object schema |
83
- | `safeParseFn` | `Function` | `v.safeParse` or `v.safeParseAsync` from valibot |
80
+ | Parameter | Type | Description |
81
+ | ------------- | -------------- | ------------------------------------------------ |
82
+ | `schema` | Valibot schema | Any Valibot object schema |
83
+ | `safeParseFn` | `Function` | `v.safeParse` or `v.safeParseAsync` from valibot |
84
84
 
85
85
  **Returns:** `SchemaValidateFn<TValues>`
86
86
 
@@ -103,10 +103,10 @@ const form = useForm({
103
103
 
104
104
  Create a single-field validator from a Valibot schema.
105
105
 
106
- | Parameter | Type | Description |
107
- | --- | --- | --- |
108
- | `schema` | Valibot schema | Any Valibot schema |
109
- | `safeParseFn` | `Function` | `v.safeParse` or `v.safeParseAsync` from valibot |
106
+ | Parameter | Type | Description |
107
+ | ------------- | -------------- | ------------------------------------------------ |
108
+ | `schema` | Valibot schema | Any Valibot schema |
109
+ | `safeParseFn` | `Function` | `v.safeParse` or `v.safeParseAsync` from valibot |
110
110
 
111
111
  **Returns:** `ValidateFn<T>`
112
112
 
@@ -120,9 +120,9 @@ validators: {
120
120
 
121
121
  Create a form-level schema validator from an ArkType schema. ArkType validation is synchronous.
122
122
 
123
- | Parameter | Type | Description |
124
- | --- | --- | --- |
125
- | `schema` | ArkType `Type` | Any callable ArkType type |
123
+ | Parameter | Type | Description |
124
+ | --------- | -------------- | ------------------------- |
125
+ | `schema` | ArkType `Type` | Any callable ArkType type |
126
126
 
127
127
  **Returns:** `SchemaValidateFn<TValues>`
128
128
 
@@ -142,9 +142,9 @@ const form = useForm({
142
142
 
143
143
  Create a single-field validator from an ArkType schema.
144
144
 
145
- | Parameter | Type | Description |
146
- | --- | --- | --- |
147
- | `schema` | ArkType `Type` | Any callable ArkType type |
145
+ | Parameter | Type | Description |
146
+ | --------- | -------------- | ------------------------- |
147
+ | `schema` | ArkType `Type` | Any callable ArkType type |
148
148
 
149
149
  **Returns:** `ValidateFn<T>`
150
150
 
@@ -158,17 +158,17 @@ validators: {
158
158
 
159
159
  Convert an array of `ValidationIssue` objects into a flat field-to-error record. First error per field wins. Useful for building custom adapters.
160
160
 
161
- | Parameter | Type | Description |
162
- | --- | --- | --- |
163
- | `issues` | `ValidationIssue[]` | Array of `{ path: string, message: string }` |
161
+ | Parameter | Type | Description |
162
+ | --------- | ------------------- | -------------------------------------------- |
163
+ | `issues` | `ValidationIssue[]` | Array of `{ path: string, message: string }` |
164
164
 
165
165
  **Returns:** `Partial<Record<keyof TValues, ValidationError>>`
166
166
 
167
167
  ```ts
168
168
  issuesToRecord([
169
- { path: "email", message: "Required" },
170
- { path: "email", message: "Invalid" }, // ignored — first wins
171
- { path: "age", message: "Too young" },
169
+ { path: 'email', message: 'Required' },
170
+ { path: 'email', message: 'Invalid' }, // ignored — first wins
171
+ { path: 'age', message: 'Too young' },
172
172
  ])
173
173
  // => { email: "Required", age: "Too young" }
174
174
  ```
@@ -180,9 +180,9 @@ issuesToRecord([
180
180
  Each adapter is available via subpath import to avoid bundling unused adapters:
181
181
 
182
182
  ```ts
183
- import { zodSchema } from "@pyreon/validation/zod"
184
- import { valibotSchema } from "@pyreon/validation/valibot"
185
- import { arktypeSchema } from "@pyreon/validation/arktype"
183
+ import { zodSchema } from '@pyreon/validation/zod'
184
+ import { valibotSchema } from '@pyreon/validation/valibot'
185
+ import { arktypeSchema } from '@pyreon/validation/arktype'
186
186
  ```
187
187
 
188
188
  ### Mixing Field and Schema Validators
@@ -205,14 +205,14 @@ const form = useForm({
205
205
 
206
206
  ## Types
207
207
 
208
- | Type | Description |
209
- | --- | --- |
210
- | `ValidationIssue` | `{ path: string, message: string }` — normalized issue |
211
- | `SchemaAdapter<TSchema>` | Generic schema adapter factory type |
212
- | `FieldAdapter<TSchema>` | Generic field adapter factory type |
213
- | `SchemaValidateFn<TValues>` | Re-exported from `@pyreon/form` |
214
- | `ValidateFn<T>` | Re-exported from `@pyreon/form` |
215
- | `ValidationError` | Re-exported from `@pyreon/form` — `string \| undefined` |
208
+ | Type | Description |
209
+ | --------------------------- | ------------------------------------------------------- |
210
+ | `ValidationIssue` | `{ path: string, message: string }` — normalized issue |
211
+ | `SchemaAdapter<TSchema>` | Generic schema adapter factory type |
212
+ | `FieldAdapter<TSchema>` | Generic field adapter factory type |
213
+ | `SchemaValidateFn<TValues>` | Re-exported from `@pyreon/form` |
214
+ | `ValidateFn<T>` | Re-exported from `@pyreon/form` |
215
+ | `ValidationError` | Re-exported from `@pyreon/form` — `string \| undefined` |
216
216
 
217
217
  ## Gotchas
218
218
 
@@ -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 { 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"}
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 { 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
+ {"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":"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"}
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 { 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"}
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,20 +1,17 @@
1
1
  {
2
2
  "name": "@pyreon/validation",
3
- "version": "0.11.5",
3
+ "version": "0.11.6",
4
4
  "description": "Schema validation adapters for Pyreon forms (Zod, Valibot, ArkType)",
5
+ "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/validation#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/pyreon/pyreon/issues"
8
+ },
5
9
  "license": "MIT",
6
10
  "repository": {
7
11
  "type": "git",
8
12
  "url": "https://github.com/pyreon/pyreon.git",
9
13
  "directory": "packages/fundamentals/validation"
10
14
  },
11
- "homepage": "https://github.com/pyreon/pyreon/tree/main/packages/validation#readme",
12
- "bugs": {
13
- "url": "https://github.com/pyreon/pyreon/issues"
14
- },
15
- "publishConfig": {
16
- "access": "public"
17
- },
18
15
  "files": [
19
16
  "lib",
20
17
  "src",
@@ -48,15 +45,28 @@
48
45
  "types": "./lib/types/arktype.d.ts"
49
46
  }
50
47
  },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ },
51
51
  "scripts": {
52
52
  "build": "vl_rolldown_build",
53
53
  "dev": "vl_rolldown_build-watch",
54
54
  "test": "vitest run",
55
55
  "typecheck": "tsc --noEmit",
56
- "lint": "biome check ."
56
+ "lint": "oxlint ."
57
+ },
58
+ "devDependencies": {
59
+ "@happy-dom/global-registrator": "^20.8.3",
60
+ "@pyreon/core": "^0.11.6",
61
+ "@pyreon/form": "^0.11.6",
62
+ "@pyreon/reactivity": "^0.11.6",
63
+ "@pyreon/runtime-dom": "^0.11.6",
64
+ "arktype": "^2.2.0",
65
+ "valibot": "^1.2.0",
66
+ "zod": "^4.3.6"
57
67
  },
58
68
  "peerDependencies": {
59
- "@pyreon/form": "^0.11.5"
69
+ "@pyreon/form": "^0.11.6"
60
70
  },
61
71
  "peerDependenciesMeta": {
62
72
  "zod": {
@@ -68,15 +78,5 @@
68
78
  "arktype": {
69
79
  "optional": true
70
80
  }
71
- },
72
- "devDependencies": {
73
- "@happy-dom/global-registrator": "^20.8.3",
74
- "@pyreon/core": "^0.11.5",
75
- "@pyreon/form": "^0.11.5",
76
- "@pyreon/reactivity": "^0.11.5",
77
- "@pyreon/runtime-dom": "^0.11.5",
78
- "zod": "^4.3.6",
79
- "valibot": "^1.2.0",
80
- "arktype": "^2.2.0"
81
81
  }
82
82
  }
package/src/arktype.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { SchemaValidateFn, ValidateFn, ValidationError } from "@pyreon/form"
2
- import type { ValidationIssue } from "./types"
3
- import { issuesToRecord } from "./utils"
1
+ import type { SchemaValidateFn, ValidateFn, ValidationError } from '@pyreon/form'
2
+ import type { ValidationIssue } from './types'
3
+ import { issuesToRecord } from './utils'
4
4
 
5
5
  /**
6
6
  * Minimal ArkType-compatible interfaces so we don't require arktype as a hard dep.
@@ -21,12 +21,12 @@ interface ArkErrors extends Array<ArkError> {
21
21
  type ArkTypeCallable = (data: unknown) => unknown
22
22
 
23
23
  function isArkErrors(result: unknown): result is ArkErrors {
24
- return Array.isArray(result) && "summary" in (result as object)
24
+ return Array.isArray(result) && 'summary' in (result as object)
25
25
  }
26
26
 
27
27
  function arkIssuesToGeneric(errors: ArkErrors): ValidationIssue[] {
28
28
  return errors.map((err) => ({
29
- path: err.path.map(String).join("."),
29
+ path: err.path.map(String).join('.'),
30
30
  message: err.message,
31
31
  }))
32
32
  }
@@ -62,7 +62,7 @@ export function arktypeSchema<TValues extends Record<string, unknown>>(
62
62
  return issuesToRecord<TValues>(arkIssuesToGeneric(result))
63
63
  } catch (err) {
64
64
  return {
65
- "": err instanceof Error ? err.message : String(err),
65
+ '': err instanceof Error ? err.message : String(err),
66
66
  } as Partial<Record<keyof TValues, ValidationError>>
67
67
  }
68
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,36 +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(v.pipe(v.string(), v.email("Invalid email")), v.safeParseAsync)
209
- 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()
210
210
  })
211
211
 
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")
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')
215
215
  })
216
216
 
217
- it("handles result with undefined issues", async () => {
217
+ it('handles result with undefined issues', async () => {
218
218
  const mockSafeParse = async () => ({
219
219
  success: false as const,
220
220
  })
221
221
  const validate = valibotField({}, mockSafeParse)
222
- expect(await validate("x", {})).toBeUndefined()
222
+ expect(await validate('x', {})).toBeUndefined()
223
223
  })
224
224
  })
225
225
 
226
- describe("valibot + useForm integration", () => {
227
- it("validates form with valibot schema", async () => {
226
+ describe('valibot + useForm integration', () => {
227
+ it('validates form with valibot schema', async () => {
228
228
  const schema = v.object({
229
- email: v.pipe(v.string(), v.email("Invalid email")),
230
- 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')),
231
231
  })
232
232
 
233
233
  const { result: form, unmount } = mountWith(() =>
234
234
  useForm({
235
- initialValues: { email: "", password: "" },
235
+ initialValues: { email: '', password: '' },
236
236
  schema: valibotSchema(schema, v.safeParseAsync),
237
237
  onSubmit: () => {
238
238
  /* noop */
@@ -242,198 +242,198 @@ describe("valibot + useForm integration", () => {
242
242
 
243
243
  const valid = await form.validate()
244
244
  expect(valid).toBe(false)
245
- expect(form.fields.email.error()).toBe("Invalid email")
246
- 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')
247
247
  unmount()
248
248
  })
249
249
  })
250
250
 
251
251
  // ─── ArkType Adapter ─────────────────────────────────────────────────────────
252
252
 
253
- describe("arktypeSchema", () => {
253
+ describe('arktypeSchema', () => {
254
254
  const schema = type({
255
- email: "string.email",
256
- password: "string >= 8",
255
+ email: 'string.email',
256
+ password: 'string >= 8',
257
257
  })
258
258
 
259
- it("returns empty record for valid data", async () => {
259
+ it('returns empty record for valid data', async () => {
260
260
  const validate = arktypeSchema(schema)
261
- const result = await validate({ email: "a@b.com", password: "12345678" })
261
+ const result = await validate({ email: 'a@b.com', password: '12345678' })
262
262
  expect(result).toEqual({})
263
263
  })
264
264
 
265
- it("returns field errors for invalid data", async () => {
265
+ it('returns field errors for invalid data', async () => {
266
266
  const validate = arktypeSchema(schema)
267
- const result = await validate({ email: "bad", password: "short" })
267
+ const result = await validate({ email: 'bad', password: 'short' })
268
268
  expect(result.email).toBeDefined()
269
269
  expect(result.password).toBeDefined()
270
270
  })
271
271
  })
272
272
 
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()
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()
277
277
  })
278
278
 
279
- it("returns error message for invalid value", async () => {
280
- const validate = arktypeField(type("string.email"))
281
- 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', {})
282
282
  expect(result).toBeDefined()
283
- expect(typeof result).toBe("string")
283
+ expect(typeof result).toBe('string')
284
284
  })
285
285
  })
286
286
 
287
- describe("zodSchema catch branch", () => {
288
- it("captures Error when safeParseAsync throws an Error", async () => {
287
+ describe('zodSchema catch branch', () => {
288
+ it('captures Error when safeParseAsync throws an Error', async () => {
289
289
  const throwingSchema = {
290
290
  safeParseAsync: () => {
291
- throw new Error("Zod schema exploded")
291
+ throw new Error('Zod schema exploded')
292
292
  },
293
293
  safeParse: () => {
294
- throw new Error("Zod schema exploded")
294
+ throw new Error('Zod schema exploded')
295
295
  },
296
296
  }
297
297
  const validate = zodSchema(throwingSchema as any)
298
- const result = await validate({ email: "", password: "" })
299
- 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')
300
300
  })
301
301
 
302
- it("captures non-Error when safeParseAsync throws a string", async () => {
302
+ it('captures non-Error when safeParseAsync throws a string', async () => {
303
303
  const throwingSchema = {
304
304
  safeParseAsync: () => {
305
- throw "raw string error"
305
+ throw 'raw string error'
306
306
  },
307
307
  safeParse: () => {
308
- throw "raw string error"
308
+ throw 'raw string error'
309
309
  },
310
310
  }
311
311
  const validate = zodSchema(throwingSchema as any)
312
- const result = await validate({ email: "", password: "" })
313
- 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')
314
314
  })
315
315
  })
316
316
 
317
- describe("zodField catch branch", () => {
318
- it("captures Error when safeParseAsync throws an Error", async () => {
317
+ describe('zodField catch branch', () => {
318
+ it('captures Error when safeParseAsync throws an Error', async () => {
319
319
  const throwingSchema = {
320
320
  safeParseAsync: () => {
321
- throw new Error("Zod field exploded")
321
+ throw new Error('Zod field exploded')
322
322
  },
323
323
  safeParse: () => {
324
- throw new Error("Zod field exploded")
324
+ throw new Error('Zod field exploded')
325
325
  },
326
326
  }
327
327
  const validate = zodField(throwingSchema as any)
328
- const result = await validate("test", {})
329
- expect(result).toBe("Zod field exploded")
328
+ const result = await validate('test', {})
329
+ expect(result).toBe('Zod field exploded')
330
330
  })
331
331
 
332
- it("captures non-Error when safeParseAsync throws a string", async () => {
332
+ it('captures non-Error when safeParseAsync throws a string', async () => {
333
333
  const throwingSchema = {
334
334
  safeParseAsync: () => {
335
- throw "raw zod field error"
335
+ throw 'raw zod field error'
336
336
  },
337
337
  safeParse: () => {
338
- throw "raw zod field error"
338
+ throw 'raw zod field error'
339
339
  },
340
340
  }
341
341
  const validate = zodField(throwingSchema as any)
342
- const result = await validate("test", {})
343
- expect(result).toBe("raw zod field error")
342
+ const result = await validate('test', {})
343
+ expect(result).toBe('raw zod field error')
344
344
  })
345
345
  })
346
346
 
347
- describe("valibotSchema catch branch", () => {
348
- 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 () => {
349
349
  const throwingParse = () => {
350
- throw new Error("Valibot schema exploded")
350
+ throw new Error('Valibot schema exploded')
351
351
  }
352
352
  const validate = valibotSchema({}, throwingParse)
353
- const result = await validate({ email: "", password: "" })
354
- 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')
355
355
  })
356
356
 
357
- it("captures non-Error when safeParse function throws a string", async () => {
357
+ it('captures non-Error when safeParse function throws a string', async () => {
358
358
  const throwingParse = () => {
359
- throw "raw valibot schema error"
359
+ throw 'raw valibot schema error'
360
360
  }
361
361
  const validate = valibotSchema({}, throwingParse)
362
- const result = await validate({ email: "", password: "" })
363
- 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')
364
364
  })
365
365
  })
366
366
 
367
- describe("valibotField catch branch", () => {
368
- 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 () => {
369
369
  const throwingParse = () => {
370
- throw new Error("Valibot field exploded")
370
+ throw new Error('Valibot field exploded')
371
371
  }
372
372
  const validate = valibotField({}, throwingParse)
373
- const result = await validate("test", {})
374
- expect(result).toBe("Valibot field exploded")
373
+ const result = await validate('test', {})
374
+ expect(result).toBe('Valibot field exploded')
375
375
  })
376
376
 
377
- it("captures non-Error when safeParse function throws a string", async () => {
377
+ it('captures non-Error when safeParse function throws a string', async () => {
378
378
  const throwingParse = () => {
379
- throw "raw valibot field error"
379
+ throw 'raw valibot field error'
380
380
  }
381
381
  const validate = valibotField({}, throwingParse)
382
- const result = await validate("test", {})
383
- expect(result).toBe("raw valibot field error")
382
+ const result = await validate('test', {})
383
+ expect(result).toBe('raw valibot field error')
384
384
  })
385
385
  })
386
386
 
387
- describe("arktypeSchema catch branch", () => {
388
- 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 () => {
389
389
  const throwingSchema = () => {
390
- throw new Error("ArkType schema exploded")
390
+ throw new Error('ArkType schema exploded')
391
391
  }
392
392
  const validate = arktypeSchema(throwingSchema)
393
- const result = await validate({ email: "", password: "" })
394
- 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')
395
395
  })
396
396
 
397
- it("captures non-Error when schema function throws a string", async () => {
397
+ it('captures non-Error when schema function throws a string', async () => {
398
398
  const throwingSchema = () => {
399
- throw "raw arktype schema error"
399
+ throw 'raw arktype schema error'
400
400
  }
401
401
  const validate = arktypeSchema(throwingSchema)
402
- const result = await validate({ email: "", password: "" })
403
- 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')
404
404
  })
405
405
  })
406
406
 
407
- describe("arktypeField catch branch", () => {
408
- 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 () => {
409
409
  const throwingSchema = () => {
410
- throw new Error("ArkType field exploded")
410
+ throw new Error('ArkType field exploded')
411
411
  }
412
412
  const validate = arktypeField(throwingSchema)
413
- const result = await validate("test", {})
414
- expect(result).toBe("ArkType field exploded")
413
+ const result = await validate('test', {})
414
+ expect(result).toBe('ArkType field exploded')
415
415
  })
416
416
 
417
- it("captures non-Error when schema function throws a string", async () => {
417
+ it('captures non-Error when schema function throws a string', async () => {
418
418
  const throwingSchema = () => {
419
- throw "raw arktype field error"
419
+ throw 'raw arktype field error'
420
420
  }
421
421
  const validate = arktypeField(throwingSchema)
422
- const result = await validate("test", {})
423
- expect(result).toBe("raw arktype field error")
422
+ const result = await validate('test', {})
423
+ expect(result).toBe('raw arktype field error')
424
424
  })
425
425
  })
426
426
 
427
- describe("arktype + useForm integration", () => {
428
- it("validates form with arktype schema", async () => {
427
+ describe('arktype + useForm integration', () => {
428
+ it('validates form with arktype schema', async () => {
429
429
  const schema = type({
430
- email: "string.email",
431
- password: "string >= 8",
430
+ email: 'string.email',
431
+ password: 'string >= 8',
432
432
  })
433
433
 
434
434
  const { result: form, unmount } = mountWith(() =>
435
435
  useForm({
436
- initialValues: { email: "", password: "" },
436
+ initialValues: { email: '', password: '' },
437
437
  schema: arktypeSchema(schema),
438
438
  onSubmit: () => {
439
439
  /* noop */
package/src/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { SchemaValidateFn, ValidateFn, ValidationError } from "@pyreon/form"
1
+ import type { SchemaValidateFn, ValidateFn, ValidationError } from '@pyreon/form'
2
2
 
3
3
  /** Re-export form types for convenience. */
4
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,6 +1,6 @@
1
- import type { SchemaValidateFn, ValidateFn, ValidationError } from "@pyreon/form"
2
- import type { ValidationIssue } from "./types"
3
- import { issuesToRecord } from "./utils"
1
+ import type { SchemaValidateFn, ValidateFn, ValidationError } from '@pyreon/form'
2
+ import type { ValidationIssue } from './types'
3
+ import { issuesToRecord } from './utils'
4
4
 
5
5
  /**
6
6
  * Minimal Valibot-compatible interfaces so we don't require valibot as a hard dep.
@@ -31,7 +31,7 @@ type GenericSafeParseFn = Function
31
31
 
32
32
  function valibotIssuesToGeneric(issues: ValibotIssue[]): ValidationIssue[] {
33
33
  return issues.map((issue) => ({
34
- path: issue.path?.map((p) => String(p.key)).join(".") ?? "",
34
+ path: issue.path?.map((p) => String(p.key)).join('.') ?? '',
35
35
  message: issue.message,
36
36
  }))
37
37
  }
@@ -74,7 +74,7 @@ export function valibotSchema<TValues extends Record<string, unknown>>(
74
74
  return issuesToRecord<TValues>(valibotIssuesToGeneric(result.issues ?? []))
75
75
  } catch (err) {
76
76
  return {
77
- "": err instanceof Error ? err.message : String(err),
77
+ '': err instanceof Error ? err.message : String(err),
78
78
  } as Partial<Record<keyof TValues, ValidationError>>
79
79
  }
80
80
  }
package/src/zod.ts CHANGED
@@ -1,6 +1,6 @@
1
- import type { SchemaValidateFn, ValidateFn, ValidationError } from "@pyreon/form"
2
- import type { ValidationIssue } from "./types"
3
- import { issuesToRecord } from "./utils"
1
+ import type { SchemaValidateFn, ValidateFn, ValidationError } from '@pyreon/form'
2
+ import type { ValidationIssue } from './types'
3
+ import { issuesToRecord } from './utils'
4
4
 
5
5
  /**
6
6
  * Minimal Zod-compatible interfaces so we don't require zod as a hard dep.
@@ -28,7 +28,7 @@ interface ZodSchema<T = unknown> {
28
28
 
29
29
  function zodIssuesToGeneric(issues: ZodIssue[]): ValidationIssue[] {
30
30
  return issues.map((issue) => ({
31
- path: issue.path.map(String).join("."),
31
+ path: issue.path.map(String).join('.'),
32
32
  message: issue.message,
33
33
  }))
34
34
  }
@@ -62,7 +62,7 @@ export function zodSchema<TValues extends Record<string, unknown>>(
62
62
  return issuesToRecord<TValues>(zodIssuesToGeneric(result.error!.issues))
63
63
  } catch (err) {
64
64
  return {
65
- "": err instanceof Error ? err.message : String(err),
65
+ '': err instanceof Error ? err.message : String(err),
66
66
  } as Partial<Record<keyof TValues, ValidationError>>
67
67
  }
68
68
  }