@volverjs/form-vue 1.1.2 → 1.1.4

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
@@ -319,178 +319,141 @@ export const isZod4Schema = (schema: z3.ZodTypeAny | z4.$ZodType): schema is z4.
319
319
  return false
320
320
  }
321
321
 
322
- export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema, original: Partial<InferSchema<Schema>> & Record<string, unknown> = {}): Partial<InferSchema<Schema>> {
323
- // zod v4
324
- if (isZod4Schema(schema)) {
325
- const innerType = getZod4SchemaInnerType(schema)
326
- if (!isZod4Object(innerType)) {
327
- return original
328
- }
329
- const unknownKeys = innerType._zod.def.catchall && innerType._zod.def.catchall._zod.def.type !== 'never'
330
-
331
- return {
332
- ...(unknownKeys ? original : {}),
333
- ...Object.fromEntries(
334
- ('shape' in innerType._zod.def ? Object.entries(innerType._zod.def.shape) as [string, z4.$ZodType][] : []).map(
335
- ([key, subSchema]) => {
336
- const originalValue = original[key]
337
- const isOptional = isZod4SchemaOptional(subSchema)
338
- let innerType = getZod4SchemaInnerType(subSchema)
339
- let defaultValue: Partial<InferSchema<Schema>> | undefined
340
- if (isZod4Default(innerType)) {
341
- defaultValue = innerType._zod.def.defaultValue
342
- innerType = innerType._zod.def.innerType
343
- }
344
- if (
345
- originalValue === null
346
- && isZod4Nullable(innerType)
347
- ) {
348
- return [key, originalValue]
349
- }
350
- if ((originalValue === undefined || originalValue === null) && isOptional) {
351
- return [key, defaultValue]
352
- }
353
- if (innerType && originalValue !== undefined) {
354
- const parse = z4SafeParse(subSchema, originalValue)
355
- if (parse.success) {
356
- return [key, parse.data ?? defaultValue]
357
- }
358
- }
359
- if (
360
- isZod4Array(innerType)
361
- && Array.isArray(originalValue)
362
- ) {
363
- const arrayType = getZod4SchemaInnerType(innerType._zod.def.element)
364
- if (isZod4Object(arrayType)) {
365
- return [
366
- key,
367
- originalValue.map((element: unknown) =>
368
- defaultObjectBySchema(
369
- arrayType,
370
- (element && typeof element === 'object'
371
- ? element
372
- : undefined) as Partial<
373
- typeof arrayType
374
- >,
375
- ),
376
- ),
377
- ]
378
- }
379
- return [key, originalValue]
380
- }
381
- if (isZod4Record(innerType) && originalValue) {
382
- const valueType = getZod4SchemaInnerType(innerType._zod.def.valueType)
383
- if (isZod4Object(valueType)) {
384
- return [key, Object.keys(originalValue).reduce((acc: Record<string, unknown>, recordKey: string) => {
385
- acc[recordKey] = defaultObjectBySchema(valueType, originalValue[recordKey])
386
- return acc
387
- }, {})]
388
- }
389
- return [key, originalValue]
390
- }
391
- if (isZod4Object(innerType)) {
392
- return [
393
- key,
394
- defaultObjectBySchema(
395
- innerType,
396
- originalValue
397
- && typeof originalValue === 'object'
398
- ? originalValue
399
- : defaultValue,
400
- ),
401
- ]
402
- }
403
- return [key, defaultValue]
404
- },
405
- ),
406
- ),
407
- } as Partial<InferSchema<Schema>>
322
+ /**
323
+ * Adapter that abstracts the version-specific Zod internals (zod v3 vs v4) so
324
+ * that the `defaultObjectBySchema` algorithm can be written once.
325
+ */
326
+ interface ZodAdapter {
327
+ getInnerType: (schema: any) => any
328
+ isObject: (schema: any) => boolean
329
+ hasUnknownKeys: (objectSchema: any) => boolean
330
+ getShapeEntries: (objectSchema: any) => [string, any][]
331
+ isOptional: (schema: any) => boolean
332
+ isDefault: (schema: any) => boolean
333
+ getDefaultValue: (schema: any) => unknown
334
+ getDefaultInnerType: (schema: any) => any
335
+ isNullable: (schema: any) => boolean
336
+ safeParse: (schema: any, value: unknown) => { success: boolean, data?: unknown }
337
+ isArray: (schema: any) => boolean
338
+ getArrayElement: (schema: any) => any
339
+ isRecord: (schema: any) => boolean
340
+ getRecordValue: (schema: any) => any
341
+ }
342
+
343
+ const zod3Adapter: ZodAdapter = {
344
+ getInnerType: getZod3SchemaInnerType,
345
+ isObject: isZod3Object,
346
+ hasUnknownKeys: schema => schema._def.unknownKeys === 'passthrough',
347
+ getShapeEntries: schema => ('shape' in schema ? Object.entries(schema.shape) : []),
348
+ isOptional: isZod3SchemaOptional,
349
+ isDefault: isZod3Default,
350
+ getDefaultValue: schema => schema._def.defaultValue(),
351
+ getDefaultInnerType: schema => schema._def.innerType,
352
+ isNullable: isZod3Nullable,
353
+ safeParse: (schema, value) => schema.safeParse(value),
354
+ isArray: isZod3Array,
355
+ getArrayElement: schema => schema._def.type,
356
+ isRecord: isZod3Record,
357
+ getRecordValue: schema => schema._def.valueType,
358
+ }
359
+
360
+ const zod4Adapter: ZodAdapter = {
361
+ getInnerType: getZod4SchemaInnerType,
362
+ isObject: isZod4Object,
363
+ hasUnknownKeys: schema => Boolean(schema._zod.def.catchall && schema._zod.def.catchall._zod.def.type !== 'never'),
364
+ getShapeEntries: schema => ('shape' in schema._zod.def ? Object.entries(schema._zod.def.shape) : []),
365
+ isOptional: isZod4SchemaOptional,
366
+ isDefault: isZod4Default,
367
+ getDefaultValue: schema => schema._zod.def.defaultValue,
368
+ getDefaultInnerType: schema => schema._zod.def.innerType,
369
+ isNullable: isZod4Nullable,
370
+ safeParse: (schema, value) => z4SafeParse(schema, value),
371
+ isArray: isZod4Array,
372
+ getArrayElement: schema => schema._zod.def.element,
373
+ isRecord: isZod4Record,
374
+ getRecordValue: schema => schema._zod.def.valueType,
375
+ }
376
+
377
+ // Resolves the default value for an array shape entry, recursing into object elements.
378
+ function _resolveArrayValue(adapter: ZodAdapter, innerType: any, originalValue: unknown[]): unknown {
379
+ const arrayType = adapter.getInnerType(adapter.getArrayElement(innerType))
380
+ if (adapter.isObject(arrayType)) {
381
+ return originalValue.map(element => _defaultObjectFromShape(adapter, arrayType, element))
408
382
  }
383
+ return originalValue
384
+ }
409
385
 
410
- // zod v3
411
- const innerType = getZod3SchemaInnerType(schema)
386
+ // Resolves the default value for a record shape entry, recursing into object values.
387
+ function _resolveRecordValue(adapter: ZodAdapter, innerType: any, originalValue: object): unknown {
388
+ const valueType = adapter.getInnerType(adapter.getRecordValue(innerType))
389
+ if (!adapter.isObject(valueType)) {
390
+ return originalValue
391
+ }
392
+ return Object.keys(originalValue).reduce<Record<string, unknown>>((acc, recordKey) => {
393
+ acc[recordKey] = _defaultObjectFromShape(adapter, valueType, (originalValue as Record<string, unknown>)[recordKey])
394
+ return acc
395
+ }, {})
396
+ }
412
397
 
413
- if (!isZod3Object(innerType)) {
414
- return original
398
+ // Resolves the default value for a single shape entry of a Zod object schema.
399
+ function _resolveShapeEntry(adapter: ZodAdapter, key: string, subSchema: any, originalValue: unknown): [string, unknown] {
400
+ const isOptional = adapter.isOptional(subSchema)
401
+ let innerType = adapter.getInnerType(subSchema)
402
+ let defaultValue: unknown
403
+ if (adapter.isDefault(innerType)) {
404
+ defaultValue = adapter.getDefaultValue(innerType)
405
+ innerType = adapter.getDefaultInnerType(innerType)
406
+ }
407
+ if (originalValue === null && adapter.isNullable(innerType)) {
408
+ return [key, originalValue]
409
+ }
410
+ if ((originalValue === undefined || originalValue === null) && isOptional) {
411
+ return [key, defaultValue]
412
+ }
413
+ if (innerType && originalValue !== undefined) {
414
+ const parse = adapter.safeParse(subSchema, originalValue)
415
+ if (parse.success) {
416
+ return [key, parse.data ?? defaultValue]
417
+ }
415
418
  }
416
- const unknownKeys = innerType._def.unknownKeys === 'passthrough'
419
+ if (adapter.isArray(innerType) && Array.isArray(originalValue)) {
420
+ return [key, _resolveArrayValue(adapter, innerType, originalValue)]
421
+ }
422
+ if (adapter.isRecord(innerType) && originalValue) {
423
+ return [key, _resolveRecordValue(adapter, innerType, originalValue as object)]
424
+ }
425
+ if (adapter.isObject(innerType)) {
426
+ return [key, _defaultObjectFromShape(
427
+ adapter,
428
+ innerType,
429
+ originalValue && typeof originalValue === 'object' ? originalValue : defaultValue,
430
+ )]
431
+ }
432
+ return [key, defaultValue]
433
+ }
434
+
435
+ // Builds the default object for an (already unwrapped) Zod object schema.
436
+ function _defaultObjectFromShape(adapter: ZodAdapter, objectSchema: any, original: unknown): Record<string, unknown> {
437
+ const safeOriginal = (original && typeof original === 'object' && !Array.isArray(original))
438
+ ? original as Record<string, unknown>
439
+ : {}
417
440
  return {
418
- ...(unknownKeys ? original : {}),
441
+ ...(adapter.hasUnknownKeys(objectSchema) ? safeOriginal : {}),
419
442
  ...Object.fromEntries(
420
- ('shape' in innerType ? Object.entries(innerType.shape) as [string, z3.ZodTypeAny][] : []).map(
421
- ([key, subSchema]) => {
422
- const originalValue = original[key]
423
- const isOptional = isZod3SchemaOptional(subSchema)
424
- let innerType = getZod3SchemaInnerType(subSchema)
425
- let defaultValue: Partial<InferSchema<Schema>> | undefined
426
- if (isZod3Default(innerType)) {
427
- defaultValue = innerType._def.defaultValue()
428
- innerType = innerType._def.innerType
429
- }
430
- if (
431
- originalValue === null
432
- && isZod3Nullable(innerType)
433
- ) {
434
- return [key, originalValue]
435
- }
436
- if ((originalValue === undefined || originalValue === null) && isOptional) {
437
- return [key, defaultValue]
438
- }
439
- if (innerType && originalValue !== undefined) {
440
- const parse = subSchema.safeParse(originalValue)
441
- if (parse.success) {
442
- return [key, parse.data ?? defaultValue]
443
- }
444
- }
445
- if (
446
- isZod3Array(innerType)
447
- && Array.isArray(originalValue)
448
- ) {
449
- const arrayType = getZod3SchemaInnerType(innerType._def.type)
450
- if (isZod3Object(arrayType)) {
451
- return [
452
- key,
453
- originalValue.map((element: unknown) =>
454
- defaultObjectBySchema(
455
- arrayType,
456
- (element && typeof element === 'object'
457
- ? element
458
- : undefined) as Partial<
459
- typeof arrayType
460
- >,
461
- ),
462
- ),
463
- ]
464
- }
465
- return [key, originalValue]
466
- }
467
- if (isZod3Record(innerType) && originalValue) {
468
- const valueType = getZod3SchemaInnerType(innerType._def.valueType)
469
- if (isZod3Object(valueType)) {
470
- return [key, Object.keys(originalValue).reduce((acc: Record<string, unknown>, recordKey: string) => {
471
- acc[recordKey] = defaultObjectBySchema(valueType, originalValue[recordKey])
472
- return acc
473
- }, {})]
474
- }
475
- return [key, originalValue]
476
- }
477
- if (isZod3Object(innerType)) {
478
- return [
479
- key,
480
- defaultObjectBySchema(
481
- innerType,
482
- originalValue
483
- && typeof originalValue === 'object'
484
- ? originalValue
485
- : defaultValue,
486
- ),
487
- ]
488
- }
489
- return [key, defaultValue]
490
- },
443
+ adapter.getShapeEntries(objectSchema).map(([key, subSchema]) =>
444
+ _resolveShapeEntry(adapter, key, subSchema, safeOriginal[key]),
491
445
  ),
492
446
  ),
493
- } as Partial<InferSchema<Schema>>
447
+ }
448
+ }
449
+
450
+ export function defaultObjectBySchema<Schema extends FormSchema>(schema: Schema, original: Partial<InferSchema<Schema>> & Record<string, unknown> = {}): Partial<InferSchema<Schema>> {
451
+ const adapter = isZod4Schema(schema) ? zod4Adapter : zod3Adapter
452
+ const innerType = adapter.getInnerType(schema)
453
+ if (!adapter.isObject(innerType)) {
454
+ return original
455
+ }
456
+ return _defaultObjectFromShape(adapter, innerType, original) as Partial<InferSchema<Schema>>
494
457
  }
495
458
 
496
459
  export const safeParseAsync = <T extends FormSchema>(schema: T, data: any) => {