zodvex 0.6.0-beta.39 → 0.6.0-beta.40
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.
|
@@ -3,11 +3,10 @@ import type { AnyRegistry } from '../../types';
|
|
|
3
3
|
/**
|
|
4
4
|
* Creates a Mantine form validator from the zodvex registry.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
* like `sensitive()`
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* For non-codec schemas, `safeEncode` behaves identically to `safeParse`.
|
|
6
|
+
* Validates non-codec fields client-side using `z.safeParse`. Codec fields
|
|
7
|
+
* (like `sensitive()`) are automatically skipped because Mantine's `useForm`
|
|
8
|
+
* uses `structuredClone`, which breaks class instances. Codec fields are
|
|
9
|
+
* validated server-side instead.
|
|
11
10
|
*
|
|
12
11
|
* @example
|
|
13
12
|
* ```tsx
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/form/mantine/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAGtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/form/mantine/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AAGtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AA0C9C;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,WAAW,EACnD,QAAQ,EAAE,CAAC,EACX,GAAG,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAYlC,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,4BASxC"}
|
|
@@ -2,6 +2,29 @@ import { getFunctionName } from 'convex/server';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
// src/form/mantine/index.ts
|
|
5
|
+
function unwrap(schema) {
|
|
6
|
+
let current = schema;
|
|
7
|
+
for (let i = 0; i < 10; i++) {
|
|
8
|
+
if (current instanceof z.ZodOptional || current instanceof z.ZodNullable) {
|
|
9
|
+
current = current._zod.def.innerType;
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
return current;
|
|
15
|
+
}
|
|
16
|
+
function buildFormSchema(schema) {
|
|
17
|
+
const unwrapped = unwrap(schema);
|
|
18
|
+
if (!(unwrapped instanceof z.ZodObject)) return schema;
|
|
19
|
+
const shape = unwrapped.shape;
|
|
20
|
+
if (!shape) return schema;
|
|
21
|
+
const formShape = {};
|
|
22
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
23
|
+
if (unwrap(fieldSchema) instanceof z.ZodCodec) continue;
|
|
24
|
+
formShape[key] = fieldSchema;
|
|
25
|
+
}
|
|
26
|
+
return z.object(formShape);
|
|
27
|
+
}
|
|
5
28
|
function mantineResolver(registry, ref) {
|
|
6
29
|
const path = getFunctionName(ref);
|
|
7
30
|
const entry = registry[path];
|
|
@@ -9,8 +32,9 @@ function mantineResolver(registry, ref) {
|
|
|
9
32
|
if (!schema) {
|
|
10
33
|
throw new Error(`zodvex: No args schema found for "${path}" in registry`);
|
|
11
34
|
}
|
|
35
|
+
const formSchema = buildFormSchema(schema);
|
|
12
36
|
return (values) => {
|
|
13
|
-
const result = z.
|
|
37
|
+
const result = z.safeParse(formSchema, values);
|
|
14
38
|
if (result.success) return {};
|
|
15
39
|
const errors = {};
|
|
16
40
|
for (const issue of result.error.issues) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/form/mantine/index.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"sources":["../../../src/form/mantine/index.ts"],"names":[],"mappings":";;;;AAQA,SAAS,OAAO,MAAA,EAAoC;AAClD,EAAA,IAAI,OAAA,GAAU,MAAA;AACd,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,EAAA,EAAI,CAAA,EAAA,EAAK;AAC3B,IAAA,IAAI,OAAA,YAAmB,CAAA,CAAE,WAAA,IAAe,OAAA,YAAmB,EAAE,WAAA,EAAa;AACxE,MAAA,OAAA,GAAW,OAAA,CAAgB,KAAK,GAAA,CAAI,SAAA;AACpC,MAAA;AAAA,IACF;AACA,IAAA;AAAA,EACF;AACA,EAAA,OAAO,OAAA;AACT;AAYA,SAAS,gBAAgB,MAAA,EAAoC;AAC3D,EAAA,MAAM,SAAA,GAAY,OAAO,MAAM,CAAA;AAC/B,EAAA,IAAI,EAAE,SAAA,YAAqB,CAAA,CAAE,SAAA,CAAA,EAAY,OAAO,MAAA;AAEhD,EAAA,MAAM,QAAS,SAAA,CAAkB,KAAA;AACjC,EAAA,IAAI,CAAC,OAAO,OAAO,MAAA;AAEnB,EAAA,MAAM,YAA0C,EAAC;AACjD,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,WAAW,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AACtD,IAAA,IAAI,MAAA,CAAO,WAAW,CAAA,YAAa,CAAA,CAAE,QAAA,EAAU;AAC/C,IAAA,SAAA,CAAU,GAAG,CAAA,GAAI,WAAA;AAAA,EACnB;AACA,EAAA,OAAO,CAAA,CAAE,OAAO,SAAS,CAAA;AAC3B;AAqBO,SAAS,eAAA,CACd,UACA,GAAA,EACA;AACA,EAAA,MAAM,IAAA,GAAO,gBAAgB,GAAG,CAAA;AAChC,EAAA,MAAM,KAAA,GAAQ,SAAS,IAAI,CAAA;AAC3B,EAAA,MAAM,SAAS,KAAA,EAAO,IAAA;AACtB,EAAA,IAAI,CAAC,MAAA,EAAQ;AACX,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kCAAA,EAAqC,IAAI,CAAA,aAAA,CAAe,CAAA;AAAA,EAC1E;AAGA,EAAA,MAAM,UAAA,GAAa,gBAAgB,MAAM,CAAA;AAEzC,EAAA,OAAO,CAAC,MAAA,KAAoC;AAC1C,IAAA,MAAM,MAAA,GAAS,CAAA,CAAE,SAAA,CAAU,UAAA,EAAY,MAAM,CAAA;AAC7C,IAAA,IAAI,MAAA,CAAO,OAAA,EAAS,OAAO,EAAC;AAC5B,IAAA,MAAM,SAAiC,EAAC;AACxC,IAAA,KAAA,MAAW,KAAA,IAAS,MAAA,CAAO,KAAA,CAAM,MAAA,EAAQ;AACvC,MAAA,MAAA,CAAO,MAAM,IAAA,CAAK,IAAA,CAAK,GAAG,CAAC,IAAI,KAAA,CAAM,OAAA;AAAA,IACvC;AACA,IAAA,OAAO,MAAA;AAAA,EACT,CAAA;AACF","file":"index.js","sourcesContent":["import type { FunctionReference } from 'convex/server'\nimport { getFunctionName } from 'convex/server'\nimport { z } from 'zod'\nimport type { AnyRegistry } from '../../types'\n\n/**\n * Unwrap ZodOptional/ZodNullable to find the inner schema.\n */\nfunction unwrap(schema: z.ZodTypeAny): z.ZodTypeAny {\n let current = schema\n for (let i = 0; i < 10; i++) {\n if (current instanceof z.ZodOptional || current instanceof z.ZodNullable) {\n current = (current as any)._zod.def.innerType\n continue\n }\n break\n }\n return current\n}\n\n/**\n * Build a form-safe schema by stripping codec fields from a ZodObject.\n *\n * Mantine's `useForm` uses `structuredClone` internally, which breaks class\n * instances (like SensitiveField). Codec fields can't be validated client-side\n * because the encode function expects methods that don't survive cloning.\n * They are validated server-side instead.\n *\n * For non-codec fields, validation is identical to the original schema.\n */\nfunction buildFormSchema(schema: z.ZodTypeAny): z.ZodTypeAny {\n const unwrapped = unwrap(schema)\n if (!(unwrapped instanceof z.ZodObject)) return schema\n\n const shape = (unwrapped as any).shape as Record<string, z.ZodTypeAny> | undefined\n if (!shape) return schema\n\n const formShape: Record<string, z.ZodTypeAny> = {}\n for (const [key, fieldSchema] of Object.entries(shape)) {\n if (unwrap(fieldSchema) instanceof z.ZodCodec) continue\n formShape[key] = fieldSchema\n }\n return z.object(formShape)\n}\n\n/**\n * Creates a Mantine form validator from the zodvex registry.\n *\n * Validates non-codec fields client-side using `z.safeParse`. Codec fields\n * (like `sensitive()`) are automatically skipped because Mantine's `useForm`\n * uses `structuredClone`, which breaks class instances. Codec fields are\n * validated server-side instead.\n *\n * @example\n * ```tsx\n * import { useForm } from '@mantine/form'\n * import { mantineResolver } from 'zodvex/form/mantine'\n *\n * const form = useForm({\n * initialValues: { name: '', email: '' },\n * validate: mantineResolver(registry, api.users.create),\n * })\n * ```\n */\nexport function mantineResolver<R extends AnyRegistry>(\n registry: R,\n ref: FunctionReference<any, any, any, any>\n) {\n const path = getFunctionName(ref)\n const entry = registry[path]\n const schema = entry?.args\n if (!schema) {\n throw new Error(`zodvex: No args schema found for \"${path}\" in registry`)\n }\n\n // Build a schema without codec fields — done once at creation time\n const formSchema = buildFormSchema(schema)\n\n return (values: Record<string, unknown>) => {\n const result = z.safeParse(formSchema, values)\n if (result.success) return {}\n const errors: Record<string, string> = {}\n for (const issue of result.error.issues) {\n errors[issue.path.join('.')] = issue.message\n }\n return errors\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zodvex",
|
|
3
|
-
"version": "0.6.0-beta.
|
|
3
|
+
"version": "0.6.0-beta.40",
|
|
4
4
|
"description": "Zod <=> Convex integration, supporting Zod 4",
|
|
5
5
|
"keywords": ["zod", "convex", "validators", "codec", "mapping", "schema", "validation"],
|
|
6
6
|
"homepage": "https://github.com/panzacoder/zodvex#readme",
|
|
@@ -4,13 +4,52 @@ import { z } from 'zod'
|
|
|
4
4
|
import type { AnyRegistry } from '../../types'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
7
|
+
* Unwrap ZodOptional/ZodNullable to find the inner schema.
|
|
8
|
+
*/
|
|
9
|
+
function unwrap(schema: z.ZodTypeAny): z.ZodTypeAny {
|
|
10
|
+
let current = schema
|
|
11
|
+
for (let i = 0; i < 10; i++) {
|
|
12
|
+
if (current instanceof z.ZodOptional || current instanceof z.ZodNullable) {
|
|
13
|
+
current = (current as any)._zod.def.innerType
|
|
14
|
+
continue
|
|
15
|
+
}
|
|
16
|
+
break
|
|
17
|
+
}
|
|
18
|
+
return current
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Build a form-safe schema by stripping codec fields from a ZodObject.
|
|
8
23
|
*
|
|
9
|
-
*
|
|
10
|
-
* like
|
|
11
|
-
*
|
|
24
|
+
* Mantine's `useForm` uses `structuredClone` internally, which breaks class
|
|
25
|
+
* instances (like SensitiveField). Codec fields can't be validated client-side
|
|
26
|
+
* because the encode function expects methods that don't survive cloning.
|
|
27
|
+
* They are validated server-side instead.
|
|
12
28
|
*
|
|
13
|
-
* For non-codec
|
|
29
|
+
* For non-codec fields, validation is identical to the original schema.
|
|
30
|
+
*/
|
|
31
|
+
function buildFormSchema(schema: z.ZodTypeAny): z.ZodTypeAny {
|
|
32
|
+
const unwrapped = unwrap(schema)
|
|
33
|
+
if (!(unwrapped instanceof z.ZodObject)) return schema
|
|
34
|
+
|
|
35
|
+
const shape = (unwrapped as any).shape as Record<string, z.ZodTypeAny> | undefined
|
|
36
|
+
if (!shape) return schema
|
|
37
|
+
|
|
38
|
+
const formShape: Record<string, z.ZodTypeAny> = {}
|
|
39
|
+
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
40
|
+
if (unwrap(fieldSchema) instanceof z.ZodCodec) continue
|
|
41
|
+
formShape[key] = fieldSchema
|
|
42
|
+
}
|
|
43
|
+
return z.object(formShape)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Creates a Mantine form validator from the zodvex registry.
|
|
48
|
+
*
|
|
49
|
+
* Validates non-codec fields client-side using `z.safeParse`. Codec fields
|
|
50
|
+
* (like `sensitive()`) are automatically skipped because Mantine's `useForm`
|
|
51
|
+
* uses `structuredClone`, which breaks class instances. Codec fields are
|
|
52
|
+
* validated server-side instead.
|
|
14
53
|
*
|
|
15
54
|
* @example
|
|
16
55
|
* ```tsx
|
|
@@ -34,8 +73,11 @@ export function mantineResolver<R extends AnyRegistry>(
|
|
|
34
73
|
throw new Error(`zodvex: No args schema found for "${path}" in registry`)
|
|
35
74
|
}
|
|
36
75
|
|
|
76
|
+
// Build a schema without codec fields — done once at creation time
|
|
77
|
+
const formSchema = buildFormSchema(schema)
|
|
78
|
+
|
|
37
79
|
return (values: Record<string, unknown>) => {
|
|
38
|
-
const result = z.
|
|
80
|
+
const result = z.safeParse(formSchema, values)
|
|
39
81
|
if (result.success) return {}
|
|
40
82
|
const errors: Record<string, string> = {}
|
|
41
83
|
for (const issue of result.error.issues) {
|