@pyreon/validation 0.0.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.
- package/LICENSE +21 -0
- package/README.md +223 -0
- package/lib/analysis/arktype.js.html +5406 -0
- package/lib/analysis/index.js.html +5406 -0
- package/lib/analysis/valibot.js.html +5406 -0
- package/lib/analysis/zod.js.html +5406 -0
- package/lib/arktype.js +88 -0
- package/lib/arktype.js.map +1 -0
- package/lib/index.js +223 -0
- package/lib/index.js.map +1 -0
- package/lib/types/arktype.d.ts +90 -0
- package/lib/types/arktype.d.ts.map +1 -0
- package/lib/types/arktype2.d.ts +51 -0
- package/lib/types/arktype2.d.ts.map +1 -0
- package/lib/types/index.d.ts +195 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/valibot.d.ts +89 -0
- package/lib/types/valibot.d.ts.map +1 -0
- package/lib/types/valibot2.d.ts +51 -0
- package/lib/types/valibot2.d.ts.map +1 -0
- package/lib/types/zod.d.ts +86 -0
- package/lib/types/zod.d.ts.map +1 -0
- package/lib/types/zod2.d.ts +71 -0
- package/lib/types/zod2.d.ts.map +1 -0
- package/lib/valibot.js +87 -0
- package/lib/valibot.js.map +1 -0
- package/lib/zod.js +84 -0
- package/lib/zod.js.map +1 -0
- package/package.json +81 -0
- package/src/arktype.ts +103 -0
- package/src/index.ts +13 -0
- package/src/tests/setup.ts +1 -0
- package/src/tests/validation.test.ts +451 -0
- package/src/types.ts +33 -0
- package/src/utils.ts +21 -0
- package/src/valibot.ts +119 -0
- package/src/zod.ts +102 -0
package/lib/zod.js
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
//#region src/utils.ts
|
|
2
|
+
/**
|
|
3
|
+
* Convert an array of validation issues into a flat field → error record.
|
|
4
|
+
* For nested paths like ["address", "city"], produces "address.city".
|
|
5
|
+
* When multiple issues exist for the same path, the first message wins.
|
|
6
|
+
*/
|
|
7
|
+
function issuesToRecord(issues) {
|
|
8
|
+
const errors = {};
|
|
9
|
+
for (const issue of issues) {
|
|
10
|
+
const key = issue.path;
|
|
11
|
+
if (errors[key] === void 0) errors[key] = issue.message;
|
|
12
|
+
}
|
|
13
|
+
return errors;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region src/zod.ts
|
|
18
|
+
function zodIssuesToGeneric(issues) {
|
|
19
|
+
return issues.map((issue) => ({
|
|
20
|
+
path: issue.path.map(String).join("."),
|
|
21
|
+
message: issue.message
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Create a form-level schema validator from a Zod schema.
|
|
26
|
+
* Supports both sync and async Zod schemas (uses `safeParseAsync`).
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* import { z } from 'zod'
|
|
30
|
+
* import { zodSchema } from '@pyreon/validation/zod'
|
|
31
|
+
*
|
|
32
|
+
* const schema = z.object({
|
|
33
|
+
* email: z.string().email(),
|
|
34
|
+
* password: z.string().min(8),
|
|
35
|
+
* })
|
|
36
|
+
*
|
|
37
|
+
* const form = useForm({
|
|
38
|
+
* initialValues: { email: '', password: '' },
|
|
39
|
+
* schema: zodSchema(schema),
|
|
40
|
+
* onSubmit: (values) => { ... },
|
|
41
|
+
* })
|
|
42
|
+
*/
|
|
43
|
+
function zodSchema(schema) {
|
|
44
|
+
return async (values) => {
|
|
45
|
+
try {
|
|
46
|
+
const result = await schema.safeParseAsync(values);
|
|
47
|
+
if (result.success) return {};
|
|
48
|
+
return issuesToRecord(zodIssuesToGeneric(result.error.issues));
|
|
49
|
+
} catch (err) {
|
|
50
|
+
return { "": err instanceof Error ? err.message : String(err) };
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create a single-field validator from a Zod schema.
|
|
56
|
+
* Supports both sync and async Zod refinements.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* import { z } from 'zod'
|
|
60
|
+
* import { zodField } from '@pyreon/validation/zod'
|
|
61
|
+
*
|
|
62
|
+
* const form = useForm({
|
|
63
|
+
* initialValues: { email: '' },
|
|
64
|
+
* validators: {
|
|
65
|
+
* email: zodField(z.string().email('Invalid email')),
|
|
66
|
+
* },
|
|
67
|
+
* onSubmit: (values) => { ... },
|
|
68
|
+
* })
|
|
69
|
+
*/
|
|
70
|
+
function zodField(schema) {
|
|
71
|
+
return async (value) => {
|
|
72
|
+
try {
|
|
73
|
+
const result = await schema.safeParseAsync(value);
|
|
74
|
+
if (result.success) return void 0;
|
|
75
|
+
return result.error.issues[0]?.message;
|
|
76
|
+
} catch (err) {
|
|
77
|
+
return err instanceof Error ? err.message : String(err);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
export { zodField, zodSchema };
|
|
84
|
+
//# sourceMappingURL=zod.js.map
|
package/lib/zod.js.map
ADDED
|
@@ -0,0 +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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pyreon/validation",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Schema validation adapters for Pyreon forms (Zod, Valibot, ArkType)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/pyreon/fundamentals.git",
|
|
9
|
+
"directory": "packages/validation"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/pyreon/fundamentals/tree/main/packages/validation#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/pyreon/fundamentals/issues"
|
|
14
|
+
},
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"lib",
|
|
20
|
+
"src",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"type": "module",
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"main": "./lib/index.js",
|
|
27
|
+
"module": "./lib/index.js",
|
|
28
|
+
"types": "./lib/types/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"bun": "./src/index.ts",
|
|
32
|
+
"import": "./lib/index.js",
|
|
33
|
+
"types": "./lib/types/index.d.ts"
|
|
34
|
+
},
|
|
35
|
+
"./zod": {
|
|
36
|
+
"bun": "./src/zod.ts",
|
|
37
|
+
"import": "./lib/zod.js",
|
|
38
|
+
"types": "./lib/types/zod.d.ts"
|
|
39
|
+
},
|
|
40
|
+
"./valibot": {
|
|
41
|
+
"bun": "./src/valibot.ts",
|
|
42
|
+
"import": "./lib/valibot.js",
|
|
43
|
+
"types": "./lib/types/valibot.d.ts"
|
|
44
|
+
},
|
|
45
|
+
"./arktype": {
|
|
46
|
+
"bun": "./src/arktype.ts",
|
|
47
|
+
"import": "./lib/arktype.js",
|
|
48
|
+
"types": "./lib/types/arktype.d.ts"
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "vl_rolldown_build",
|
|
53
|
+
"dev": "vl_rolldown_build-watch",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"typecheck": "tsc --noEmit"
|
|
56
|
+
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"@pyreon/form": "workspace:^"
|
|
59
|
+
},
|
|
60
|
+
"peerDependenciesMeta": {
|
|
61
|
+
"zod": {
|
|
62
|
+
"optional": true
|
|
63
|
+
},
|
|
64
|
+
"valibot": {
|
|
65
|
+
"optional": true
|
|
66
|
+
},
|
|
67
|
+
"arktype": {
|
|
68
|
+
"optional": true
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@happy-dom/global-registrator": "^20.8.3",
|
|
73
|
+
"@pyreon/core": "^0.2.1",
|
|
74
|
+
"@pyreon/form": "workspace:*",
|
|
75
|
+
"@pyreon/reactivity": "^0.2.1",
|
|
76
|
+
"@pyreon/runtime-dom": "^0.2.1",
|
|
77
|
+
"zod": "^4.3.6",
|
|
78
|
+
"valibot": "^1.2.0",
|
|
79
|
+
"arktype": "^2.2.0"
|
|
80
|
+
}
|
|
81
|
+
}
|
package/src/arktype.ts
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
SchemaValidateFn,
|
|
3
|
+
ValidateFn,
|
|
4
|
+
ValidationError,
|
|
5
|
+
} from '@pyreon/form'
|
|
6
|
+
import type { ValidationIssue } from './types'
|
|
7
|
+
import { issuesToRecord } from './utils'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Minimal ArkType-compatible interfaces so we don't require arktype as a hard dep.
|
|
11
|
+
*/
|
|
12
|
+
interface ArkError {
|
|
13
|
+
path: PropertyKey[]
|
|
14
|
+
message: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ArkErrors extends Array<ArkError> {
|
|
18
|
+
summary: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Internal callable interface matching ArkType's Type.
|
|
23
|
+
* Not exposed publicly — consumers pass their ArkType schema directly.
|
|
24
|
+
*/
|
|
25
|
+
interface ArkTypeCallable {
|
|
26
|
+
(data: unknown): unknown
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isArkErrors(result: unknown): result is ArkErrors {
|
|
30
|
+
return Array.isArray(result) && 'summary' in (result as object)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function arkIssuesToGeneric(errors: ArkErrors): ValidationIssue[] {
|
|
34
|
+
return errors.map((err) => ({
|
|
35
|
+
path: err.path.map(String).join('.'),
|
|
36
|
+
message: err.message,
|
|
37
|
+
}))
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Create a form-level schema validator from an ArkType schema.
|
|
42
|
+
*
|
|
43
|
+
* Accepts any callable ArkType `Type` instance. The schema is duck-typed —
|
|
44
|
+
* no ArkType import required.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* import { type } from 'arktype'
|
|
48
|
+
* import { arktypeSchema } from '@pyreon/validation/arktype'
|
|
49
|
+
*
|
|
50
|
+
* const schema = type({
|
|
51
|
+
* email: 'string.email',
|
|
52
|
+
* password: 'string >= 8',
|
|
53
|
+
* })
|
|
54
|
+
*
|
|
55
|
+
* const form = useForm({
|
|
56
|
+
* initialValues: { email: '', password: '' },
|
|
57
|
+
* schema: arktypeSchema(schema),
|
|
58
|
+
* onSubmit: (values) => { ... },
|
|
59
|
+
* })
|
|
60
|
+
*/
|
|
61
|
+
export function arktypeSchema<TValues extends Record<string, unknown>>(
|
|
62
|
+
schema: ArkTypeCallable,
|
|
63
|
+
): SchemaValidateFn<TValues> {
|
|
64
|
+
return (values: TValues) => {
|
|
65
|
+
try {
|
|
66
|
+
const result = schema(values)
|
|
67
|
+
if (!isArkErrors(result))
|
|
68
|
+
return {} as Partial<Record<keyof TValues, ValidationError>>
|
|
69
|
+
return issuesToRecord<TValues>(arkIssuesToGeneric(result))
|
|
70
|
+
} catch (err) {
|
|
71
|
+
return {
|
|
72
|
+
'': err instanceof Error ? err.message : String(err),
|
|
73
|
+
} as Partial<Record<keyof TValues, ValidationError>>
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Create a single-field validator from an ArkType schema.
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* import { type } from 'arktype'
|
|
83
|
+
* import { arktypeField } from '@pyreon/validation/arktype'
|
|
84
|
+
*
|
|
85
|
+
* const form = useForm({
|
|
86
|
+
* initialValues: { email: '' },
|
|
87
|
+
* validators: {
|
|
88
|
+
* email: arktypeField(type('string.email')),
|
|
89
|
+
* },
|
|
90
|
+
* onSubmit: (values) => { ... },
|
|
91
|
+
* })
|
|
92
|
+
*/
|
|
93
|
+
export function arktypeField<T>(schema: ArkTypeCallable): ValidateFn<T> {
|
|
94
|
+
return (value: T) => {
|
|
95
|
+
try {
|
|
96
|
+
const result = schema(value)
|
|
97
|
+
if (!isArkErrors(result)) return undefined
|
|
98
|
+
return result[0]?.message
|
|
99
|
+
} catch (err) {
|
|
100
|
+
return err instanceof Error ? err.message : String(err)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { zodSchema, zodField } from './zod'
|
|
2
|
+
export { valibotSchema, valibotField } from './valibot'
|
|
3
|
+
export { arktypeSchema, arktypeField } from './arktype'
|
|
4
|
+
export { issuesToRecord } from './utils'
|
|
5
|
+
|
|
6
|
+
export type {
|
|
7
|
+
ValidationIssue,
|
|
8
|
+
SchemaValidateFn,
|
|
9
|
+
ValidateFn,
|
|
10
|
+
ValidationError,
|
|
11
|
+
SchemaAdapter,
|
|
12
|
+
FieldAdapter,
|
|
13
|
+
} from './types'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '@happy-dom/global-registrator'
|