zodvex 0.2.4 → 0.3.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/README.md +319 -7
- package/dist/index.d.ts +159 -66
- package/dist/index.js +273 -209
- package/dist/index.js.map +1 -1
- package/package.json +13 -24
- package/src/__type-tests__/infer-returns.ts +154 -0
- package/src/custom.ts +15 -6
- package/src/ids.ts +8 -8
- package/src/registry.ts +171 -0
- package/src/tables.ts +173 -30
- package/src/types.ts +10 -22
- package/src/wrappers.ts +2 -2
- package/dist/index.d.mts +0 -489
- package/dist/index.mjs +0 -1001
- package/dist/index.mjs.map +0 -1
package/src/tables.ts
CHANGED
|
@@ -1,8 +1,50 @@
|
|
|
1
|
+
import { defineTable } from 'convex/server'
|
|
1
2
|
import type { GenericId } from 'convex/values'
|
|
2
3
|
import { Table } from 'convex-helpers/server'
|
|
3
4
|
import { z } from 'zod'
|
|
4
5
|
import { zid } from './ids'
|
|
5
|
-
import { type ConvexValidatorFromZodFieldsAuto, zodToConvexFields } from './mapping'
|
|
6
|
+
import { type ConvexValidatorFromZodFieldsAuto, zodToConvex, zodToConvexFields } from './mapping'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Adds Convex system fields (_id, _creationTime) to a Zod schema.
|
|
10
|
+
*
|
|
11
|
+
* For object schemas: extends with system fields
|
|
12
|
+
* For union schemas: adds system fields to each variant
|
|
13
|
+
*
|
|
14
|
+
* @param tableName - The Convex table name
|
|
15
|
+
* @param schema - The Zod schema (object or union)
|
|
16
|
+
* @returns Schema with system fields added
|
|
17
|
+
*/
|
|
18
|
+
export function addSystemFields<T extends string, S extends z.ZodTypeAny>(
|
|
19
|
+
tableName: T,
|
|
20
|
+
schema: S
|
|
21
|
+
): z.ZodTypeAny {
|
|
22
|
+
// Handle union schemas - add system fields to each variant
|
|
23
|
+
if (schema instanceof z.ZodUnion || schema instanceof z.ZodDiscriminatedUnion) {
|
|
24
|
+
const options = (schema as z.ZodUnion<any>).options.map((variant: z.ZodTypeAny) => {
|
|
25
|
+
if (variant instanceof z.ZodObject) {
|
|
26
|
+
return variant.extend({
|
|
27
|
+
_id: zid(tableName),
|
|
28
|
+
_creationTime: z.number()
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
// Non-object variants are returned as-is (shouldn't happen in practice)
|
|
32
|
+
return variant
|
|
33
|
+
})
|
|
34
|
+
return z.union(options as any)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Handle object schemas
|
|
38
|
+
if (schema instanceof z.ZodObject) {
|
|
39
|
+
return schema.extend({
|
|
40
|
+
_id: zid(tableName),
|
|
41
|
+
_creationTime: z.number()
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Fallback: return schema as-is
|
|
46
|
+
return schema
|
|
47
|
+
}
|
|
6
48
|
|
|
7
49
|
// Helper to create a Zod schema for a Convex document
|
|
8
50
|
export function zodDoc<
|
|
@@ -35,21 +77,43 @@ export function zodDocOrNull<
|
|
|
35
77
|
}
|
|
36
78
|
|
|
37
79
|
/**
|
|
38
|
-
*
|
|
80
|
+
* Helper to detect if input is an object shape (plain object with Zod validators)
|
|
81
|
+
*/
|
|
82
|
+
function isObjectShape(input: any): input is Record<string, z.ZodTypeAny> {
|
|
83
|
+
// Check if it's a plain object (not a Zod instance)
|
|
84
|
+
if (!input || typeof input !== 'object') return false
|
|
85
|
+
|
|
86
|
+
// If it's a Zod instance, it's not an object shape
|
|
87
|
+
if (input instanceof z.ZodType) return false
|
|
88
|
+
|
|
89
|
+
// Check if all values are Zod types
|
|
90
|
+
for (const key in input) {
|
|
91
|
+
if (!(input[key] instanceof z.ZodType)) {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return true
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Defines a Convex table using either:
|
|
101
|
+
* - A raw Zod shape (an object mapping field names to Zod types)
|
|
102
|
+
* - A Zod union schema (for polymorphic tables)
|
|
39
103
|
*
|
|
40
|
-
*
|
|
104
|
+
* For object shapes, this function intentionally accepts a raw shape instead of a ZodObject instance.
|
|
41
105
|
* Accepting raw shapes allows TypeScript to infer field types more accurately and efficiently,
|
|
42
106
|
* leading to better type inference and performance throughout the codebase.
|
|
43
|
-
*
|
|
44
|
-
*
|
|
107
|
+
*
|
|
108
|
+
* For union schemas, this enables polymorphic tables with discriminated unions.
|
|
45
109
|
*
|
|
46
110
|
* Returns the Table definition along with Zod schemas for documents and arrays.
|
|
47
111
|
*
|
|
48
112
|
* @param name - The table name
|
|
49
|
-
* @param
|
|
50
|
-
* @returns A Table with attached shape,
|
|
113
|
+
* @param schemaOrShape - Either a raw object shape or a Zod union schema
|
|
114
|
+
* @returns A Table with attached helpers (shape, schema, zDoc, docArray, withSystemFields)
|
|
51
115
|
*
|
|
52
|
-
* @example
|
|
116
|
+
* @example Object shape
|
|
53
117
|
* ```ts
|
|
54
118
|
* const Users = zodTable('users', {
|
|
55
119
|
* name: z.string(),
|
|
@@ -66,36 +130,115 @@ export function zodDocOrNull<
|
|
|
66
130
|
* { returns: Users.docArray }
|
|
67
131
|
* )
|
|
68
132
|
* ```
|
|
133
|
+
*
|
|
134
|
+
* @example Union schema (polymorphic table)
|
|
135
|
+
* ```ts
|
|
136
|
+
* const shapeSchema = z.union([
|
|
137
|
+
* z.object({ kind: z.literal('circle'), r: z.number() }),
|
|
138
|
+
* z.object({ kind: z.literal('rectangle'), width: z.number() })
|
|
139
|
+
* ])
|
|
140
|
+
*
|
|
141
|
+
* const Shapes = zodTable('shapes', shapeSchema)
|
|
142
|
+
*
|
|
143
|
+
* // Use in schema
|
|
144
|
+
* export default defineSchema({ shapes: Shapes.table })
|
|
145
|
+
*
|
|
146
|
+
* // Use for return types with system fields
|
|
147
|
+
* export const getShapes = zQuery(query, {},
|
|
148
|
+
* async (ctx) => ctx.db.query('shapes').collect(),
|
|
149
|
+
* { returns: Shapes.docArray }
|
|
150
|
+
* )
|
|
151
|
+
* ```
|
|
69
152
|
*/
|
|
70
153
|
export function zodTable<TableName extends string, Shape extends Record<string, z.ZodTypeAny>>(
|
|
71
154
|
name: TableName,
|
|
72
155
|
shape: Shape
|
|
73
|
-
) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// Create docArray helper for return types
|
|
84
|
-
const docArray = z.array(zDoc)
|
|
85
|
-
|
|
86
|
-
// Attach everything for comprehensive usage
|
|
87
|
-
return Object.assign(table, {
|
|
88
|
-
shape,
|
|
89
|
-
zDoc,
|
|
90
|
-
docArray
|
|
91
|
-
}) as typeof table & {
|
|
92
|
-
shape: Shape
|
|
93
|
-
zDoc: z.ZodObject<
|
|
156
|
+
): ReturnType<typeof Table<any, TableName>> & {
|
|
157
|
+
shape: Shape
|
|
158
|
+
zDoc: z.ZodObject<
|
|
159
|
+
Shape & {
|
|
160
|
+
_id: ReturnType<typeof zid<TableName>>
|
|
161
|
+
_creationTime: z.ZodNumber
|
|
162
|
+
}
|
|
163
|
+
>
|
|
164
|
+
docArray: z.ZodArray<
|
|
165
|
+
z.ZodObject<
|
|
94
166
|
Shape & {
|
|
95
167
|
_id: ReturnType<typeof zid<TableName>>
|
|
96
168
|
_creationTime: z.ZodNumber
|
|
97
169
|
}
|
|
98
170
|
>
|
|
99
|
-
|
|
171
|
+
>
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export function zodTable<TableName extends string, Schema extends z.ZodTypeAny>(
|
|
175
|
+
name: TableName,
|
|
176
|
+
schema: Schema
|
|
177
|
+
): {
|
|
178
|
+
table: ReturnType<typeof defineTable>
|
|
179
|
+
tableName: TableName
|
|
180
|
+
validator: ReturnType<typeof zodToConvex<Schema>>
|
|
181
|
+
schema: Schema
|
|
182
|
+
docArray: z.ZodArray<ReturnType<typeof addSystemFields<TableName, Schema>>>
|
|
183
|
+
withSystemFields: () => ReturnType<typeof addSystemFields<TableName, Schema>>
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function zodTable<
|
|
187
|
+
TableName extends string,
|
|
188
|
+
SchemaOrShape extends z.ZodTypeAny | Record<string, z.ZodTypeAny>
|
|
189
|
+
>(name: TableName, schemaOrShape: SchemaOrShape): any {
|
|
190
|
+
// Detect if it's an object shape or a schema
|
|
191
|
+
if (isObjectShape(schemaOrShape)) {
|
|
192
|
+
// Original object shape logic
|
|
193
|
+
const shape = schemaOrShape as Record<string, z.ZodTypeAny>
|
|
194
|
+
|
|
195
|
+
// Convert fields with proper types
|
|
196
|
+
const convexFields = zodToConvexFields(shape) as ConvexValidatorFromZodFieldsAuto<typeof shape>
|
|
197
|
+
|
|
198
|
+
// Create the Table from convex-helpers with explicit type
|
|
199
|
+
const table = Table<ConvexValidatorFromZodFieldsAuto<typeof shape>, TableName>(
|
|
200
|
+
name,
|
|
201
|
+
convexFields
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
// Create zDoc schema with system fields
|
|
205
|
+
const zDoc = zodDoc(name, z.object(shape))
|
|
206
|
+
|
|
207
|
+
// Create docArray helper for return types
|
|
208
|
+
const docArray = z.array(zDoc)
|
|
209
|
+
|
|
210
|
+
// Attach everything for comprehensive usage
|
|
211
|
+
return Object.assign(table, {
|
|
212
|
+
shape,
|
|
213
|
+
zDoc,
|
|
214
|
+
docArray
|
|
215
|
+
})
|
|
216
|
+
} else {
|
|
217
|
+
// Union or other schema type logic
|
|
218
|
+
const schema = schemaOrShape as z.ZodTypeAny
|
|
219
|
+
|
|
220
|
+
// Convert schema to Convex validator
|
|
221
|
+
const convexValidator = zodToConvex(schema)
|
|
222
|
+
|
|
223
|
+
// For unions, use defineTable directly (not Table helper which expects object fields)
|
|
224
|
+
// Note: TypeScript types don't reflect it, but Convex supports union validators in tables
|
|
225
|
+
const table = defineTable(convexValidator as any)
|
|
226
|
+
|
|
227
|
+
// Create document schema with system fields
|
|
228
|
+
const withFields = addSystemFields(name, schema)
|
|
229
|
+
|
|
230
|
+
// Create docArray helper
|
|
231
|
+
const docArray = z.array(withFields)
|
|
232
|
+
|
|
233
|
+
// Attach helpers for union tables
|
|
234
|
+
// Return structure similar to Table() but without fields-based helpers
|
|
235
|
+
return {
|
|
236
|
+
table,
|
|
237
|
+
tableName: name,
|
|
238
|
+
validator: convexValidator,
|
|
239
|
+
schema,
|
|
240
|
+
docArray,
|
|
241
|
+
withSystemFields: () => addSystemFields(name, schema)
|
|
242
|
+
}
|
|
100
243
|
}
|
|
101
244
|
}
|
package/src/types.ts
CHANGED
|
@@ -15,31 +15,19 @@ export type InferArgs<A> = A extends z.ZodObject<infer S>
|
|
|
15
15
|
? z.infer<A>
|
|
16
16
|
: Record<string, never>
|
|
17
17
|
|
|
18
|
-
// Return type inference
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
// Return type inference - uses z.output for Zod schemas
|
|
19
|
+
// Previously had bailouts for unions/custom to avoid TypeScript depth errors,
|
|
20
|
+
// but research (Issue #20) showed convex-helpers handles these without issues.
|
|
21
|
+
// Removing bailouts fixes Issue #19 (Promise<any> return types).
|
|
22
|
+
export type InferReturns<R> = R extends z.ZodType<any, any, any>
|
|
23
|
+
? z.output<R>
|
|
24
|
+
: R extends undefined
|
|
23
25
|
? any
|
|
24
|
-
:
|
|
25
|
-
R extends z.ZodType<any, any, any>
|
|
26
|
-
? z.output<R>
|
|
27
|
-
: // Use z.output for other schemas
|
|
28
|
-
R extends undefined
|
|
29
|
-
? any
|
|
30
|
-
: R
|
|
26
|
+
: R
|
|
31
27
|
|
|
32
28
|
// For handler authoring: what the handler returns before wrapper validation/encoding
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
: // Bail immediately for unions
|
|
36
|
-
R extends z.ZodCustom<any>
|
|
37
|
-
? any
|
|
38
|
-
: // Bail immediately for custom
|
|
39
|
-
R extends z.ZodType<any, any, any>
|
|
40
|
-
? z.input<R>
|
|
41
|
-
: // Use z.input for other schemas
|
|
42
|
-
any
|
|
29
|
+
// Uses z.input since this is what the handler produces before encoding
|
|
30
|
+
export type InferHandlerReturns<R> = R extends z.ZodType<any, any, any> ? z.input<R> : any
|
|
43
31
|
|
|
44
32
|
/**
|
|
45
33
|
* Extract the visibility type from a Convex builder function
|
package/src/wrappers.ts
CHANGED
|
@@ -39,8 +39,8 @@ function containsCustom(schema: z.ZodTypeAny, maxDepth = 50, currentDepth = 0):
|
|
|
39
39
|
|
|
40
40
|
let result = false
|
|
41
41
|
|
|
42
|
-
//
|
|
43
|
-
if (
|
|
42
|
+
// Zod v4 exports ZodCustom and instances expose `schema.type === "custom"`.
|
|
43
|
+
if (schema instanceof z.ZodCustom) {
|
|
44
44
|
result = true
|
|
45
45
|
} else if (schema instanceof z.ZodUnion) {
|
|
46
46
|
result = (schema.options as z.ZodTypeAny[]).some(opt =>
|