zodvex 0.2.2 → 0.2.4

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
@@ -26,6 +26,7 @@ npm install zodvex zod@^4.1.0 convex convex-helpers
26
26
  ```
27
27
 
28
28
  **Peer dependencies:**
29
+
29
30
  - `zod` (^4.1.0 or later)
30
31
  - `convex` (>= 1.27.0)
31
32
  - `convex-helpers` (>= 0.1.104)
@@ -106,16 +107,16 @@ import { zodTable, zid } from 'zodvex'
106
107
  export const Users = zodTable('users', {
107
108
  name: z.string(),
108
109
  email: z.string().email(),
109
- age: z.number().optional(), // → v.optional(v.float64())
110
- deletedAt: z.date().nullable(), // → v.union(v.float64(), v.null())
110
+ age: z.number().optional(), // → v.optional(v.float64())
111
+ deletedAt: z.date().nullable(), // → v.union(v.float64(), v.null())
111
112
  teamId: zid('teams').optional()
112
113
  })
113
114
 
114
115
  // Access the underlying table
115
- Users.table // Convex table definition
116
- Users.shape // Original Zod shape
117
- Users.zDoc // Zod schema with _id and _creationTime
118
- Users.docArray // z.array(zDoc) for return types
116
+ Users.table // Convex table definition
117
+ Users.shape // Original Zod shape
118
+ Users.zDoc // Zod schema with _id and _creationTime
119
+ Users.docArray // z.array(zDoc) for return types
119
120
  ```
120
121
 
121
122
  ## Building Your Schema
@@ -134,8 +135,7 @@ export default defineSchema({
134
135
  .index('by_team', ['teamId'])
135
136
  .searchIndex('search_name', { searchField: 'name' }),
136
137
 
137
- teams: Teams.table
138
- .index('by_created', ['_creationTime'])
138
+ teams: Teams.table.index('by_created', ['_creationTime'])
139
139
  })
140
140
  ```
141
141
 
@@ -236,7 +236,11 @@ type CreateUserForm = z.infer<typeof CreateUserForm>
236
236
  function UserForm() {
237
237
  const createUser = useMutation(api.users.createUser)
238
238
 
239
- const { register, handleSubmit, formState: { errors } } = useForm<CreateUserForm>({
239
+ const {
240
+ register,
241
+ handleSubmit,
242
+ formState: { errors }
243
+ } = useForm<CreateUserForm>({
240
244
  resolver: zodResolver(CreateUserForm)
241
245
  })
242
246
 
@@ -263,19 +267,22 @@ function UserForm() {
263
267
  ### Builders
264
268
 
265
269
  **Basic builders** - Create type-safe functions without auth:
270
+
266
271
  ```ts
267
- zQueryBuilder(query) // Creates query builder
272
+ zQueryBuilder(query) // Creates query builder
268
273
  zMutationBuilder(mutation) // Creates mutation builder
269
- zActionBuilder(action) // Creates action builder
274
+ zActionBuilder(action) // Creates action builder
270
275
  ```
271
276
 
272
277
  **Custom builders** - Add auth or custom context:
278
+
273
279
  ```ts
280
+ import { type QueryCtx } from './_generated/server'
274
281
  import { customCtx } from 'zodvex'
275
282
 
276
283
  const authQuery = zCustomQueryBuilder(
277
284
  query,
278
- customCtx(async (ctx) => {
285
+ customCtx(async (ctx: QueryCtx) => {
279
286
  const user = await getUserOrThrow(ctx)
280
287
  return { user }
281
288
  })
@@ -337,28 +344,52 @@ const decoded = codec.decode(encoded)
337
344
 
338
345
  ### Supported Types
339
346
 
340
- | Zod Type | Convex Validator |
341
- | ----------------- | ------------------------- |
342
- | `z.string()` | `v.string()` |
343
- | `z.number()` | `v.float64()` |
344
- | `z.bigint()` | `v.int64()` |
345
- | `z.boolean()` | `v.boolean()` |
346
- | `z.date()` | `v.float64()` (timestamp) |
347
- | `z.null()` | `v.null()` |
348
- | `z.array(T)` | `v.array(T)` |
349
- | `z.object({...})` | `v.object({...})` |
350
- | `z.record(T)` | `v.record(v.string(), T)` |
351
- | `z.union([...])` | `v.union(...)` |
352
- | `z.literal(x)` | `v.literal(x)` |
353
- | `z.enum([...])` | `v.union(literals...)` |
354
- | `z.optional(T)` | `v.optional(T)` |
355
- | `z.nullable(T)` | `v.union(T, v.null())` |
347
+ | Zod Type | Convex Validator |
348
+ | -------------------- | ------------------------------------------- |
349
+ | `z.string()` | `v.string()` |
350
+ | `z.number()` | `v.float64()` |
351
+ | `z.bigint()` | `v.int64()` |
352
+ | `z.boolean()` | `v.boolean()` |
353
+ | `z.date()` | `v.float64()` (timestamp) |
354
+ | `z.null()` | `v.null()` |
355
+ | `z.array(T)` | `v.array(T)` |
356
+ | `z.object({...})` | `v.object({...})` |
357
+ | `z.record(T)` | `v.record(v.string(), T)` |
358
+ | `z.union([...])` | `v.union(...)` |
359
+ | `z.literal(x)` | `v.literal(x)` |
360
+ | `z.enum(['a', 'b'])` | `v.union(v.literal('a'), v.literal('b'))` ¹ |
361
+ | `z.optional(T)` | `v.optional(T)` |
362
+ | `z.nullable(T)` | `v.union(T, v.null())` |
363
+
364
+ **Zod v4 Enum Type Note:**
365
+
366
+ ¹ Enum types in Zod v4 produce a slightly different TypeScript signature than manually created unions:
367
+
368
+ ```typescript
369
+ // Manual union (precise tuple type)
370
+ const manual = v.union(v.literal('a'), v.literal('b'))
371
+ // Type: VUnion<"a" | "b", [VLiteral<"a", "required">, VLiteral<"b", "required">], "required", never>
372
+
373
+ // From Zod enum (array type)
374
+ const fromZod = zodToConvex(z.enum(['a', 'b']))
375
+ // Type: VUnion<"a" | "b", Array<VLiteral<"a" | "b", "required">>, "required", never>
376
+ ```
377
+
378
+ **This difference is purely cosmetic with no functional impact:**
379
+
380
+ - ✅ Value types are identical (`"a" | "b"`)
381
+ - ✅ Runtime validation is identical
382
+ - ✅ Type safety for function arguments/returns is preserved
383
+ - ✅ Convex uses `T[number]` which works identically for both array and tuple types
384
+
385
+ This limitation exists because Zod v4 changed enum types from tuple-based to Record-based ([`ToEnum<T>`](https://github.com/colinhacks/zod/blob/v4/src/v4/core/util.ts#L83-L85)). TypeScript cannot convert a Record type to a specific tuple without knowing the keys at compile time. See [Zod v4 changelog](https://zod.dev/v4/changelog) and [enum evolution discussion](https://github.com/colinhacks/zod/discussions/2125) for more details.
356
386
 
357
387
  **Convex IDs:**
388
+
358
389
  ```ts
359
390
  import { zid } from 'zodvex'
360
391
 
361
- zid('tableName') // → v.id('tableName')
392
+ zid('tableName') // → v.id('tableName')
362
393
  zid('tableName').optional() // → v.optional(v.id('tableName'))
363
394
  ```
364
395
 
@@ -368,14 +399,16 @@ zid('tableName').optional() // → v.optional(v.id('tableName'))
368
399
 
369
400
  Create builders with injected auth, permissions, or other context:
370
401
 
402
+ > **Best Practice:** Always add explicit type annotations to the `ctx` parameter in your `customCtx` functions. This improves TypeScript performance and prevents `ctx` from falling back to `any` in complex type scenarios. Import context types from `./_generated/server` (e.g., `QueryCtx`, `MutationCtx`, `ActionCtx`).
403
+
371
404
  ```ts
372
405
  import { zCustomQueryBuilder, zCustomMutationBuilder, customCtx } from 'zodvex'
373
- import { query, mutation } from './_generated/server'
406
+ import { type QueryCtx, type MutationCtx, query, mutation } from './_generated/server'
374
407
 
375
408
  // Add user to all queries
376
409
  export const authQuery = zCustomQueryBuilder(
377
410
  query,
378
- customCtx(async (ctx) => {
411
+ customCtx(async (ctx: QueryCtx) => {
379
412
  const user = await getUserOrThrow(ctx)
380
413
  return { user }
381
414
  })
@@ -384,7 +417,7 @@ export const authQuery = zCustomQueryBuilder(
384
417
  // Add user + permissions to mutations
385
418
  export const authMutation = zCustomMutationBuilder(
386
419
  mutation,
387
- customCtx(async (ctx) => {
420
+ customCtx(async (ctx: MutationCtx) => {
388
421
  const user = await getUserOrThrow(ctx)
389
422
  const permissions = await getPermissions(ctx, user)
390
423
  return { user, permissions }
@@ -461,7 +494,11 @@ const userShape = pickShape(User, ['email', 'firstName', 'lastName'])
461
494
  const UserUpdate = z.object(userShape)
462
495
 
463
496
  // Or use safePick (convenience wrapper that does the same thing)
464
- const UserUpdate = safePick(User, { email: true, firstName: true, lastName: true })
497
+ const UserUpdate = safePick(User, {
498
+ email: true,
499
+ firstName: true,
500
+ lastName: true
501
+ })
465
502
  ```
466
503
 
467
504
  ## Why zodvex?
package/dist/index.d.mts CHANGED
@@ -13,16 +13,29 @@ type IsZid<T> = T extends {
13
13
  type ExtractTableName<T> = T extends {
14
14
  _tableName: infer TableName;
15
15
  } ? TableName : never;
16
+ type EnumToLiteralsTuple<T extends readonly [string, ...string[]]> = T['length'] extends 1 ? [VLiteral<T[0], 'required'>] : T['length'] extends 2 ? [VLiteral<T[0], 'required'>, VLiteral<T[1], 'required'>] : [
17
+ VLiteral<T[0], 'required'>,
18
+ VLiteral<T[1], 'required'>,
19
+ ...{
20
+ [K in keyof T]: K extends '0' | '1' ? never : K extends keyof T ? VLiteral<T[K], 'required'> : never;
21
+ }[keyof T & number][]
22
+ ];
16
23
  type ZodValidator = Record<string, z.ZodTypeAny>;
17
24
  type ConvexValidatorFromZodRequired<Z extends z.ZodTypeAny> = Z extends z.ZodOptional<infer T extends z.ZodTypeAny> ? VUnion<z.infer<T> | null, any[], 'required'> : ConvexValidatorFromZodBase<Z>;
18
- type ConvexValidatorFromZodBase<Z extends z.ZodTypeAny> = IsZid<Z> extends true ? ExtractTableName<Z> extends infer TableName extends string ? VId<GenericId<TableName>, 'required'> : VAny<'required'> : Z extends z.ZodString ? VString<z.infer<Z>, 'required'> : Z extends z.ZodNumber ? VFloat64<z.infer<Z>, 'required'> : Z extends z.ZodDate ? VFloat64<number, 'required'> : Z extends z.ZodBigInt ? VInt64<z.infer<Z>, 'required'> : Z extends z.ZodBoolean ? VBoolean<z.infer<Z>, 'required'> : Z extends z.ZodNull ? VNull<null, 'required'> : Z extends z.ZodArray<infer T extends z.ZodTypeAny> ? VArray<z.infer<Z>, ConvexValidatorFromZodRequired<T>, 'required'> : Z extends z.ZodObject<infer T> ? VObject<z.infer<Z>, ConvexValidatorFromZodFieldsAuto<T>, 'required', string> : Z extends z.ZodUnion<infer T> ? T extends readonly [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]] ? VUnion<z.infer<Z>, any[], 'required'> : never : Z extends z.ZodLiteral<infer T> ? VLiteral<T, 'required'> : Z extends z.ZodEnum<infer T> ? T extends Readonly<Record<string, infer _V>> ? T[keyof T] extends infer Values ? VUnion<Values, any[], 'required'> : never : never : Z extends z.ZodRecord<z.ZodString, infer V extends z.ZodTypeAny> ? VRecord<Record<string, z.infer<V>>, VString<string, 'required'>, ConvexValidatorFromZodRequired<V>, 'required', string> : Z extends z.ZodNullable<infer Inner extends z.ZodTypeAny> ? Inner extends z.ZodOptional<infer InnerInner extends z.ZodTypeAny> ? VOptional<VUnion<z.infer<InnerInner> | null, [
25
+ type ConvexValidatorFromZodBase<Z extends z.ZodTypeAny> = IsZid<Z> extends true ? ExtractTableName<Z> extends infer TableName extends string ? VId<GenericId<TableName>, 'required'> : VAny<'required'> : Z extends z.ZodString ? VString<z.infer<Z>, 'required'> : Z extends z.ZodNumber ? VFloat64<z.infer<Z>, 'required'> : Z extends z.ZodDate ? VFloat64<number, 'required'> : Z extends z.ZodBigInt ? VInt64<z.infer<Z>, 'required'> : Z extends z.ZodBoolean ? VBoolean<z.infer<Z>, 'required'> : Z extends z.ZodNull ? VNull<null, 'required'> : Z extends z.ZodArray<infer T extends z.ZodTypeAny> ? VArray<z.infer<Z>, ConvexValidatorFromZodRequired<T>, 'required'> : Z extends z.ZodObject<infer T> ? VObject<z.infer<Z>, ConvexValidatorFromZodFieldsAuto<T>, 'required', string> : Z extends z.ZodUnion<infer T> ? T extends readonly [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]] ? VUnion<z.infer<Z>, any[], 'required'> : never : Z extends z.ZodLiteral<infer T> ? VLiteral<T, 'required'> : Z extends z.ZodEnum<infer T> ? T extends readonly [string, ...string[]] ? T['length'] extends 1 ? VLiteral<T[0], 'required'> : T['length'] extends 2 ? VUnion<T[number], [
26
+ VLiteral<T[0], 'required'>,
27
+ VLiteral<T[1], 'required'>
28
+ ], 'required', never> : VUnion<T[number], EnumToLiteralsTuple<T>, 'required', never> : T extends Record<string, string | number> ? VUnion<T[keyof T], Array<VLiteral<T[keyof T], 'required'>>, 'required', never> : VUnion<string, any[], 'required', any> : Z extends z.ZodRecord<z.ZodString, infer V extends z.ZodTypeAny> ? VRecord<Record<string, z.infer<V>>, VString<string, 'required'>, ConvexValidatorFromZodRequired<V>, 'required', string> : Z extends z.ZodNullable<infer Inner extends z.ZodTypeAny> ? Inner extends z.ZodOptional<infer InnerInner extends z.ZodTypeAny> ? VOptional<VUnion<z.infer<InnerInner> | null, [
19
29
  ConvexValidatorFromZodBase<InnerInner>,
20
30
  VNull<null, 'required'>
21
31
  ], 'required'>> : VUnion<z.infer<Inner> | null, [
22
32
  ConvexValidatorFromZodBase<Inner>,
23
33
  VNull<null, 'required'>
24
34
  ], 'required'> : Z extends z.ZodAny ? VAny<'required'> : Z extends z.ZodUnknown ? VAny<'required'> : VAny<'required'>;
25
- type ConvexValidatorFromZod<Z extends z.ZodTypeAny, Constraint extends 'required' | 'optional' = 'required'> = Z extends z.ZodAny ? VAny<'required'> : Z extends z.ZodUnknown ? VAny<'required'> : Z extends z.ZodDefault<infer T extends z.ZodTypeAny> ? ConvexValidatorFromZod<T, Constraint> : Z extends z.ZodOptional<infer T extends z.ZodTypeAny> ? T extends z.ZodNullable<infer Inner extends z.ZodTypeAny> ? VOptional<VUnion<z.infer<Inner> | null, any[], 'required'>> : Constraint extends 'required' ? VUnion<z.infer<T>, any[], 'required'> : VOptional<ConvexValidatorFromZod<T, 'required'>> : Z extends z.ZodNullable<infer T extends z.ZodTypeAny> ? VUnion<z.infer<T> | null, any[], Constraint> : IsZid<Z> extends true ? ExtractTableName<Z> extends infer TableName extends string ? VId<GenericId<TableName>, Constraint> : VAny<'required'> : Z extends z.ZodString ? VString<z.infer<Z>, Constraint> : Z extends z.ZodNumber ? VFloat64<z.infer<Z>, Constraint> : Z extends z.ZodDate ? VFloat64<number, Constraint> : Z extends z.ZodBigInt ? VInt64<z.infer<Z>, Constraint> : Z extends z.ZodBoolean ? VBoolean<z.infer<Z>, Constraint> : Z extends z.ZodNull ? VNull<null, Constraint> : Z extends z.ZodArray<infer T extends z.ZodTypeAny> ? VArray<z.infer<Z>, ConvexValidatorFromZodRequired<T>, Constraint> : Z extends z.ZodObject<infer T> ? VObject<z.infer<Z>, ConvexValidatorFromZodFields<T, 'required'>, Constraint, string> : Z extends z.ZodUnion<infer T> ? T extends readonly [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]] ? VUnion<z.infer<Z>, any[], Constraint> : never : Z extends z.ZodLiteral<infer T> ? VLiteral<T, Constraint> : Z extends z.ZodEnum<infer T> ? T extends Readonly<Record<string, infer _V>> ? T[keyof T] extends infer Values ? VUnion<Values, any[], Constraint> : never : never : Z extends z.ZodRecord<z.ZodString, infer V extends z.ZodTypeAny> ? VRecord<Record<string, z.infer<V>>, VString<string, 'required'>, ConvexValidatorFromZodRequired<V>, Constraint, string> : VAny<'required'>;
35
+ type ConvexValidatorFromZod<Z extends z.ZodTypeAny, Constraint extends 'required' | 'optional' = 'required'> = Z extends z.ZodAny ? VAny<'required'> : Z extends z.ZodUnknown ? VAny<'required'> : Z extends z.ZodDefault<infer T extends z.ZodTypeAny> ? ConvexValidatorFromZod<T, Constraint> : Z extends z.ZodOptional<infer T extends z.ZodTypeAny> ? T extends z.ZodNullable<infer Inner extends z.ZodTypeAny> ? VOptional<VUnion<z.infer<Inner> | null, any[], 'required'>> : Constraint extends 'required' ? VUnion<z.infer<T>, any[], 'required'> : VOptional<ConvexValidatorFromZod<T, 'required'>> : Z extends z.ZodNullable<infer T extends z.ZodTypeAny> ? VUnion<z.infer<T> | null, any[], Constraint> : IsZid<Z> extends true ? ExtractTableName<Z> extends infer TableName extends string ? VId<GenericId<TableName>, Constraint> : VAny<'required'> : Z extends z.ZodString ? VString<z.infer<Z>, Constraint> : Z extends z.ZodNumber ? VFloat64<z.infer<Z>, Constraint> : Z extends z.ZodDate ? VFloat64<number, Constraint> : Z extends z.ZodBigInt ? VInt64<z.infer<Z>, Constraint> : Z extends z.ZodBoolean ? VBoolean<z.infer<Z>, Constraint> : Z extends z.ZodNull ? VNull<null, Constraint> : Z extends z.ZodArray<infer T extends z.ZodTypeAny> ? VArray<z.infer<Z>, ConvexValidatorFromZodRequired<T>, Constraint> : Z extends z.ZodObject<infer T> ? VObject<z.infer<Z>, ConvexValidatorFromZodFields<T, 'required'>, Constraint, string> : Z extends z.ZodUnion<infer T> ? T extends readonly [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]] ? VUnion<z.infer<Z>, any[], Constraint> : never : Z extends z.ZodLiteral<infer T> ? VLiteral<T, Constraint> : Z extends z.ZodEnum<infer T> ? T extends readonly [string, ...string[]] ? T['length'] extends 1 ? VLiteral<T[0], Constraint> : T['length'] extends 2 ? VUnion<T[number], [
36
+ VLiteral<T[0], 'required'>,
37
+ VLiteral<T[1], 'required'>
38
+ ], Constraint, never> : VUnion<T[number], EnumToLiteralsTuple<T>, Constraint, never> : T extends Record<string, string | number> ? VUnion<T[keyof T], Array<VLiteral<T[keyof T], 'required'>>, Constraint, never> : VUnion<string, any[], Constraint, any> : Z extends z.ZodRecord<z.ZodString, infer V extends z.ZodTypeAny> ? VRecord<Record<string, z.infer<V>>, VString<string, 'required'>, ConvexValidatorFromZodRequired<V>, Constraint, string> : VAny<'required'>;
26
39
  type ConvexValidatorFromZodFields<T extends {
27
40
  [key: string]: any;
28
41
  }, Constraint extends 'required' | 'optional' = 'required'> = {
@@ -228,13 +241,13 @@ declare function zActionBuilder<Builder extends (fn: any) => any>(builder: Build
228
241
  *
229
242
  * @example
230
243
  * ```ts
231
- * import { query } from './_generated/server'
244
+ * import { type QueryCtx, query } from './_generated/server'
232
245
  * import { zCustomQueryBuilder, customCtx } from 'zodvex'
233
246
  *
234
247
  * // Create a builder with auth context
235
248
  * export const authQuery = zCustomQueryBuilder(
236
249
  * query,
237
- * customCtx(async (ctx) => {
250
+ * customCtx(async (ctx: QueryCtx) => {
238
251
  * const user = await getUserOrThrow(ctx)
239
252
  * return { user }
240
253
  * })
@@ -257,13 +270,13 @@ declare function zCustomQueryBuilder<Builder extends (fn: any) => any, CustomArg
257
270
  *
258
271
  * @example
259
272
  * ```ts
260
- * import { mutation } from './_generated/server'
273
+ * import { type MutationCtx, mutation } from './_generated/server'
261
274
  * import { zCustomMutationBuilder, customCtx } from 'zodvex'
262
275
  *
263
276
  * // Create a builder with auth context
264
277
  * export const authMutation = zCustomMutationBuilder(
265
278
  * mutation,
266
- * customCtx(async (ctx) => {
279
+ * customCtx(async (ctx: MutationCtx) => {
267
280
  * const user = await getUserOrThrow(ctx)
268
281
  * return { user }
269
282
  * })
@@ -286,13 +299,13 @@ declare function zCustomMutationBuilder<Builder extends (fn: any) => any, Custom
286
299
  *
287
300
  * @example
288
301
  * ```ts
289
- * import { action } from './_generated/server'
302
+ * import { type ActionCtx, action } from './_generated/server'
290
303
  * import { zCustomActionBuilder, customCtx } from 'zodvex'
291
304
  *
292
305
  * // Create a builder with auth context
293
306
  * export const authAction = zCustomActionBuilder(
294
307
  * action,
295
- * customCtx(async (ctx) => {
308
+ * customCtx(async (ctx: ActionCtx) => {
296
309
  * const identity = await ctx.auth.getUserIdentity()
297
310
  * if (!identity) throw new Error('Unauthorized')
298
311
  * return { userId: identity.subject }
package/dist/index.d.ts CHANGED
@@ -13,16 +13,29 @@ type IsZid<T> = T extends {
13
13
  type ExtractTableName<T> = T extends {
14
14
  _tableName: infer TableName;
15
15
  } ? TableName : never;
16
+ type EnumToLiteralsTuple<T extends readonly [string, ...string[]]> = T['length'] extends 1 ? [VLiteral<T[0], 'required'>] : T['length'] extends 2 ? [VLiteral<T[0], 'required'>, VLiteral<T[1], 'required'>] : [
17
+ VLiteral<T[0], 'required'>,
18
+ VLiteral<T[1], 'required'>,
19
+ ...{
20
+ [K in keyof T]: K extends '0' | '1' ? never : K extends keyof T ? VLiteral<T[K], 'required'> : never;
21
+ }[keyof T & number][]
22
+ ];
16
23
  type ZodValidator = Record<string, z.ZodTypeAny>;
17
24
  type ConvexValidatorFromZodRequired<Z extends z.ZodTypeAny> = Z extends z.ZodOptional<infer T extends z.ZodTypeAny> ? VUnion<z.infer<T> | null, any[], 'required'> : ConvexValidatorFromZodBase<Z>;
18
- type ConvexValidatorFromZodBase<Z extends z.ZodTypeAny> = IsZid<Z> extends true ? ExtractTableName<Z> extends infer TableName extends string ? VId<GenericId<TableName>, 'required'> : VAny<'required'> : Z extends z.ZodString ? VString<z.infer<Z>, 'required'> : Z extends z.ZodNumber ? VFloat64<z.infer<Z>, 'required'> : Z extends z.ZodDate ? VFloat64<number, 'required'> : Z extends z.ZodBigInt ? VInt64<z.infer<Z>, 'required'> : Z extends z.ZodBoolean ? VBoolean<z.infer<Z>, 'required'> : Z extends z.ZodNull ? VNull<null, 'required'> : Z extends z.ZodArray<infer T extends z.ZodTypeAny> ? VArray<z.infer<Z>, ConvexValidatorFromZodRequired<T>, 'required'> : Z extends z.ZodObject<infer T> ? VObject<z.infer<Z>, ConvexValidatorFromZodFieldsAuto<T>, 'required', string> : Z extends z.ZodUnion<infer T> ? T extends readonly [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]] ? VUnion<z.infer<Z>, any[], 'required'> : never : Z extends z.ZodLiteral<infer T> ? VLiteral<T, 'required'> : Z extends z.ZodEnum<infer T> ? T extends Readonly<Record<string, infer _V>> ? T[keyof T] extends infer Values ? VUnion<Values, any[], 'required'> : never : never : Z extends z.ZodRecord<z.ZodString, infer V extends z.ZodTypeAny> ? VRecord<Record<string, z.infer<V>>, VString<string, 'required'>, ConvexValidatorFromZodRequired<V>, 'required', string> : Z extends z.ZodNullable<infer Inner extends z.ZodTypeAny> ? Inner extends z.ZodOptional<infer InnerInner extends z.ZodTypeAny> ? VOptional<VUnion<z.infer<InnerInner> | null, [
25
+ type ConvexValidatorFromZodBase<Z extends z.ZodTypeAny> = IsZid<Z> extends true ? ExtractTableName<Z> extends infer TableName extends string ? VId<GenericId<TableName>, 'required'> : VAny<'required'> : Z extends z.ZodString ? VString<z.infer<Z>, 'required'> : Z extends z.ZodNumber ? VFloat64<z.infer<Z>, 'required'> : Z extends z.ZodDate ? VFloat64<number, 'required'> : Z extends z.ZodBigInt ? VInt64<z.infer<Z>, 'required'> : Z extends z.ZodBoolean ? VBoolean<z.infer<Z>, 'required'> : Z extends z.ZodNull ? VNull<null, 'required'> : Z extends z.ZodArray<infer T extends z.ZodTypeAny> ? VArray<z.infer<Z>, ConvexValidatorFromZodRequired<T>, 'required'> : Z extends z.ZodObject<infer T> ? VObject<z.infer<Z>, ConvexValidatorFromZodFieldsAuto<T>, 'required', string> : Z extends z.ZodUnion<infer T> ? T extends readonly [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]] ? VUnion<z.infer<Z>, any[], 'required'> : never : Z extends z.ZodLiteral<infer T> ? VLiteral<T, 'required'> : Z extends z.ZodEnum<infer T> ? T extends readonly [string, ...string[]] ? T['length'] extends 1 ? VLiteral<T[0], 'required'> : T['length'] extends 2 ? VUnion<T[number], [
26
+ VLiteral<T[0], 'required'>,
27
+ VLiteral<T[1], 'required'>
28
+ ], 'required', never> : VUnion<T[number], EnumToLiteralsTuple<T>, 'required', never> : T extends Record<string, string | number> ? VUnion<T[keyof T], Array<VLiteral<T[keyof T], 'required'>>, 'required', never> : VUnion<string, any[], 'required', any> : Z extends z.ZodRecord<z.ZodString, infer V extends z.ZodTypeAny> ? VRecord<Record<string, z.infer<V>>, VString<string, 'required'>, ConvexValidatorFromZodRequired<V>, 'required', string> : Z extends z.ZodNullable<infer Inner extends z.ZodTypeAny> ? Inner extends z.ZodOptional<infer InnerInner extends z.ZodTypeAny> ? VOptional<VUnion<z.infer<InnerInner> | null, [
19
29
  ConvexValidatorFromZodBase<InnerInner>,
20
30
  VNull<null, 'required'>
21
31
  ], 'required'>> : VUnion<z.infer<Inner> | null, [
22
32
  ConvexValidatorFromZodBase<Inner>,
23
33
  VNull<null, 'required'>
24
34
  ], 'required'> : Z extends z.ZodAny ? VAny<'required'> : Z extends z.ZodUnknown ? VAny<'required'> : VAny<'required'>;
25
- type ConvexValidatorFromZod<Z extends z.ZodTypeAny, Constraint extends 'required' | 'optional' = 'required'> = Z extends z.ZodAny ? VAny<'required'> : Z extends z.ZodUnknown ? VAny<'required'> : Z extends z.ZodDefault<infer T extends z.ZodTypeAny> ? ConvexValidatorFromZod<T, Constraint> : Z extends z.ZodOptional<infer T extends z.ZodTypeAny> ? T extends z.ZodNullable<infer Inner extends z.ZodTypeAny> ? VOptional<VUnion<z.infer<Inner> | null, any[], 'required'>> : Constraint extends 'required' ? VUnion<z.infer<T>, any[], 'required'> : VOptional<ConvexValidatorFromZod<T, 'required'>> : Z extends z.ZodNullable<infer T extends z.ZodTypeAny> ? VUnion<z.infer<T> | null, any[], Constraint> : IsZid<Z> extends true ? ExtractTableName<Z> extends infer TableName extends string ? VId<GenericId<TableName>, Constraint> : VAny<'required'> : Z extends z.ZodString ? VString<z.infer<Z>, Constraint> : Z extends z.ZodNumber ? VFloat64<z.infer<Z>, Constraint> : Z extends z.ZodDate ? VFloat64<number, Constraint> : Z extends z.ZodBigInt ? VInt64<z.infer<Z>, Constraint> : Z extends z.ZodBoolean ? VBoolean<z.infer<Z>, Constraint> : Z extends z.ZodNull ? VNull<null, Constraint> : Z extends z.ZodArray<infer T extends z.ZodTypeAny> ? VArray<z.infer<Z>, ConvexValidatorFromZodRequired<T>, Constraint> : Z extends z.ZodObject<infer T> ? VObject<z.infer<Z>, ConvexValidatorFromZodFields<T, 'required'>, Constraint, string> : Z extends z.ZodUnion<infer T> ? T extends readonly [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]] ? VUnion<z.infer<Z>, any[], Constraint> : never : Z extends z.ZodLiteral<infer T> ? VLiteral<T, Constraint> : Z extends z.ZodEnum<infer T> ? T extends Readonly<Record<string, infer _V>> ? T[keyof T] extends infer Values ? VUnion<Values, any[], Constraint> : never : never : Z extends z.ZodRecord<z.ZodString, infer V extends z.ZodTypeAny> ? VRecord<Record<string, z.infer<V>>, VString<string, 'required'>, ConvexValidatorFromZodRequired<V>, Constraint, string> : VAny<'required'>;
35
+ type ConvexValidatorFromZod<Z extends z.ZodTypeAny, Constraint extends 'required' | 'optional' = 'required'> = Z extends z.ZodAny ? VAny<'required'> : Z extends z.ZodUnknown ? VAny<'required'> : Z extends z.ZodDefault<infer T extends z.ZodTypeAny> ? ConvexValidatorFromZod<T, Constraint> : Z extends z.ZodOptional<infer T extends z.ZodTypeAny> ? T extends z.ZodNullable<infer Inner extends z.ZodTypeAny> ? VOptional<VUnion<z.infer<Inner> | null, any[], 'required'>> : Constraint extends 'required' ? VUnion<z.infer<T>, any[], 'required'> : VOptional<ConvexValidatorFromZod<T, 'required'>> : Z extends z.ZodNullable<infer T extends z.ZodTypeAny> ? VUnion<z.infer<T> | null, any[], Constraint> : IsZid<Z> extends true ? ExtractTableName<Z> extends infer TableName extends string ? VId<GenericId<TableName>, Constraint> : VAny<'required'> : Z extends z.ZodString ? VString<z.infer<Z>, Constraint> : Z extends z.ZodNumber ? VFloat64<z.infer<Z>, Constraint> : Z extends z.ZodDate ? VFloat64<number, Constraint> : Z extends z.ZodBigInt ? VInt64<z.infer<Z>, Constraint> : Z extends z.ZodBoolean ? VBoolean<z.infer<Z>, Constraint> : Z extends z.ZodNull ? VNull<null, Constraint> : Z extends z.ZodArray<infer T extends z.ZodTypeAny> ? VArray<z.infer<Z>, ConvexValidatorFromZodRequired<T>, Constraint> : Z extends z.ZodObject<infer T> ? VObject<z.infer<Z>, ConvexValidatorFromZodFields<T, 'required'>, Constraint, string> : Z extends z.ZodUnion<infer T> ? T extends readonly [z.ZodTypeAny, z.ZodTypeAny, ...z.ZodTypeAny[]] ? VUnion<z.infer<Z>, any[], Constraint> : never : Z extends z.ZodLiteral<infer T> ? VLiteral<T, Constraint> : Z extends z.ZodEnum<infer T> ? T extends readonly [string, ...string[]] ? T['length'] extends 1 ? VLiteral<T[0], Constraint> : T['length'] extends 2 ? VUnion<T[number], [
36
+ VLiteral<T[0], 'required'>,
37
+ VLiteral<T[1], 'required'>
38
+ ], Constraint, never> : VUnion<T[number], EnumToLiteralsTuple<T>, Constraint, never> : T extends Record<string, string | number> ? VUnion<T[keyof T], Array<VLiteral<T[keyof T], 'required'>>, Constraint, never> : VUnion<string, any[], Constraint, any> : Z extends z.ZodRecord<z.ZodString, infer V extends z.ZodTypeAny> ? VRecord<Record<string, z.infer<V>>, VString<string, 'required'>, ConvexValidatorFromZodRequired<V>, Constraint, string> : VAny<'required'>;
26
39
  type ConvexValidatorFromZodFields<T extends {
27
40
  [key: string]: any;
28
41
  }, Constraint extends 'required' | 'optional' = 'required'> = {
@@ -228,13 +241,13 @@ declare function zActionBuilder<Builder extends (fn: any) => any>(builder: Build
228
241
  *
229
242
  * @example
230
243
  * ```ts
231
- * import { query } from './_generated/server'
244
+ * import { type QueryCtx, query } from './_generated/server'
232
245
  * import { zCustomQueryBuilder, customCtx } from 'zodvex'
233
246
  *
234
247
  * // Create a builder with auth context
235
248
  * export const authQuery = zCustomQueryBuilder(
236
249
  * query,
237
- * customCtx(async (ctx) => {
250
+ * customCtx(async (ctx: QueryCtx) => {
238
251
  * const user = await getUserOrThrow(ctx)
239
252
  * return { user }
240
253
  * })
@@ -257,13 +270,13 @@ declare function zCustomQueryBuilder<Builder extends (fn: any) => any, CustomArg
257
270
  *
258
271
  * @example
259
272
  * ```ts
260
- * import { mutation } from './_generated/server'
273
+ * import { type MutationCtx, mutation } from './_generated/server'
261
274
  * import { zCustomMutationBuilder, customCtx } from 'zodvex'
262
275
  *
263
276
  * // Create a builder with auth context
264
277
  * export const authMutation = zCustomMutationBuilder(
265
278
  * mutation,
266
- * customCtx(async (ctx) => {
279
+ * customCtx(async (ctx: MutationCtx) => {
267
280
  * const user = await getUserOrThrow(ctx)
268
281
  * return { user }
269
282
  * })
@@ -286,13 +299,13 @@ declare function zCustomMutationBuilder<Builder extends (fn: any) => any, Custom
286
299
  *
287
300
  * @example
288
301
  * ```ts
289
- * import { action } from './_generated/server'
302
+ * import { type ActionCtx, action } from './_generated/server'
290
303
  * import { zCustomActionBuilder, customCtx } from 'zodvex'
291
304
  *
292
305
  * // Create a builder with auth context
293
306
  * export const authAction = zCustomActionBuilder(
294
307
  * action,
295
- * customCtx(async (ctx) => {
308
+ * customCtx(async (ctx: ActionCtx) => {
296
309
  * const identity = await ctx.auth.getUserIdentity()
297
310
  * if (!identity) throw new Error('Unauthorized')
298
311
  * return { userId: identity.subject }
package/dist/index.js CHANGED
@@ -146,7 +146,10 @@ function convertDiscriminatedUnionType(actualValidator, visited, zodToConvexInte
146
146
  if (options) {
147
147
  const opts = Array.isArray(options) ? options : Array.from(options);
148
148
  if (opts.length >= 2) {
149
- const convexOptions = opts.map((opt) => zodToConvexInternal2(opt, visited));
149
+ const convexOptions = opts.map((opt) => {
150
+ const branchVisited = new Set(visited);
151
+ return zodToConvexInternal2(opt, branchVisited);
152
+ });
150
153
  const [first, second, ...rest] = convexOptions;
151
154
  return values.v.union(
152
155
  first,
@@ -166,9 +169,10 @@ function convertUnionType(actualValidator, visited, zodToConvexInternal2) {
166
169
  if (options.length === 1) {
167
170
  return zodToConvexInternal2(options[0], visited);
168
171
  } else {
169
- const convexOptions = options.map(
170
- (opt) => zodToConvexInternal2(opt, visited)
171
- );
172
+ const convexOptions = options.map((opt) => {
173
+ const branchVisited = new Set(visited);
174
+ return zodToConvexInternal2(opt, branchVisited);
175
+ });
172
176
  if (convexOptions.length >= 2) {
173
177
  const [first, second, ...rest] = convexOptions;
174
178
  return values.v.union(
@@ -724,7 +728,11 @@ function customFnBuilder(builder, customization) {
724
728
  handleZodValidationError(e, "returns");
725
729
  }
726
730
  if (added?.onSuccess) {
727
- await added.onSuccess({ ctx, args: parsed.data, result: validated });
731
+ await added.onSuccess({
732
+ ctx,
733
+ args: parsed.data,
734
+ result: validated
735
+ });
728
736
  }
729
737
  return toConvexJS(returns, validated);
730
738
  }