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
- * Uses `z.safeEncode` (runtime wire direction) so that codec fields
7
- * like `sensitive()` validate runtime values (e.g. SensitiveField instances)
8
- * rather than expecting wire format input.
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;AAE9C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,WAAW,EACnD,QAAQ,EAAE,CAAC,EACX,GAAG,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IASlC,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,4BASxC"}
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.safeEncode(schema, values);
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":";;;;AAyBO,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;AAEA,EAAA,OAAO,CAAC,MAAA,KAAoC;AAC1C,IAAA,MAAM,MAAA,GAAS,CAAA,CAAE,UAAA,CAAW,MAAA,EAAQ,MAAM,CAAA;AAC1C,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 * Creates a Mantine form validator from the zodvex registry.\n *\n * Uses `z.safeEncode` (runtime wire direction) so that codec fields\n * like `sensitive()` validate runtime values (e.g. SensitiveField instances)\n * rather than expecting wire format input.\n *\n * For non-codec schemas, `safeEncode` behaves identically to `safeParse`.\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 return (values: Record<string, unknown>) => {\n const result = z.safeEncode(schema, 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"]}
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.39",
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
- * Creates a Mantine form validator from the zodvex registry.
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
- * Uses `z.safeEncode` (runtime wire direction) so that codec fields
10
- * like `sensitive()` validate runtime values (e.g. SensitiveField instances)
11
- * rather than expecting wire format input.
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 schemas, `safeEncode` behaves identically to `safeParse`.
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.safeEncode(schema, values)
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) {