lazyconvex 0.0.0

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.
Files changed (70) hide show
  1. package/README.md +926 -0
  2. package/dist/components/index.mjs +937 -0
  3. package/dist/error-D4GuI0ot.mjs +71 -0
  4. package/dist/file-field-BqVgy8xY.mjs +205 -0
  5. package/dist/form-BXJK_j10.d.mts +99 -0
  6. package/dist/index.d.mts +433 -0
  7. package/dist/index.mjs +1 -0
  8. package/dist/index2.d.mts +5 -0
  9. package/dist/index3.d.mts +35 -0
  10. package/dist/index4.d.mts +101 -0
  11. package/dist/index5.d.mts +842 -0
  12. package/dist/next/index.mjs +151 -0
  13. package/dist/org-CmJBb8z-.d.mts +56 -0
  14. package/dist/react/index.mjs +158 -0
  15. package/dist/retry.d.mts +12 -0
  16. package/dist/retry.mjs +35 -0
  17. package/dist/schema.d.mts +23 -0
  18. package/dist/schema.mjs +15 -0
  19. package/dist/server/index.mjs +2572 -0
  20. package/dist/types-DWBVRtit.d.mts +322 -0
  21. package/dist/use-online-status-CMr73Jlk.mjs +155 -0
  22. package/dist/use-upload-DtELytQi.mjs +95 -0
  23. package/dist/zod.d.mts +18 -0
  24. package/dist/zod.mjs +87 -0
  25. package/package.json +40 -0
  26. package/src/components/editors-section.tsx +86 -0
  27. package/src/components/fields.tsx +884 -0
  28. package/src/components/file-field.tsx +234 -0
  29. package/src/components/form.tsx +191 -0
  30. package/src/components/index.ts +11 -0
  31. package/src/components/offline-indicator.tsx +15 -0
  32. package/src/components/org-avatar.tsx +13 -0
  33. package/src/components/permission-guard.tsx +36 -0
  34. package/src/components/role-badge.tsx +14 -0
  35. package/src/components/suspense-wrap.tsx +8 -0
  36. package/src/index.ts +40 -0
  37. package/src/next/active-org.ts +33 -0
  38. package/src/next/auth.ts +9 -0
  39. package/src/next/image.ts +134 -0
  40. package/src/next/index.ts +3 -0
  41. package/src/react/form-meta.ts +53 -0
  42. package/src/react/form.ts +201 -0
  43. package/src/react/index.ts +8 -0
  44. package/src/react/org.tsx +96 -0
  45. package/src/react/use-active-org.ts +48 -0
  46. package/src/react/use-bulk-selection.ts +47 -0
  47. package/src/react/use-online-status.ts +21 -0
  48. package/src/react/use-optimistic.ts +54 -0
  49. package/src/react/use-upload.ts +101 -0
  50. package/src/retry.ts +47 -0
  51. package/src/schema.ts +30 -0
  52. package/src/server/cache-crud.ts +175 -0
  53. package/src/server/check-schema.ts +29 -0
  54. package/src/server/child.ts +98 -0
  55. package/src/server/crud.ts +384 -0
  56. package/src/server/db.ts +7 -0
  57. package/src/server/error.ts +39 -0
  58. package/src/server/file.ts +372 -0
  59. package/src/server/helpers.ts +214 -0
  60. package/src/server/index.ts +12 -0
  61. package/src/server/org-crud.ts +307 -0
  62. package/src/server/org-helpers.ts +54 -0
  63. package/src/server/org.ts +572 -0
  64. package/src/server/schema-helpers.ts +107 -0
  65. package/src/server/setup.ts +138 -0
  66. package/src/server/test-crud.ts +211 -0
  67. package/src/server/test.ts +554 -0
  68. package/src/server/types.ts +392 -0
  69. package/src/server/unique.ts +28 -0
  70. package/src/zod.ts +141 -0
@@ -0,0 +1,392 @@
1
+ import type { CustomBuilder } from 'convex-helpers/server/zod4'
2
+ import type {
3
+ ActionBuilder,
4
+ FunctionVisibility,
5
+ GenericDataModel,
6
+ MutationBuilder,
7
+ PaginationOptions,
8
+ paginationOptsValidator,
9
+ QueryBuilder,
10
+ RegisteredAction,
11
+ RegisteredMutation,
12
+ RegisteredQuery
13
+ } from 'convex/server'
14
+ import type { GenericId } from 'convex/values'
15
+ import type { z as _, ZodNullable, ZodNumber, ZodObject, ZodOptional, ZodRawShape } from 'zod/v4'
16
+
17
+ interface BaseBuilders {
18
+ m: Mb
19
+ q: Qb
20
+ }
21
+ interface CacheBuilders<DM extends GenericDataModel = GenericDataModel> {
22
+ action: ActionBuilder<DM, 'public'>
23
+ cm: Mb
24
+ cq: Qb
25
+ internalMutation: MutationBuilder<DM, 'internal'>
26
+ internalQuery: QueryBuilder<DM, 'internal'>
27
+ mutation: MutationBuilder<DM, 'public'>
28
+ query: QueryBuilder<DM, 'public'>
29
+ }
30
+ interface CacheOptions<S extends ZodRawShape, K extends keyof _.output<ZodObject<S>> & string> {
31
+ fetcher?: (c: ActionCtxLike, key: _.output<ZodObject<S>>[K]) => Promise<_.output<ZodObject<S>>>
32
+ key: K
33
+ schema: ZodObject<S>
34
+ table: string
35
+ ttl?: number
36
+ }
37
+ interface ChildConfig {
38
+ foreignKey: string
39
+ index?: string
40
+ parent: string
41
+ schema: ZodObject<ZodRawShape>
42
+ }
43
+ interface ComparisonOp<V> {
44
+ $between?: [V, V]
45
+ $gt?: V
46
+ $gte?: V
47
+ $lt?: V
48
+ $lte?: V
49
+ }
50
+ interface CrudBuilders extends BaseBuilders {
51
+ children: Record<string, ChildConfig>
52
+ cm: Mb
53
+ cq: Qb
54
+ pq: Qb
55
+ }
56
+ interface CrudOptions<S extends ZodRawShape> {
57
+ auth?: { where?: WhereOf<S> }
58
+ cascade?: boolean
59
+ pub?: { where?: WhereOf<S> }
60
+ search?: 'index'
61
+ softDelete?: boolean
62
+ }
63
+ interface DbCtx {
64
+ db: DbLike
65
+ }
66
+ interface MutCtx extends UserCtx {
67
+ storage: StorageLike
68
+ }
69
+ type Rec = Record<string, unknown>
70
+ interface UserCtx extends DbCtx {
71
+ user: Rec
72
+ }
73
+ const ERROR_MESSAGES = {
74
+ ALREADY_ORG_MEMBER: 'Already a member of this organization',
75
+ CANNOT_MODIFY_ADMIN: 'Admins cannot modify other admins',
76
+ CANNOT_MODIFY_OWNER: 'Cannot modify the owner',
77
+ CHUNK_ALREADY_UPLOADED: 'Chunk already uploaded',
78
+ CHUNK_NOT_FOUND: 'Chunk not found',
79
+ CONFLICT: 'Conflict detected',
80
+ EDITOR_REQUIRED: 'Editor permission required',
81
+ FILE_NOT_FOUND: 'File not found',
82
+ FILE_TOO_LARGE: 'File too large',
83
+ FORBIDDEN: 'Forbidden',
84
+ INCOMPLETE_UPLOAD: 'Incomplete upload',
85
+ INSUFFICIENT_ORG_ROLE: 'Insufficient permissions',
86
+ INVALID_FILE_TYPE: 'Invalid file type',
87
+ INVALID_INVITE: 'Invalid invite',
88
+ INVALID_MESSAGE: 'Invalid message',
89
+ INVALID_SESSION_STATE: 'Invalid session state',
90
+ INVALID_TOOL_ARGS: 'Invalid tool arguments',
91
+ INVALID_WHERE: 'Invalid filters',
92
+ INVITE_EXPIRED: 'Invite has expired',
93
+ JOIN_REQUEST_EXISTS: 'Join request already exists',
94
+ LIMIT_EXCEEDED: 'Limit exceeded',
95
+ MESSAGE_NOT_SAVED: 'Message not saved',
96
+ MUST_TRANSFER_OWNERSHIP: 'Must transfer ownership before leaving',
97
+ NO_FETCHER: 'No fetcher configured',
98
+ NO_PRECEDING_USER_MESSAGE: 'No preceding user message',
99
+ NOT_AUTHENTICATED: 'Please log in',
100
+ NOT_AUTHORIZED: 'Not authorized',
101
+ NOT_FOUND: 'Not found',
102
+ NOT_ORG_MEMBER: 'Not a member of this organization',
103
+ ORG_SLUG_TAKEN: 'Organization slug already taken',
104
+ RATE_LIMITED: 'Too many requests',
105
+ SESSION_NOT_FOUND: 'Session not found',
106
+ TARGET_MUST_BE_ADMIN: 'Can only transfer ownership to an admin',
107
+ UNAUTHORIZED: 'Unauthorized',
108
+ USER_NOT_FOUND: 'User not found'
109
+ } as const
110
+ type Ab<V extends FunctionVisibility = 'public'> = CustomBuilder<
111
+ 'action',
112
+ Record<string, never>,
113
+ Rec,
114
+ Record<string, never>,
115
+ unknown,
116
+ V,
117
+ Rec
118
+ >
119
+ interface ActionCtxLike {
120
+ runMutation: (ref: string, args: Rec) => Promise<unknown>
121
+ runQuery: (ref: string, args: Rec) => Promise<unknown>
122
+ }
123
+ interface AuthorInfo {
124
+ [key: string]: unknown
125
+ email?: string
126
+ image?: string
127
+ name?: string
128
+ }
129
+ interface CacheCrudResult<S extends ZodRawShape> {
130
+ all: RegisteredQuery<'public', Rec, DocBase<S>[]>
131
+ create: RegisteredMutation<'public', Rec, string>
132
+ get: RegisteredQuery<'public', Rec, (DocBase<S> & { cacheHit: true }) | null>
133
+ getInternal: RegisteredQuery<'internal', Rec, DocBase<S> | null>
134
+ invalidate: RegisteredMutation<'public', Rec, DocBase<S> | null>
135
+ list: RegisteredQuery<'public', Rec, PaginatedResult<DocBase<S>>>
136
+ load: RegisteredAction<'public', Rec, _.output<ZodObject<S>> & { cacheHit: boolean }>
137
+ purge: RegisteredMutation<'public', Rec, number>
138
+ read: RegisteredQuery<'public', Rec, DocBase<S> | null>
139
+ refresh: RegisteredAction<'public', Rec, _.output<ZodObject<S>> & { cacheHit: boolean }>
140
+ rm: RegisteredMutation<'public', Rec, DocBase<S> | null>
141
+ set: RegisteredMutation<'internal', Rec, void>
142
+ update: RegisteredMutation<'public', Rec, DocBase<S>>
143
+ }
144
+ interface CanEditOpts {
145
+ acl: boolean
146
+ doc: {
147
+ editors?: string[]
148
+ userId: string
149
+ }
150
+ role: OrgRole
151
+ userId: string
152
+ }
153
+ interface ChildCrudResult<S extends ZodRawShape> {
154
+ create: RegisteredMutation<'public', Rec, string>
155
+ get: RegisteredQuery<'public', Rec, DocBase<S> | null>
156
+ list: RegisteredQuery<'public', Rec, DocBase<S>[]>
157
+ rm: RegisteredMutation<'public', Rec, DocBase<S>>
158
+ update: RegisteredMutation<'public', Rec, DocBase<S> | null>
159
+ }
160
+ interface CrudReadApi<S extends ZodRawShape, V extends FunctionVisibility = 'public'> {
161
+ all: RegisteredQuery<V, { where?: WhereOf<S> }, EnrichedDoc<S>[]>
162
+ count: RegisteredQuery<V, { where?: WhereOf<S> }, number>
163
+ list: RegisteredQuery<V, { paginationOpts: PaginationOptions; where?: WhereOf<S> }, PaginatedResult<EnrichedDoc<S>>>
164
+ read: RegisteredQuery<V, { id: string; own?: boolean; where?: WhereOf<S> }, EnrichedDoc<S> | null>
165
+ search: RegisteredQuery<V, { fields?: string[]; query: string; where?: WhereOf<S> }, EnrichedDoc<S>[]>
166
+ }
167
+ interface CrudResult<S extends ZodRawShape> {
168
+ auth: CrudReadApi<S>
169
+ authIndexed: RegisteredQuery<
170
+ 'public',
171
+ { index: string; key: string; value: string; where?: WhereOf<S> },
172
+ EnrichedDoc<S>[]
173
+ >
174
+ bulkRm: RegisteredMutation<'public', { ids: string[] }, number>
175
+ bulkUpdate: RegisteredMutation<'public', { data: Partial<_.output<ZodObject<S>>>; ids: string[] }, unknown[]>
176
+ create: RegisteredMutation<'public', _.output<ZodObject<S>>, string>
177
+ pub: CrudReadApi<S>
178
+ pubIndexed: RegisteredQuery<
179
+ 'public',
180
+ { index: string; key: string; value: string; where?: WhereOf<S> },
181
+ EnrichedDoc<S>[]
182
+ >
183
+ restore?: RegisteredMutation<'public', { id: string }, DocBase<S>>
184
+ rm: RegisteredMutation<'public', { id: string }, DocBase<S>>
185
+ update: RegisteredMutation<
186
+ 'public',
187
+ Partial<_.output<ZodObject<S>>> & { expectedUpdatedAt?: number; id: string },
188
+ DocBase<S>
189
+ >
190
+ }
191
+ interface DbLike extends DbReadLike {
192
+ delete: (id: string) => Promise<void>
193
+ insert: (table: string, data: Rec) => Promise<string>
194
+ patch: (id: string, data: Rec) => Promise<void>
195
+ system: DbReadLike
196
+ }
197
+ interface DbReadLike {
198
+ get: (id: string) => Promise<null | Rec>
199
+ query: (table: string) => QueryLike
200
+ }
201
+ type DocBase<S extends ZodRawShape> = _.output<ZodObject<S>> & {
202
+ _creationTime: number
203
+ _id: string
204
+ updatedAt: number
205
+ }
206
+ type EnrichedDoc<S extends ZodRawShape> = WithUrls<
207
+ DocBase<S> & {
208
+ author: AuthorInfo | null
209
+ own: boolean | null
210
+ userId: string
211
+ }
212
+ >
213
+ type ErrorCode = keyof typeof ERROR_MESSAGES
214
+ type FID = GenericId<'_storage'>
215
+ interface FilterLike {
216
+ and: (a: unknown, b: unknown) => unknown
217
+ eq: (a: unknown, b: unknown) => unknown
218
+ field: (name: string) => unknown
219
+ gt: (a: unknown, b: unknown) => unknown
220
+ gte: (a: unknown, b: unknown) => unknown
221
+ lt: (a: unknown, b: unknown) => unknown
222
+ lte: (a: unknown, b: unknown) => unknown
223
+ or: (a: unknown, b: unknown) => unknown
224
+ }
225
+ interface IndexLike {
226
+ eq: (field: string, value: unknown) => IndexLike
227
+ }
228
+ type Mb<V extends FunctionVisibility = 'public'> = CustomBuilder<
229
+ 'mutation',
230
+ Record<string, never>,
231
+ Rec,
232
+ Record<string, never>,
233
+ unknown,
234
+ V,
235
+ Rec
236
+ >
237
+ interface MutationCtxLike {
238
+ auth: { getUserIdentity: () => Promise<unknown> }
239
+ db: DbLike
240
+ storage: StorageLike
241
+ }
242
+ interface OrgCrudResult<S extends ZodRawShape> {
243
+ addEditor: RegisteredMutation<'public', Rec, DocBase<S> | null>
244
+ all: RegisteredQuery<'public', Rec, OrgEnrichedDoc<S>[]>
245
+ bulkRm: RegisteredMutation<'public', Rec, number>
246
+ bulkUpdate: RegisteredMutation<'public', Rec, DocBase<S>[]>
247
+ count: RegisteredQuery<'public', Rec, number>
248
+ create: RegisteredMutation<'public', Rec, string>
249
+ editors: RegisteredQuery<'public', Rec, { email: string; name: string; userId: string }[]>
250
+ list: RegisteredQuery<'public', Rec, PaginatedResult<OrgEnrichedDoc<S>>>
251
+ read: RegisteredQuery<'public', Rec, OrgEnrichedDoc<S>>
252
+ removeEditor: RegisteredMutation<'public', Rec, DocBase<S> | null>
253
+ rm: RegisteredMutation<'public', Rec, DocBase<S>>
254
+ setEditors: RegisteredMutation<'public', Rec, DocBase<S> | null>
255
+ update: RegisteredMutation<'public', Rec, DocBase<S> | null>
256
+ }
257
+ type OrgEnrichedDoc<S extends ZodRawShape> = WithUrls<
258
+ DocBase<S> & {
259
+ author: AuthorInfo | null
260
+ orgId: string
261
+ own: boolean | null
262
+ userId: string
263
+ }
264
+ >
265
+ type OrgRole = 'admin' | 'member' | 'owner'
266
+ interface PaginatedResult<D> {
267
+ continueCursor: string
268
+ isDone: boolean
269
+ page: D[]
270
+ }
271
+ type PaginationOptsShape = Record<keyof typeof paginationOptsValidator.fields, ZodNullable | ZodNumber | ZodOptional>
272
+ type Qb<V extends FunctionVisibility = 'public'> = CustomBuilder<
273
+ 'query',
274
+ Record<string, never>,
275
+ Rec,
276
+ Record<string, never>,
277
+ unknown,
278
+ V,
279
+ Rec
280
+ >
281
+ interface QueryCtxLike {
282
+ auth: { getUserIdentity: () => Promise<unknown> }
283
+ db: DbLike
284
+ storage: StorageLike
285
+ }
286
+ interface QueryLike {
287
+ collect: () => Promise<Rec[]>
288
+ filter: (fn: (fb: FilterLike) => unknown) => QueryLike
289
+ first: () => Promise<null | Rec>
290
+ order: (dir: 'asc' | 'desc') => QueryLike
291
+ paginate: (opts: Rec) => Promise<{ continueCursor: string; isDone: boolean; page: Rec[] }>
292
+ take: (n: number) => Promise<Rec[]>
293
+ unique: () => Promise<null | Rec>
294
+ withIndex: (name: string, fn?: (ib: IndexLike) => unknown) => QueryLike
295
+ withSearchIndex: (name: string, fn: (sb: SearchLike) => unknown) => QueryLike
296
+ }
297
+ interface ReadCtx {
298
+ db: DbLike
299
+ storage: StorageLike
300
+ viewerId: null | string
301
+ withAuthor: <T extends { userId: string }>(
302
+ docs: T[]
303
+ ) => Promise<
304
+ (T & {
305
+ author: null | Rec
306
+ own: boolean | null
307
+ })[]
308
+ >
309
+ }
310
+ interface SearchLike {
311
+ search: (field: string, query: string) => unknown
312
+ }
313
+ interface SetupConfig<DM extends GenericDataModel = GenericDataModel> {
314
+ action: ActionBuilder<DM, 'public'>
315
+ children?: Record<string, ChildConfig>
316
+ getAuthUserId: (ctx: never) => Promise<null | string>
317
+ internalMutation: MutationBuilder<DM, 'internal'>
318
+ internalQuery: QueryBuilder<DM, 'internal'>
319
+ mutation: MutationBuilder<DM, 'public'>
320
+ orgCascadeTables?: string[]
321
+ orgSchema?: ZodObject<ZodRawShape>
322
+ query: QueryBuilder<DM, 'public'>
323
+ }
324
+ interface StorageLike {
325
+ delete: (id: string) => Promise<void>
326
+ getUrl: (id: string) => Promise<null | string>
327
+ }
328
+ type UrlKey<K, V> =
329
+ NonNullable<V> extends FID | FID[] | readonly FID[] ? `${K & string}Url${NonNullable<V> extends FID ? '' : 's'}` : never
330
+ type UrlVal<V> =
331
+ NonNullable<V> extends FID | FID[] | readonly FID[]
332
+ ? NonNullable<V> extends FID
333
+ ? null | string
334
+ : (null | string)[]
335
+ : never
336
+ type WhereFieldValue<V> = ComparisonOp<V> | V
337
+ type WhereGroupOf<S extends ZodRawShape> = {
338
+ [K in keyof _.output<ZodObject<S>>]?: WhereFieldValue<_.output<ZodObject<S>>[K]>
339
+ } & {
340
+ own?: boolean
341
+ }
342
+ type WhereOf<S extends ZodRawShape> = WhereGroupOf<S> & {
343
+ or?: WhereGroupOf<S>[]
344
+ }
345
+ type WithUrls<D> = D & { [K in keyof D as UrlKey<K, D[K]>]: UrlVal<D[K]> }
346
+ export type {
347
+ Ab,
348
+ ActionCtxLike,
349
+ AuthorInfo,
350
+ BaseBuilders,
351
+ CacheBuilders,
352
+ CacheCrudResult,
353
+ CacheOptions,
354
+ CanEditOpts,
355
+ ChildConfig,
356
+ ChildCrudResult,
357
+ ComparisonOp,
358
+ CrudBuilders,
359
+ CrudOptions,
360
+ CrudReadApi,
361
+ CrudResult,
362
+ DbCtx,
363
+ DbLike,
364
+ DbReadLike,
365
+ DocBase,
366
+ EnrichedDoc,
367
+ ErrorCode,
368
+ FID,
369
+ FilterLike,
370
+ IndexLike,
371
+ Mb,
372
+ MutationCtxLike,
373
+ MutCtx,
374
+ OrgCrudResult,
375
+ OrgEnrichedDoc,
376
+ OrgRole,
377
+ PaginatedResult,
378
+ PaginationOptsShape,
379
+ Qb,
380
+ QueryCtxLike,
381
+ QueryLike,
382
+ ReadCtx,
383
+ Rec,
384
+ SearchLike,
385
+ SetupConfig,
386
+ StorageLike,
387
+ UserCtx,
388
+ WhereGroupOf,
389
+ WhereOf,
390
+ WithUrls
391
+ }
392
+ export { ERROR_MESSAGES }
@@ -0,0 +1,28 @@
1
+ import type { RegisteredQuery } from 'convex/server'
2
+
3
+ import { zid } from 'convex-helpers/server/zod4'
4
+ import { string } from 'zod/v4'
5
+
6
+ import type { FilterLike, Qb, QueryCtxLike } from './types'
7
+
8
+ const makeUnique = ({
9
+ field,
10
+ pq,
11
+ table
12
+ }: {
13
+ field: string
14
+ pq: Qb
15
+ table: string
16
+ }): RegisteredQuery<'public', { exclude?: string; value: string }, boolean> =>
17
+ pq({
18
+ args: { exclude: zid(table).optional(), value: string() },
19
+ handler: (async (c: QueryCtxLike, { exclude, value }: { exclude?: string; value: string }) => {
20
+ const existing = await c.db
21
+ .query(table)
22
+ .filter((f: FilterLike) => f.eq(f.field(field), value))
23
+ .first()
24
+ return !(existing as null | Record<string, unknown>) || (existing as Record<string, unknown>)._id === exclude
25
+ }) as never
26
+ }) as never
27
+
28
+ export { makeUnique }
package/src/zod.ts ADDED
@@ -0,0 +1,141 @@
1
+ import type { core, output, ZodObject, ZodRawShape, ZodType } from 'zod/v4'
2
+
3
+ type CvMeta = 'file' | 'files'
4
+ type DefType = core.$ZodTypeDef['type']
5
+ type ZodSchema = ZodType
6
+
7
+ const WRAPPERS: ReadonlySet<DefType> = new Set<DefType>([
8
+ 'catch',
9
+ 'default',
10
+ 'nullable',
11
+ 'optional',
12
+ 'prefault',
13
+ 'readonly'
14
+ ]),
15
+ unwrapZod = (
16
+ schema: unknown
17
+ ): {
18
+ def: undefined | ZodSchema['def']
19
+ schema: undefined | ZodSchema
20
+ type: '' | DefType
21
+ } => {
22
+ let cur = schema as undefined | ZodSchema
23
+ while (cur && typeof cur === 'object' && 'type' in cur) {
24
+ if (!WRAPPERS.has(cur.type)) return { def: cur.def, schema: cur, type: cur.type }
25
+ cur = (cur.def as { innerType?: ZodSchema }).innerType
26
+ }
27
+ return { def: undefined, schema: undefined, type: '' }
28
+ },
29
+ isOptionalField = (schema: unknown): boolean => {
30
+ let cur = schema as undefined | ZodSchema
31
+ while (cur && typeof cur === 'object' && 'type' in cur) {
32
+ if (cur.type === 'optional') return true
33
+ if (!WRAPPERS.has(cur.type)) return false
34
+ cur = (cur.def as { innerType?: ZodSchema }).innerType
35
+ }
36
+ return false
37
+ },
38
+ elementOf = (s: undefined | ZodSchema): unknown => (s?.def as undefined | { element?: unknown })?.element,
39
+ cvMetaOf = (schema: undefined | ZodSchema): CvMeta | undefined => {
40
+ if (!schema || typeof schema.meta !== 'function') return
41
+ const m = schema.meta() as undefined | { cv?: unknown }
42
+ if (m && typeof m === 'object') {
43
+ const { cv } = m
44
+ if (cv === 'file' || cv === 'files') return cv
45
+ }
46
+ },
47
+ isArrayType = (t: '' | DefType) => t === 'array',
48
+ isBooleanType = (t: '' | DefType) => t === 'boolean',
49
+ isDateType = (t: '' | DefType) => t === 'date',
50
+ isNumberType = (t: '' | DefType) => t === 'number',
51
+ isStringType = (t: '' | DefType) => t === 'string' || t === 'enum',
52
+ cvFileKindOf = (schema: unknown): CvMeta | undefined => {
53
+ const { schema: s, type } = unwrapZod(schema),
54
+ cv = cvMetaOf(s)
55
+ if (cv) return cv
56
+ if (isArrayType(type) && cvMetaOf(elementOf(s) as undefined | ZodSchema) === 'file') return 'files'
57
+ },
58
+ enumToOptions = <T extends string>(
59
+ schema: { options: readonly T[] },
60
+ transform?: (v: T) => string
61
+ ): {
62
+ label: string
63
+ value: T
64
+ }[] =>
65
+ schema.options.map(v => ({
66
+ label: transform?.(v) ?? v.charAt(0).toUpperCase() + v.slice(1),
67
+ value: v
68
+ })),
69
+ requiredPartial = <S extends ZodObject<ZodRawShape>>(
70
+ schema: S,
71
+ requiredKeys: (keyof S['shape'])[]
72
+ ): ZodObject<ZodRawShape> => {
73
+ const partial = schema.partial(),
74
+ required = Object.fromEntries(requiredKeys.map(k => [k, true])) as Record<string, true>
75
+ return partial.required(required) as ZodObject<ZodRawShape>
76
+ },
77
+ // eslint-disable-next-line max-statements
78
+ defaultValue = (schema: unknown): unknown => {
79
+ const { schema: base, type } = unwrapZod(schema),
80
+ fk = cvFileKindOf(schema)
81
+ if (fk === 'file') return null
82
+ if (fk === 'files') return []
83
+ if (isArrayType(type)) return []
84
+ if (isBooleanType(type)) return false
85
+ if (isNumberType(type)) return 0
86
+ if (isStringType(type)) {
87
+ if (base && 'options' in base) {
88
+ const opts = (base as { options: readonly string[] }).options
89
+ if (opts.length) return opts[0]
90
+ }
91
+ return ''
92
+ }
93
+ if (isDateType(type)) return null
94
+ const inner = (base?.def as undefined | { innerType?: unknown })?.innerType
95
+ if (inner) return defaultValue(inner)
96
+ },
97
+ defaultValues = <S extends ZodObject<ZodRawShape>>(schema: S): output<S> => {
98
+ const result: Record<string, unknown> = {}
99
+ // biome-ignore lint/nursery/noForIn: iterating shape keys with hasOwn guard
100
+ for (const k in schema.shape) if (Object.hasOwn(schema.shape, k)) result[k] = defaultValue(schema.shape[k])
101
+ return result as output<S>
102
+ },
103
+ pickValues = <S extends ZodObject<ZodRawShape>>(schema: S, doc: object): output<S> => {
104
+ const d = doc as Record<string, unknown>,
105
+ result: Record<string, unknown> = {}
106
+ // biome-ignore lint/nursery/noForIn: iterating shape keys with hasOwn guard
107
+ for (const k in schema.shape) if (Object.hasOwn(schema.shape, k)) result[k] = d[k] ?? defaultValue(schema.shape[k])
108
+ return result as output<S>
109
+ },
110
+ coerceOptionals = <S extends ZodObject<ZodRawShape>>(schema: S, data: output<S>): output<S> => {
111
+ const result: Record<string, unknown> = { ...data }
112
+ for (const k of Object.keys(result))
113
+ if (isOptionalField(schema.shape[k]) && isStringType(unwrapZod(schema.shape[k]).type)) {
114
+ const v = result[k]
115
+ if (typeof v === 'string') {
116
+ const trimmed = v.trim()
117
+ result[k] = trimmed.length > 0 ? trimmed : undefined
118
+ }
119
+ }
120
+ return result as output<S>
121
+ }
122
+
123
+ export type { CvMeta, DefType, ZodSchema }
124
+ export {
125
+ coerceOptionals,
126
+ cvFileKindOf,
127
+ cvMetaOf,
128
+ defaultValue,
129
+ defaultValues,
130
+ elementOf,
131
+ enumToOptions,
132
+ isArrayType,
133
+ isBooleanType,
134
+ isDateType,
135
+ isNumberType,
136
+ isOptionalField,
137
+ isStringType,
138
+ pickValues,
139
+ requiredPartial,
140
+ unwrapZod
141
+ }