@volverjs/form-vue 1.0.1 → 1.1.0-beta.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.
package/src/utils.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import {
2
-
3
2
  ZodDefault,
4
3
  ZodObject,
5
4
  ZodEffects,
@@ -8,40 +7,180 @@ import {
8
7
  ZodOptional,
9
8
  ZodRecord,
10
9
  ZodArray,
11
- } from 'zod'
12
- import type { z, AnyZodObject, ZodTypeAny } from 'zod'
13
- import type { EffectType, FormSchema } from './types'
14
-
15
- export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema, original: Partial<z.infer<Schema>> & Record<string, unknown> = {}): Partial<z.infer<Schema>> {
16
- const getSchemaInnerType = <Type extends ZodTypeAny>(
17
- schema: EffectType<Type>,
18
- ) => {
19
- let toReturn = schema
20
- while (toReturn instanceof ZodEffects) {
21
- toReturn = toReturn.innerType()
10
+ ZodError,
11
+ } from 'zod/v3'
12
+ import {
13
+ $ZodError,
14
+ safeParse as z4SafeParse,
15
+ safeParseAsync as z4SafeParseAsync,
16
+ formatError as z4FormatError,
17
+ } from 'zod/v4/core'
18
+ import { toJSONSchema as z4toJSONSchema } from 'zod/v4'
19
+ import type * as z3 from 'zod/v3'
20
+ import type * as z4 from 'zod/v4/core'
21
+ import type { FormSchema, InferSchema, VvZodError, ZodIssue } from './types'
22
+
23
+ // Helper function to get the inner type of a Zod v3 schema
24
+ const getZod3SchemaInnerType = <Type extends z3.ZodTypeAny>(
25
+ schema:
26
+ | Type
27
+ | z3.ZodEffects<Type>
28
+ | z3.ZodEffects<ZodEffects<Type>>
29
+ | z3.ZodOptional<Type>,
30
+ ) => {
31
+ let toReturn = schema
32
+ while (toReturn instanceof ZodEffects) {
33
+ toReturn = toReturn.innerType()
34
+ }
35
+ if (toReturn instanceof ZodOptional) {
36
+ toReturn = toReturn._def.innerType
37
+ }
38
+ return toReturn
39
+ }
40
+
41
+ // Helper function to check if a Zod v3 schema is optional
42
+ const isZod3SchemaOptional = <Type extends z3.ZodTypeAny>(
43
+ schema:
44
+ | Type
45
+ | z3.ZodEffects<Type>
46
+ | z3.ZodEffects<ZodEffects<Type>>
47
+ | z3.ZodOptional<Type>
48
+ | z3.ZodNullable<Type>,
49
+ ) => {
50
+ let toReturn = schema
51
+ while (toReturn instanceof ZodEffects) {
52
+ toReturn = toReturn.innerType()
53
+ }
54
+ if (toReturn instanceof ZodOptional) {
55
+ return true
56
+ }
57
+ if (toReturn instanceof ZodNullable) {
58
+ return true
59
+ }
60
+ return false
61
+ }
62
+
63
+ // Helper function to determine the type of a value
64
+ function getValueType(value: unknown) {
65
+ if (Array.isArray(value)) {
66
+ return 'array'
67
+ }
68
+ if (value === null) {
69
+ return 'null'
70
+ }
71
+ return typeof value
72
+ }
73
+
74
+ // Helper function to check if a value matches a schema type
75
+ function isValueCompatibleWithSchema(value: unknown, subSchema: z4.JSONSchema.JSONSchema): boolean {
76
+ const valueType = getValueType(value)
77
+
78
+ if (subSchema.type) {
79
+ return subSchema.type === valueType
80
+ || (subSchema.type === 'integer' && valueType === 'number' && Number.isInteger(value as number))
81
+ }
82
+
83
+ // If no type specified, assume compatibility
84
+ return true
85
+ }
86
+
87
+ export function defaultObjectByJSONSchema(schema: z4.JSONSchema.JSONSchema, original?: unknown): unknown {
88
+ // Handle anyOf - find the best matching schema without unnecessary recursion
89
+ if (schema.anyOf && Array.isArray(schema.anyOf)) {
90
+ if (original !== undefined) {
91
+ // First pass: find exact type match
92
+ for (const subSchema of schema.anyOf) {
93
+ if (isValueCompatibleWithSchema(original, subSchema as z4.JSONSchema.JSONSchema)) {
94
+ return defaultObjectByJSONSchema(subSchema as z4.JSONSchema.JSONSchema, original)
95
+ }
96
+ }
97
+ // Second pass: try first schema that doesn't explicitly conflict
98
+ for (const subSchema of schema.anyOf) {
99
+ const subSchemaTyped = subSchema as z4.JSONSchema.JSONSchema
100
+ if (!subSchemaTyped.type || subSchemaTyped.type === 'object') {
101
+ return defaultObjectByJSONSchema(subSchemaTyped, original)
102
+ }
103
+ }
22
104
  }
23
- if (toReturn instanceof ZodOptional) {
24
- toReturn = toReturn._def.innerType
105
+ // Fallback to first schema
106
+ return defaultObjectByJSONSchema(schema.anyOf[0] as z4.JSONSchema.JSONSchema, original)
107
+ }
108
+
109
+ // Early return for non-object types
110
+ if (schema.type !== 'object' || !schema.properties) {
111
+ switch (schema.type) {
112
+ case 'string':
113
+ return typeof original === 'string' ? original : schema.default
114
+ case 'number':
115
+ case 'integer':
116
+ return typeof original === 'number' ? original : schema.default
117
+ case 'boolean':
118
+ return typeof original === 'boolean' ? original : schema.default
119
+ case 'null':
120
+ return original === null ? original : schema.default
121
+ case 'array':
122
+ if (Array.isArray(original) && schema.items) {
123
+ return original.map(item => defaultObjectByJSONSchema(schema.items as z4.JSONSchema.JSONSchema, item))
124
+ }
125
+ return schema.default
126
+ default:
127
+ return schema.default
25
128
  }
26
- return toReturn
27
- }
28
- const isSchemaOptional = <Type extends ZodTypeAny>(
29
- schema:
30
- | Type
31
- | ZodEffects<Type>
32
- | ZodEffects<ZodEffects<Type>>
33
- | ZodOptional<Type>,
34
- ) => {
35
- let toReturn = schema
36
- while (toReturn instanceof ZodEffects) {
37
- toReturn = toReturn.innerType()
129
+ }
130
+
131
+ // Object handling with optimizations
132
+ const properties = schema.properties as Record<string, z4.JSONSchema.JSONSchema>
133
+ const isOriginalObject = original && typeof original === 'object' && !Array.isArray(original)
134
+ const originalObj = isOriginalObject ? original as Record<string, unknown> : undefined
135
+
136
+ // Initialize result object more efficiently
137
+ const toReturn: Record<string, unknown> = {}
138
+
139
+ // Process schema properties first
140
+ for (const key in properties) {
141
+ const originalValue = originalObj?.[key]
142
+ toReturn[key] = defaultObjectByJSONSchema(properties[key], originalValue)
143
+ }
144
+
145
+ // Handle additional properties after schema properties
146
+ if (originalObj && schema.additionalProperties !== false) {
147
+ const schemaKeys = new Set(Object.keys(properties))
148
+
149
+ for (const [key, value] of Object.entries(originalObj)) {
150
+ if (!schemaKeys.has(key)) {
151
+ // Allow any additional properties as-is
152
+ toReturn[key] = value
153
+ }
38
154
  }
39
- if (toReturn instanceof ZodOptional) {
40
- return true
155
+ }
156
+
157
+ return toReturn
158
+ }
159
+
160
+ export const isZod4Schema = (schema: z3.ZodTypeAny | z4.$ZodType): schema is z4.$ZodType => {
161
+ if ('_zod' in schema) {
162
+ return true
163
+ }
164
+ return false
165
+ }
166
+
167
+ export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema, original: Partial<InferSchema<Schema>> & Record<string, unknown> = {}): Partial<InferSchema<Schema>> {
168
+ // zod v4
169
+ if (isZod4Schema(schema)) {
170
+ const jsonSchema = z4toJSONSchema(schema)
171
+ if (jsonSchema.type !== 'object' || !jsonSchema.properties) {
172
+ return original
41
173
  }
42
- return false
174
+ const parse = z4SafeParse(schema, original)
175
+ return defaultObjectByJSONSchema(jsonSchema, parse.success ? parse.data : original) as Partial<InferSchema<Schema>>
176
+ }
177
+
178
+ // zod v3
179
+ const innerType = getZod3SchemaInnerType(schema)
180
+
181
+ if (!(innerType instanceof ZodObject)) {
182
+ return original
43
183
  }
44
- const innerType = getSchemaInnerType<AnyZodObject>(schema)
45
184
  const unknownKeys
46
185
  = innerType instanceof ZodObject
47
186
  ? innerType._def.unknownKeys === 'passthrough'
@@ -49,12 +188,12 @@ export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema,
49
188
  return {
50
189
  ...(unknownKeys ? original : {}),
51
190
  ...Object.fromEntries(
52
- (Object.entries(innerType.shape) as [string, ZodTypeAny][]).map(
191
+ ('shape' in innerType ? Object.entries(innerType.shape) as [string, z3.ZodTypeAny][] : []).map(
53
192
  ([key, subSchema]) => {
54
193
  const originalValue = original[key]
55
- const isOptional = isSchemaOptional(subSchema)
56
- let innerType = getSchemaInnerType(subSchema)
57
- let defaultValue: Partial<z.infer<Schema>> | undefined
194
+ const isOptional = isZod3SchemaOptional(subSchema)
195
+ let innerType = getZod3SchemaInnerType(subSchema)
196
+ let defaultValue: Partial<InferSchema<Schema>> | undefined
58
197
  if (innerType instanceof ZodDefault) {
59
198
  defaultValue = innerType._def.defaultValue()
60
199
  innerType = innerType._def.innerType
@@ -79,7 +218,7 @@ export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema,
79
218
  && Array.isArray(originalValue)
80
219
  && originalValue.length
81
220
  ) {
82
- const arrayType = getSchemaInnerType(innerType._def.type)
221
+ const arrayType = getZod3SchemaInnerType(innerType._def.type)
83
222
  if (arrayType instanceof ZodObject) {
84
223
  return [
85
224
  key,
@@ -90,17 +229,17 @@ export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema,
90
229
  ? element
91
230
  : undefined) as Partial<
92
231
  typeof arrayType
93
- >,
232
+ >,
94
233
  ),
95
234
  ),
96
235
  ]
97
236
  }
98
237
  }
99
238
  if (innerType instanceof ZodRecord && originalValue) {
100
- const valueType = getSchemaInnerType(innerType._def.valueType)
239
+ const valueType = getZod3SchemaInnerType(innerType._def.valueType)
101
240
  if (valueType instanceof ZodObject) {
102
241
  return [key, Object.keys(originalValue).reduce((acc: Record<string, unknown>, recordKey: string) => {
103
- acc[recordKey] = defaultObjectBySchema(valueType, (originalValue as Record<string, unknown>)[recordKey] as Partial<any> & Record<string, unknown>)
242
+ acc[recordKey] = defaultObjectBySchema(valueType, originalValue[recordKey])
104
243
  return acc
105
244
  }, {})]
106
245
  }
@@ -112,7 +251,7 @@ export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema,
112
251
  innerType,
113
252
  originalValue
114
253
  && typeof originalValue === 'object'
115
- ? (originalValue as Partial<z.infer<Schema>> & Record<string, unknown>)
254
+ ? originalValue
116
255
  : defaultValue,
117
256
  ),
118
257
  ]
@@ -121,5 +260,26 @@ export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema,
121
260
  },
122
261
  ),
123
262
  ),
124
- } as Partial<z.infer<Schema>>
263
+ } as Partial<InferSchema<Schema>>
264
+ }
265
+
266
+ export const safeParseAsync = <T extends FormSchema>(schema: T, data: any) => {
267
+ if (isZod4Schema(schema)) {
268
+ return z4SafeParseAsync(schema, data)
269
+ }
270
+ return schema.safeParseAsync(data)
271
+ }
272
+
273
+ export const formatError = <T extends FormSchema>(schema: T, error: VvZodError<T>) => {
274
+ if (isZod4Schema(schema)) {
275
+ return z4FormatError(error as z4.$ZodError<T>)
276
+ }
277
+ return (error as z3.ZodError<T>).format()
278
+ }
279
+
280
+ export const formatIssues = (schema: FormSchema, issues: ZodIssue[]) => {
281
+ if (isZod4Schema(schema)) {
282
+ return z4FormatError(new $ZodError(issues as z4.$ZodIssue[]))
283
+ }
284
+ return new ZodError(issues as z3.ZodIssue[]).format()
125
285
  }