zodvex 0.2.0 → 0.2.2

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.
@@ -0,0 +1,310 @@
1
+ import type {
2
+ FunctionVisibility,
3
+ RegisteredAction,
4
+ RegisteredMutation,
5
+ RegisteredQuery
6
+ } from 'convex/server'
7
+ import type { PropertyValidators } from 'convex/values'
8
+ import type { Customization } from 'convex-helpers/server/customFunctions'
9
+ import { z } from 'zod'
10
+ import { type CustomBuilder, customFnBuilder } from './custom'
11
+ import type {
12
+ ExtractCtx,
13
+ ExtractVisibility,
14
+ InferHandlerReturns,
15
+ InferReturns,
16
+ ZodToConvexArgs
17
+ } from './types'
18
+ import { zAction, zMutation, zQuery } from './wrappers'
19
+
20
+ /**
21
+ * Creates a reusable query builder from a Convex query builder.
22
+ * Returns a builder function that accepts Convex-style config objects with args, handler, and returns.
23
+ *
24
+ * @example
25
+ * ```ts
26
+ * import { query } from './_generated/server'
27
+ * import { zQueryBuilder } from 'zodvex'
28
+ *
29
+ * // Create a reusable builder
30
+ * export const zq = zQueryBuilder(query)
31
+ *
32
+ * // Use it with Convex-style object syntax
33
+ * export const getUser = zq({
34
+ * args: { id: z.string() },
35
+ * handler: async (ctx, { id }) => {
36
+ * return ctx.db.get(id)
37
+ * }
38
+ * })
39
+ * ```
40
+ */
41
+ export function zQueryBuilder<Builder extends (fn: any) => any>(builder: Builder) {
42
+ return <
43
+ A extends z.ZodTypeAny | Record<string, z.ZodTypeAny>,
44
+ R extends z.ZodTypeAny | undefined = undefined,
45
+ Visibility extends FunctionVisibility = ExtractVisibility<Builder>
46
+ >(config: {
47
+ args?: A
48
+ handler: (
49
+ ctx: ExtractCtx<Builder>,
50
+ args: ZodToConvexArgs<A extends undefined ? Record<string, never> : A>
51
+ ) => InferHandlerReturns<R> | Promise<InferHandlerReturns<R>>
52
+ returns?: R
53
+ }): RegisteredQuery<
54
+ Visibility,
55
+ ZodToConvexArgs<A extends undefined ? Record<string, never> : A>,
56
+ Promise<InferReturns<R>>
57
+ > => {
58
+ return zQuery(builder, config.args ?? ({} as any), config.handler, {
59
+ returns: config.returns
60
+ }) as any
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Creates a reusable mutation builder from a Convex mutation builder.
66
+ * Returns a builder function that accepts Convex-style config objects with args, handler, and returns.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * import { mutation } from './_generated/server'
71
+ * import { zMutationBuilder } from 'zodvex'
72
+ *
73
+ * // Create a reusable builder
74
+ * export const zm = zMutationBuilder(mutation)
75
+ *
76
+ * // Use it with Convex-style object syntax
77
+ * export const updateUser = zm({
78
+ * args: { id: z.string(), name: z.string() },
79
+ * handler: async (ctx, { id, name }) => {
80
+ * return ctx.db.patch(id, { name })
81
+ * }
82
+ * })
83
+ * ```
84
+ */
85
+ export function zMutationBuilder<Builder extends (fn: any) => any>(builder: Builder) {
86
+ return <
87
+ A extends z.ZodTypeAny | Record<string, z.ZodTypeAny>,
88
+ R extends z.ZodTypeAny | undefined = undefined,
89
+ Visibility extends FunctionVisibility = ExtractVisibility<Builder>
90
+ >(config: {
91
+ args?: A
92
+ handler: (
93
+ ctx: ExtractCtx<Builder>,
94
+ args: ZodToConvexArgs<A extends undefined ? Record<string, never> : A>
95
+ ) => InferHandlerReturns<R> | Promise<InferHandlerReturns<R>>
96
+ returns?: R
97
+ }): RegisteredMutation<
98
+ Visibility,
99
+ ZodToConvexArgs<A extends undefined ? Record<string, never> : A>,
100
+ Promise<InferReturns<R>>
101
+ > => {
102
+ return zMutation(builder, config.args ?? ({} as any), config.handler, {
103
+ returns: config.returns
104
+ }) as any
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Creates a reusable action builder from a Convex action builder.
110
+ * Returns a builder function that accepts Convex-style config objects with args, handler, and returns.
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * import { action } from './_generated/server'
115
+ * import { zActionBuilder } from 'zodvex'
116
+ *
117
+ * // Create a reusable builder
118
+ * export const za = zActionBuilder(action)
119
+ *
120
+ * // Use it with Convex-style object syntax
121
+ * export const sendEmail = za({
122
+ * args: { to: z.string().email(), subject: z.string() },
123
+ * handler: async (ctx, { to, subject }) => {
124
+ * // Send email
125
+ * }
126
+ * })
127
+ * ```
128
+ */
129
+ export function zActionBuilder<Builder extends (fn: any) => any>(builder: Builder) {
130
+ return <
131
+ A extends z.ZodTypeAny | Record<string, z.ZodTypeAny>,
132
+ R extends z.ZodTypeAny | undefined = undefined,
133
+ Visibility extends FunctionVisibility = ExtractVisibility<Builder>
134
+ >(config: {
135
+ args?: A
136
+ handler: (
137
+ ctx: ExtractCtx<Builder>,
138
+ args: ZodToConvexArgs<A extends undefined ? Record<string, never> : A>
139
+ ) => InferHandlerReturns<R> | Promise<InferHandlerReturns<R>>
140
+ returns?: R
141
+ }): RegisteredAction<
142
+ Visibility,
143
+ ZodToConvexArgs<A extends undefined ? Record<string, never> : A>,
144
+ Promise<InferReturns<R>>
145
+ > => {
146
+ return zAction(builder, config.args ?? ({} as any), config.handler, {
147
+ returns: config.returns
148
+ }) as any
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Creates a custom query builder with context injection from a Convex query builder.
154
+ * Allows you to add custom context (like auth, permissions, etc.) to your queries.
155
+ *
156
+ * @example
157
+ * ```ts
158
+ * import { query } from './_generated/server'
159
+ * import { zCustomQueryBuilder, customCtx } from 'zodvex'
160
+ *
161
+ * // Create a builder with auth context
162
+ * export const authQuery = zCustomQueryBuilder(
163
+ * query,
164
+ * customCtx(async (ctx) => {
165
+ * const user = await getUserOrThrow(ctx)
166
+ * return { user }
167
+ * })
168
+ * )
169
+ *
170
+ * // Use it with automatic user injection
171
+ * export const getMyProfile = authQuery({
172
+ * args: {},
173
+ * handler: async (ctx) => {
174
+ * // ctx.user is automatically available
175
+ * return ctx.db.get(ctx.user._id)
176
+ * }
177
+ * })
178
+ * ```
179
+ */
180
+ export function zCustomQueryBuilder<
181
+ Builder extends (fn: any) => any,
182
+ CustomArgsValidator extends PropertyValidators,
183
+ CustomCtx extends Record<string, any>,
184
+ CustomMadeArgs extends Record<string, any>,
185
+ Visibility extends FunctionVisibility = ExtractVisibility<Builder>,
186
+ ExtraArgs extends Record<string, any> = Record<string, any>
187
+ >(
188
+ query: Builder,
189
+ customization: Customization<any, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>
190
+ ): CustomBuilder<
191
+ 'query',
192
+ CustomArgsValidator,
193
+ CustomCtx,
194
+ CustomMadeArgs,
195
+ ExtractCtx<Builder>,
196
+ Visibility,
197
+ ExtraArgs
198
+ > {
199
+ return customFnBuilder<any, Builder, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>(
200
+ query as any,
201
+ customization as any
202
+ ) as any
203
+ }
204
+
205
+ /**
206
+ * Creates a custom mutation builder with context injection from a Convex mutation builder.
207
+ * Allows you to add custom context (like auth, permissions, etc.) to your mutations.
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * import { mutation } from './_generated/server'
212
+ * import { zCustomMutationBuilder, customCtx } from 'zodvex'
213
+ *
214
+ * // Create a builder with auth context
215
+ * export const authMutation = zCustomMutationBuilder(
216
+ * mutation,
217
+ * customCtx(async (ctx) => {
218
+ * const user = await getUserOrThrow(ctx)
219
+ * return { user }
220
+ * })
221
+ * )
222
+ *
223
+ * // Use it with automatic user injection
224
+ * export const updateProfile = authMutation({
225
+ * args: { name: z.string() },
226
+ * handler: async (ctx, { name }) => {
227
+ * // ctx.user is automatically available
228
+ * await ctx.db.patch(ctx.user._id, { name })
229
+ * }
230
+ * })
231
+ * ```
232
+ */
233
+ export function zCustomMutationBuilder<
234
+ Builder extends (fn: any) => any,
235
+ CustomArgsValidator extends PropertyValidators,
236
+ CustomCtx extends Record<string, any>,
237
+ CustomMadeArgs extends Record<string, any>,
238
+ Visibility extends FunctionVisibility = ExtractVisibility<Builder>,
239
+ ExtraArgs extends Record<string, any> = Record<string, any>
240
+ >(
241
+ mutation: Builder,
242
+ customization: Customization<any, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>
243
+ ): CustomBuilder<
244
+ 'mutation',
245
+ CustomArgsValidator,
246
+ CustomCtx,
247
+ CustomMadeArgs,
248
+ ExtractCtx<Builder>,
249
+ Visibility,
250
+ ExtraArgs
251
+ > {
252
+ return customFnBuilder<any, Builder, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>(
253
+ mutation as any,
254
+ customization as any
255
+ ) as any
256
+ }
257
+
258
+ /**
259
+ * Creates a custom action builder with context injection from a Convex action builder.
260
+ * Allows you to add custom context (like auth, permissions, etc.) to your actions.
261
+ *
262
+ * @example
263
+ * ```ts
264
+ * import { action } from './_generated/server'
265
+ * import { zCustomActionBuilder, customCtx } from 'zodvex'
266
+ *
267
+ * // Create a builder with auth context
268
+ * export const authAction = zCustomActionBuilder(
269
+ * action,
270
+ * customCtx(async (ctx) => {
271
+ * const identity = await ctx.auth.getUserIdentity()
272
+ * if (!identity) throw new Error('Unauthorized')
273
+ * return { userId: identity.subject }
274
+ * })
275
+ * )
276
+ *
277
+ * // Use it with automatic auth injection
278
+ * export const sendEmail = authAction({
279
+ * args: { to: z.string().email() },
280
+ * handler: async (ctx, { to }) => {
281
+ * // ctx.userId is automatically available
282
+ * await sendEmailService(to, ctx.userId)
283
+ * }
284
+ * })
285
+ * ```
286
+ */
287
+ export function zCustomActionBuilder<
288
+ Builder extends (fn: any) => any,
289
+ CustomArgsValidator extends PropertyValidators,
290
+ CustomCtx extends Record<string, any>,
291
+ CustomMadeArgs extends Record<string, any>,
292
+ Visibility extends FunctionVisibility = ExtractVisibility<Builder>,
293
+ ExtraArgs extends Record<string, any> = Record<string, any>
294
+ >(
295
+ action: Builder,
296
+ customization: Customization<any, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>
297
+ ): CustomBuilder<
298
+ 'action',
299
+ CustomArgsValidator,
300
+ CustomCtx,
301
+ CustomMadeArgs,
302
+ ExtractCtx<Builder>,
303
+ Visibility,
304
+ ExtraArgs
305
+ > {
306
+ return customFnBuilder<any, Builder, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>(
307
+ action as any,
308
+ customization as any
309
+ ) as any
310
+ }
package/src/custom.ts CHANGED
@@ -162,7 +162,7 @@ export type CustomBuilder<
162
162
  >
163
163
  }
164
164
 
165
- function customFnBuilder<
165
+ export function customFnBuilder<
166
166
  Ctx extends Record<string, any>,
167
167
  Builder extends (fn: any) => any,
168
168
  CustomArgsValidator extends PropertyValidators,
@@ -220,10 +220,9 @@ function customFnBuilder<
220
220
  handleZodValidationError(parsed.error, 'args')
221
221
  }
222
222
  const finalCtx = { ...ctx, ...(added?.ctx ?? {}) }
223
- const finalArgs = {
224
- ...(parsed.data as Record<string, unknown>),
225
- ...((added?.args as Record<string, unknown>) ?? {})
226
- }
223
+ const baseArgs = parsed.data as Record<string, unknown>
224
+ const addedArgs = (added?.args as Record<string, unknown>) ?? {}
225
+ const finalArgs = { ...baseArgs, ...addedArgs }
227
226
  const ret = await handler(finalCtx, finalArgs)
228
227
  if (returns && !fn.skipConvexValidation) {
229
228
  let validated: any
@@ -343,85 +342,6 @@ export function zCustomQuery<
343
342
  >(query as any, customization as any) as any
344
343
  }
345
344
 
346
- // Strict variants: infer ctx directly from builder for precise db typing
347
- export function zStrictQuery<
348
- Builder extends (fn: any) => any,
349
- CustomArgsValidator extends PropertyValidators,
350
- CustomCtx extends Record<string, any>,
351
- CustomMadeArgs extends Record<string, any>,
352
- Visibility extends FunctionVisibility = ExtractVisibility<Builder>,
353
- ExtraArgs extends Record<string, any> = Record<string, any>
354
- >(
355
- query: Builder,
356
- // Note: Customization from convex-helpers requires Ctx extends Record<string, any>.
357
- // We accept `any` here to avoid overly constraining Convex Ctx types while
358
- // still threading the precise ctx type to the resulting builder.
359
- customization: Customization<any, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>
360
- ): CustomBuilder<
361
- 'query',
362
- CustomArgsValidator,
363
- CustomCtx,
364
- CustomMadeArgs,
365
- ExtractCtx<Builder>,
366
- Visibility,
367
- ExtraArgs
368
- > {
369
- return customFnBuilder<any, Builder, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>(
370
- query as any,
371
- customization as any
372
- ) as any
373
- }
374
-
375
- export function zStrictMutation<
376
- Builder extends (fn: any) => any,
377
- CustomArgsValidator extends PropertyValidators,
378
- CustomCtx extends Record<string, any>,
379
- CustomMadeArgs extends Record<string, any>,
380
- Visibility extends FunctionVisibility = ExtractVisibility<Builder>,
381
- ExtraArgs extends Record<string, any> = Record<string, any>
382
- >(
383
- mutation: Builder,
384
- customization: Customization<any, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>
385
- ): CustomBuilder<
386
- 'mutation',
387
- CustomArgsValidator,
388
- CustomCtx,
389
- CustomMadeArgs,
390
- ExtractCtx<Builder>,
391
- Visibility,
392
- ExtraArgs
393
- > {
394
- return customFnBuilder<any, Builder, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>(
395
- mutation as any,
396
- customization as any
397
- ) as any
398
- }
399
-
400
- export function zStrictAction<
401
- Builder extends (fn: any) => any,
402
- CustomArgsValidator extends PropertyValidators,
403
- CustomCtx extends Record<string, any>,
404
- CustomMadeArgs extends Record<string, any>,
405
- Visibility extends FunctionVisibility = ExtractVisibility<Builder>,
406
- ExtraArgs extends Record<string, any> = Record<string, any>
407
- >(
408
- action: Builder,
409
- customization: Customization<any, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>
410
- ): CustomBuilder<
411
- 'action',
412
- CustomArgsValidator,
413
- CustomCtx,
414
- CustomMadeArgs,
415
- ExtractCtx<Builder>,
416
- Visibility,
417
- ExtraArgs
418
- > {
419
- return customFnBuilder<any, Builder, CustomArgsValidator, CustomCtx, CustomMadeArgs, ExtraArgs>(
420
- action as any,
421
- customization as any
422
- ) as any
423
- }
424
-
425
345
  // Overload 1: With constraint - preferred to preserve DataModel types
426
346
  export function zCustomMutation<
427
347
  CustomArgsValidator extends PropertyValidators,
package/src/index.ts CHANGED
@@ -1,3 +1,6 @@
1
+ // Re-export customCtx from convex-helpers for convenience
2
+ export { customCtx } from 'convex-helpers/server/customFunctions'
3
+ export * from './builders'
1
4
  export * from './codec'
2
5
  export * from './custom'
3
6
  export * from './ids'
@@ -6,4 +9,4 @@ export * from './registry'
6
9
  export * from './tables'
7
10
  export * from './types'
8
11
  export * from './utils'
9
- export * from './wrappers'
12
+ // wrappers are internal-only - use builders instead
@@ -22,6 +22,11 @@ function zodToConvexInternal<Z extends z.ZodTypeAny>(
22
22
  zodValidator: Z,
23
23
  visited: Set<z.ZodTypeAny> = new Set()
24
24
  ): ConvexValidatorFromZod<Z, 'required'> {
25
+ // Guard against undefined/null validators (can happen with { field: undefined } in args)
26
+ if (!zodValidator) {
27
+ return v.any() as ConvexValidatorFromZod<Z, 'required'>
28
+ }
29
+
25
30
  // Detect circular references to prevent infinite recursion
26
31
  if (visited.has(zodValidator)) {
27
32
  return v.any() as ConvexValidatorFromZod<Z, 'required'>
@@ -72,6 +77,10 @@ function zodToConvexInternal<Z extends z.ZodTypeAny>(
72
77
  // 3. Future-proof: Zod's internal structure is stable; instanceof checks can miss custom types
73
78
  // 4. Precision: def.type distinguishes between semantically different types (date vs number)
74
79
  // This private API access is intentional and necessary for comprehensive type coverage.
80
+ //
81
+ // Compatibility: This code relies on the internal `.def.type` property of ZodType.
82
+ // This structure has been stable across Zod v3.x and v4.x. If upgrading Zod major versions,
83
+ // verify that `.def.type` is still present and unchanged.
75
84
  const defType = (actualValidator as any).def?.type
76
85
 
77
86
  switch (defType) {
@@ -268,6 +277,13 @@ function zodToConvexInternal<Z extends z.ZodTypeAny>(
268
277
  default:
269
278
  // For any unrecognized def.type, return v.any()
270
279
  // No instanceof fallbacks - keep it simple and performant
280
+ if (process.env.NODE_ENV !== 'production') {
281
+ console.warn(
282
+ `[zodvex] Unrecognized Zod type "${defType}" encountered. Falling back to v.any().`,
283
+ 'Schema:',
284
+ actualValidator
285
+ )
286
+ }
271
287
  convexValidator = v.any()
272
288
  break
273
289
  }
package/src/tables.ts CHANGED
@@ -2,8 +2,7 @@ import type { GenericId } from 'convex/values'
2
2
  import { Table } from 'convex-helpers/server'
3
3
  import { z } from 'zod'
4
4
  import { zid } from './ids'
5
- import { type ConvexValidatorFromZodFieldsAuto, getObjectShape, zodToConvexFields } from './mapping'
6
- import { mapDateFieldToNumber } from './utils'
5
+ import { type ConvexValidatorFromZodFieldsAuto, zodToConvexFields } from './mapping'
7
6
 
8
7
  // Helper to create a Zod schema for a Convex document
9
8
  export function zodDoc<
@@ -44,7 +43,29 @@ export function zodDocOrNull<
44
43
  * This architectural decision is important for projects that rely heavily on type safety and
45
44
  * developer experience, as it avoids the type erasure that can occur when using ZodObject directly.
46
45
  *
47
- * Returns both the Table and the shape for use with zCrud.
46
+ * Returns the Table definition along with Zod schemas for documents and arrays.
47
+ *
48
+ * @param name - The table name
49
+ * @param shape - A raw object mapping field names to Zod validators
50
+ * @returns A Table with attached shape, zDoc schema, and docArray helper
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * const Users = zodTable('users', {
55
+ * name: z.string(),
56
+ * email: z.string().email(),
57
+ * age: z.number().optional()
58
+ * })
59
+ *
60
+ * // Use in schema
61
+ * export default defineSchema({ users: Users.table })
62
+ *
63
+ * // Use for return types
64
+ * export const getUsers = zQuery(query, {},
65
+ * async (ctx) => ctx.db.query('users').collect(),
66
+ * { returns: Users.docArray }
67
+ * )
68
+ * ```
48
69
  */
49
70
  export function zodTable<TableName extends string, Shape extends Record<string, z.ZodTypeAny>>(
50
71
  name: TableName,
@@ -56,10 +77,17 @@ export function zodTable<TableName extends string, Shape extends Record<string,
56
77
  // Create the Table from convex-helpers with explicit type
57
78
  const table = Table<ConvexValidatorFromZodFieldsAuto<Shape>, TableName>(name, convexFields)
58
79
 
59
- // Attach the shape for zCrud usage
80
+ // Create zDoc schema with system fields
81
+ const zDoc = zodDoc(name, z.object(shape))
82
+
83
+ // Create docArray helper for return types
84
+ const docArray = z.array(zDoc)
85
+
86
+ // Attach everything for comprehensive usage
60
87
  return Object.assign(table, {
61
88
  shape,
62
- zDoc: zodDoc(name, z.object(shape))
89
+ zDoc,
90
+ docArray
63
91
  }) as typeof table & {
64
92
  shape: Shape
65
93
  zDoc: z.ZodObject<
@@ -68,32 +96,6 @@ export function zodTable<TableName extends string, Shape extends Record<string,
68
96
  _creationTime: z.ZodNumber
69
97
  }
70
98
  >
99
+ docArray: z.ZodArray<typeof zDoc>
71
100
  }
72
101
  }
73
-
74
- // Keep the old implementation available for backward compatibility
75
- export function zodTableWithDocs<T extends z.ZodObject<any>, TableName extends string>(
76
- name: TableName,
77
- schema: T
78
- ) {
79
- // Use zodToConvexFields with proper types - pass the shape for type preservation
80
- const convexFields = zodToConvexFields(schema.shape)
81
-
82
- // Simplified: only convert dates at top level to avoid deep recursion
83
- const shape = getObjectShape(schema)
84
- const mapped: Record<string, any> = {}
85
-
86
- for (const [k, field] of Object.entries(shape)) {
87
- mapped[k] = mapDateFieldToNumber(field as z.ZodTypeAny)
88
- }
89
- const docSchema = z.object({
90
- ...mapped,
91
- _id: zid(name),
92
- _creationTime: z.number()
93
- })
94
- const docArray = z.array(docSchema)
95
-
96
- const base = Table(name, convexFields)
97
- // Return with docSchema and docArray for backward compatibility
98
- return { ...base, schema, docSchema, docArray }
99
- }
package/src/utils.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { ConvexError } from 'convex/values'
2
2
  import { z } from 'zod'
3
+ import { getObjectShape } from './mapping'
3
4
 
4
5
  export function pick<T extends Record<string, any>, K extends keyof T>(
5
6
  obj: T,
@@ -97,15 +98,15 @@ function toKeys(mask: Mask): string[] {
97
98
  return Object.keys(mask).filter(k => !!(mask as any)[k])
98
99
  }
99
100
 
100
- // Returns a plain shape object containing only the selected fields.
101
- // Accepts either a ZodObject or a raw shape object.
101
+ /**
102
+ * Returns a plain shape object containing only the selected fields.
103
+ * Accepts either a ZodObject or a raw shape object.
104
+ */
102
105
  export function pickShape(
103
106
  schemaOrShape: z.ZodObject<any> | Record<string, any>,
104
107
  mask: Mask
105
108
  ): Record<string, any> {
106
109
  const keys = toKeys(mask)
107
- // Import getObjectShape lazily to avoid circular dependency
108
- const { getObjectShape } = require('./mapping')
109
110
  const shape =
110
111
  schemaOrShape instanceof z.ZodObject ? getObjectShape(schemaOrShape) : schemaOrShape || {}
111
112
 
@@ -121,9 +122,11 @@ export function safePick(schema: z.ZodObject<any>, mask: Mask): z.ZodObject<any>
121
122
  return z.object(pickShape(schema, mask))
122
123
  }
123
124
 
124
- // Convenience: omit a set of keys by building the complement
125
+ /**
126
+ * Convenience: omit a set of keys by building the complement.
127
+ * Avoids using Zod's .omit() which can cause type depth issues.
128
+ */
125
129
  export function safeOmit(schema: z.ZodObject<any>, mask: Mask): z.ZodObject<any> {
126
- const { getObjectShape } = require('./mapping')
127
130
  const shape = getObjectShape(schema)
128
131
  const omit = new Set(toKeys(mask))
129
132
  const keep = Object.keys(shape).filter(k => !omit.has(k))