@volverjs/form-vue 1.1.0-beta.4 → 1.1.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.
package/src/utils.ts CHANGED
@@ -7,31 +7,74 @@ import {
7
7
  safeParseAsync as z4SafeParseAsync,
8
8
  formatError as z4FormatError,
9
9
  } from 'zod/v4/core'
10
- import { toJSONSchema as z4toJSONSchema } from 'zod/v4'
11
10
  import type * as z3 from 'zod/v3'
12
11
  import type * as z4 from 'zod/v4/core'
13
12
  import type { FormSchema, InferSchema, VvZodError, ZodIssue } from './types'
14
13
 
14
+ // Helper function to determine the type of a value
15
+ function _getValueType(value: unknown) {
16
+ if (Array.isArray(value)) {
17
+ return 'array'
18
+ }
19
+ if (value === null) {
20
+ return 'null'
21
+ }
22
+ return typeof value
23
+ }
24
+
25
+ // Helper function to check if a value matches a schema type
26
+ function _isValueCompatibleWithSchema(value: unknown, subSchema: z4.JSONSchema.JSONSchema): boolean {
27
+ const valueType = _getValueType(value)
28
+
29
+ if (subSchema.type) {
30
+ return subSchema.type === valueType
31
+ || (subSchema.type === 'integer' && valueType === 'number' && Number.isInteger(value as number))
32
+ }
33
+
34
+ // If no type specified, assume compatibility
35
+ return true
36
+ }
37
+
15
38
  export const isZod3Object = (value: z3.ZodTypeAny): value is z3.ZodObject<any> => {
16
39
  return value._def.typeName === 'ZodObject'
17
40
  }
18
41
 
42
+ export const isZod4Object = (value: z4.$ZodType): value is z4.$ZodObject<any> => {
43
+ return value._zod.def.type === 'object'
44
+ }
45
+
19
46
  export const isZod3Default = (value: z3.ZodTypeAny): value is z3.ZodDefault<any> => {
20
47
  return value._def.typeName === 'ZodDefault'
21
48
  }
22
49
 
50
+ export const isZod4Default = (value: z4.$ZodType): value is z4.$ZodDefault<any> => {
51
+ return value._zod.def.type === 'default'
52
+ }
53
+
23
54
  export const isZod3Nullable = (value: z3.ZodTypeAny): value is z3.ZodNullable<any> => {
24
55
  return value._def.typeName === 'ZodNullable'
25
56
  }
26
57
 
58
+ export const isZod4Nullable = (value: z4.$ZodType): value is z4.$ZodNullable<any> => {
59
+ return value._zod.def.type === 'nullable'
60
+ }
61
+
27
62
  export const isZod3Record = (value: z3.ZodTypeAny): value is z3.ZodRecord<any, any> => {
28
63
  return value._def.typeName === 'ZodRecord'
29
64
  }
30
65
 
66
+ export const isZod4Record = (value: z4.$ZodType): value is z4.$ZodRecord<any, any> => {
67
+ return value._zod.def.type === 'record'
68
+ }
69
+
31
70
  export const isZod3Array = (value: z3.ZodTypeAny): value is z3.ZodArray<any> => {
32
71
  return value._def.typeName === 'ZodArray'
33
72
  }
34
73
 
74
+ export const isZod4Array = (value: z4.$ZodType): value is z4.$ZodArray<any> => {
75
+ return value._zod.def.type === 'array'
76
+ }
77
+
35
78
  export const isZod3Effects = (value: z3.ZodTypeAny): value is z3.ZodEffects<any> => {
36
79
  return value._def.typeName === 'ZodEffects'
37
80
  }
@@ -40,64 +83,103 @@ export const isZod3Optional = (value: z3.ZodTypeAny): value is z3.ZodOptional<an
40
83
  return value._def.typeName === 'ZodOptional'
41
84
  }
42
85
 
43
- // Helper function to get the inner type of a Zod v3 schema
44
- const getZod3SchemaInnerType = <Type extends z3.ZodTypeAny>(
86
+ export const isZod4Optional = (value: z4.$ZodType): value is z4.$ZodOptional<any> => {
87
+ return value._zod.def.type === 'optional'
88
+ }
89
+
90
+ // zod 4 replacements for ZodEffects
91
+ export const isZod4Pipe = (value: z4.$ZodType): value is z4.$ZodPipe<any> => {
92
+ return value._zod.def.type === 'pipe'
93
+ }
94
+
95
+ export const isZod4Transform = (value: z4.$ZodType): value is z4.$ZodTransform<any> => {
96
+ return value._zod.def.type === 'transform'
97
+ }
98
+
99
+ function _loopOnZod3Effects<Type extends z3.ZodTypeAny>(
100
+ schema: Type | z3.ZodEffects<Type> | z3.ZodEffects<z3.ZodEffects<Type>>,
101
+ ) {
102
+ let toReturn = schema
103
+ while (isZod3Effects(toReturn)) {
104
+ toReturn = toReturn.innerType()
105
+ }
106
+ return toReturn
107
+ }
108
+
109
+ function _loopOnZod4Pipe<Type extends z4.$ZodType>(
110
+ schema:
111
+ | Type
112
+ | z4.$ZodPipe<Type>
113
+ | z4.$ZodPipe<any, Type>,
114
+ ) {
115
+ let toReturn = schema
116
+ while (isZod4Pipe(toReturn)) {
117
+ if (isZod4Transform(toReturn._zod.def.out)) {
118
+ toReturn = toReturn._zod.def.in
119
+ }
120
+ else {
121
+ toReturn = toReturn._zod.def.out as Type
122
+ }
123
+ }
124
+ return toReturn
125
+ }
126
+
127
+ // Helper function to get the inner type of a Zod schema
128
+ export const getZod3SchemaInnerType = <Type extends z3.ZodTypeAny>(
45
129
  schema:
46
130
  | Type
47
131
  | z3.ZodEffects<Type>
48
132
  | z3.ZodEffects<z3.ZodEffects<Type>>
49
133
  | z3.ZodOptional<Type>,
50
134
  ) => {
51
- let toReturn = schema
52
- while (isZod3Effects(toReturn)) {
53
- toReturn = toReturn.innerType()
54
- }
135
+ let toReturn = _loopOnZod3Effects(schema)
55
136
  if (isZod3Optional(toReturn)) {
56
137
  toReturn = toReturn._def.innerType
57
138
  }
58
139
  return toReturn
59
140
  }
60
141
 
61
- // Helper function to check if a Zod v3 schema is optional
62
- const isZod3SchemaOptional = <Type extends z3.ZodTypeAny>(
142
+ export const getZod4SchemaInnerType = <Type extends z4.$ZodType>(
143
+ schema:
144
+ | Type
145
+ | z4.$ZodPipe<Type>
146
+ | z4.$ZodPipe<any, Type>
147
+ | z4.$ZodOptional<Type>,
148
+ ) => {
149
+ let toReturn = _loopOnZod4Pipe(schema)
150
+ if (isZod4Optional(toReturn)) {
151
+ toReturn = toReturn._zod.def.innerType
152
+ }
153
+ return toReturn
154
+ }
155
+
156
+ // Helper function to check if a Zod schema is optional
157
+ export const isZod3SchemaOptional = <Type extends z3.ZodTypeAny>(
63
158
  schema:
64
159
  | Type
65
160
  | z3.ZodEffects<Type>
66
161
  | z3.ZodEffects<z3.ZodEffects<Type>>
67
162
  | z3.ZodOptional<Type>,
68
163
  ) => {
69
- let toReturn = schema
70
- while (isZod3Effects(toReturn)) {
71
- toReturn = toReturn.innerType()
72
- }
164
+ const toReturn = _loopOnZod3Effects(schema)
73
165
  if (isZod3Optional(toReturn)) {
74
166
  return true
75
167
  }
76
168
  return false
77
169
  }
78
170
 
79
- // Helper function to determine the type of a value
80
- function getValueType(value: unknown) {
81
- if (Array.isArray(value)) {
82
- return 'array'
83
- }
84
- if (value === null) {
85
- return 'null'
86
- }
87
- return typeof value
88
- }
89
-
90
- // Helper function to check if a value matches a schema type
91
- function isValueCompatibleWithSchema(value: unknown, subSchema: z4.JSONSchema.JSONSchema): boolean {
92
- const valueType = getValueType(value)
93
-
94
- if (subSchema.type) {
95
- return subSchema.type === valueType
96
- || (subSchema.type === 'integer' && valueType === 'number' && Number.isInteger(value as number))
171
+ export const isZod4SchemaOptional = <Type extends z4.$ZodType>(
172
+ schema:
173
+ | Type
174
+ | z4.$ZodPipe<Type>
175
+ | z4.$ZodPipe<any, Type>
176
+ | z4.$ZodOptional<Type>,
177
+ ) => {
178
+ const toReturn = _loopOnZod4Pipe(schema)
179
+ if (isZod4Optional(toReturn)) {
180
+ return true
97
181
  }
98
-
99
- // If no type specified, assume compatibility
100
- return true
182
+ return false
101
183
  }
102
184
 
103
185
  export function defaultObjectByJSONSchema(schema: z4.JSONSchema.JSONSchema, original?: unknown): unknown {
@@ -106,7 +188,7 @@ export function defaultObjectByJSONSchema(schema: z4.JSONSchema.JSONSchema, orig
106
188
  if (original !== undefined) {
107
189
  // First pass: find exact type match
108
190
  for (const subSchema of schema.anyOf) {
109
- if (isValueCompatibleWithSchema(original, subSchema as z4.JSONSchema.JSONSchema)) {
191
+ if (_isValueCompatibleWithSchema(original, subSchema as z4.JSONSchema.JSONSchema)) {
110
192
  return defaultObjectByJSONSchema(subSchema as z4.JSONSchema.JSONSchema, original)
111
193
  }
112
194
  }
@@ -183,12 +265,88 @@ export const isZod4Schema = (schema: z3.ZodTypeAny | z4.$ZodType): schema is z4.
183
265
  export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema, original: Partial<InferSchema<Schema>> & Record<string, unknown> = {}): Partial<InferSchema<Schema>> {
184
266
  // zod v4
185
267
  if (isZod4Schema(schema)) {
186
- const jsonSchema = z4toJSONSchema(schema)
187
- if (jsonSchema.type !== 'object' || !jsonSchema.properties) {
268
+ const innerType = getZod4SchemaInnerType(schema)
269
+ if (!isZod4Object(innerType)) {
188
270
  return original
189
271
  }
190
- const parse = z4SafeParse(schema, original)
191
- return defaultObjectByJSONSchema(jsonSchema, parse.success ? parse.data : original) as Partial<InferSchema<Schema>>
272
+ const unknownKeys = innerType._zod.def.catchall && innerType._zod.def.catchall._zod.def.type !== 'never'
273
+
274
+ return {
275
+ ...(unknownKeys ? original : {}),
276
+ ...Object.fromEntries(
277
+ ('shape' in innerType._zod.def ? Object.entries(innerType._zod.def.shape) as [string, z4.$ZodType][] : []).map(
278
+ ([key, subSchema]) => {
279
+ const originalValue = original[key]
280
+ const isOptional = isZod4SchemaOptional(subSchema)
281
+ let innerType = getZod4SchemaInnerType(subSchema)
282
+ let defaultValue: Partial<InferSchema<Schema>> | undefined
283
+ if (isZod4Default(innerType)) {
284
+ defaultValue = innerType._zod.def.defaultValue
285
+ innerType = innerType._zod.def.innerType
286
+ }
287
+ if (
288
+ originalValue === null
289
+ && isZod4Nullable(innerType)
290
+ ) {
291
+ return [key, originalValue]
292
+ }
293
+ if ((originalValue === undefined || originalValue === null) && isOptional) {
294
+ return [key, defaultValue]
295
+ }
296
+ if (innerType) {
297
+ const parse = z4SafeParse(subSchema, originalValue)
298
+ if (parse.success) {
299
+ return [key, parse.data ?? defaultValue]
300
+ }
301
+ }
302
+ if (
303
+ isZod4Array(innerType)
304
+ && Array.isArray(originalValue)
305
+ && originalValue.length
306
+ ) {
307
+ const arrayType = getZod4SchemaInnerType(innerType._zod.def.element)
308
+ if (isZod4Object(arrayType)) {
309
+ return [
310
+ key,
311
+ originalValue.map((element: unknown) =>
312
+ defaultObjectBySchema(
313
+ arrayType,
314
+ (element && typeof element === 'object'
315
+ ? element
316
+ : undefined) as Partial<
317
+ typeof arrayType
318
+ >,
319
+ ),
320
+ ),
321
+ ]
322
+ }
323
+ }
324
+ if (isZod4Record(innerType) && originalValue) {
325
+ const valueType = getZod4SchemaInnerType(innerType._zod.def.valueType)
326
+ if (isZod4Object(valueType)) {
327
+ return [key, Object.keys(originalValue).reduce((acc: Record<string, unknown>, recordKey: string) => {
328
+ acc[recordKey] = defaultObjectBySchema(valueType, originalValue[recordKey])
329
+ return acc
330
+ }, {})]
331
+ }
332
+ }
333
+ if (isZod4Object(innerType)) {
334
+ return [
335
+ key,
336
+ defaultObjectBySchema(
337
+ innerType,
338
+ originalValue
339
+ && typeof originalValue === 'object'
340
+ ? originalValue
341
+ : defaultValue,
342
+ ),
343
+ ]
344
+ }
345
+ return [key, defaultValue]
346
+ },
347
+ ),
348
+ ),
349
+ } as Partial<InferSchema<Schema>>
192
350
  }
193
351
 
194
352
  // zod v3
@@ -242,7 +400,7 @@ export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema,
242
400
  ? element
243
401
  : undefined) as Partial<
244
402
  typeof arrayType
245
- >,
403
+ >,
246
404
  ),
247
405
  ),
248
406
  ]