rune-form
Version:
Type-safe reactive form builder for Svelte 5
169 lines (168 loc) • 5.79 kB
JavaScript
/* eslint-disable @typescript-eslint/no-explicit-any */
// This file handles compatibility between Zod v3 and v4
// We need to use 'any' types here because we're dealing with different Zod internal structures
// Version-agnostic shape getter for ZodObject
export function getObjectShape(schema) {
// Both v3 and v4 have _def property
if (schema._def) {
// In v3 and v4, shape can be a function or direct property
if (typeof schema._def.shape === 'function') {
return schema._def.shape();
}
else if (schema._def.shape) {
return schema._def.shape;
}
}
// Fallback: try direct shape property (some edge cases)
if (schema.shape) {
return typeof schema.shape === 'function' ? schema.shape() : schema.shape;
}
return {};
}
// Version-agnostic array element getter
export function getArrayElement(schema) {
// Both v3 and v4 store element in _def
if (schema._def) {
// v4 uses 'element', v3 uses 'type'
if (schema._def.element) {
return schema._def.element;
}
else if (schema._def.type && typeof schema._def.type === 'object') {
return schema._def.type;
}
}
// Fallback to a schema that accepts any value
return {
_def: { type: 'unknown', typeName: 'ZodUnknown' },
def: { type: 'unknown' },
parse: (val) => val,
safeParse: (val) => ({ success: true, data: val })
};
}
// Version-agnostic inner type getter (for Optional, Nullable, Default)
export function getInnerType(schema) {
if (schema._def) {
// Both v3 and v4 use innerType
if (schema._def.innerType) {
return schema._def.innerType;
}
// Some schemas might use 'type' instead (but only if it's an object)
if (schema._def.type && typeof schema._def.type === 'object') {
return schema._def.type;
}
}
// For ZodEffects and other wrappers
if (schema.innerType) {
return schema.innerType;
}
return schema;
}
// Version-agnostic default value getter
export function getDefaultValue(schema) {
if (schema._def && schema._def.defaultValue !== undefined) {
const defaultValue = schema._def.defaultValue;
// Handle function defaults or Getter properties
if (typeof defaultValue === 'function') {
return defaultValue();
}
return defaultValue;
}
return undefined;
}
// Version-agnostic check for optional/nullable/default
export function isOptionalish(schema) {
// Check by method if available (works in both versions)
if (typeof schema.isOptional === 'function' && schema.isOptional()) {
return true;
}
if (typeof schema.isNullable === 'function' && schema.isNullable()) {
return true;
}
// Check by type name (v3 uses typeName, v4 uses type)
const typeName = schema._def?.typeName;
const type = schema._def?.type;
return (typeName === 'ZodOptional' ||
typeName === 'ZodNullable' ||
typeName === 'ZodDefault' ||
type === 'optional' ||
type === 'nullable' ||
type === 'default');
}
// Version-agnostic unwrap (remove Optional/Nullable/Default wrappers)
export function unwrapSchema(schema) {
let current = schema;
// Keep unwrapping while it's wrapped
while (isOptionalish(current)) {
const inner = getInnerType(current);
if (inner === current)
break; // Prevent infinite loop
current = inner;
}
return current;
}
// Version-agnostic checks getter for ZodNumber
export function getNumberChecks(schema) {
// Check both _def and def
if (schema._def && schema._def.checks) {
return schema._def.checks;
}
if (schema.def && schema.def.checks) {
return schema.def.checks;
}
return [];
}
// Version-agnostic union options getter
export function getUnionOptions(schema) {
if (schema._def) {
if (schema._def.options) {
return schema._def.options;
}
// Some versions might use 'types' instead
if (schema._def.types) {
return schema._def.types;
}
}
return [];
}
// Version-agnostic type name checker
export function isZodType(schema, typeName) {
if (!schema)
return false;
// Check v3 style (with typeName)
if (schema._def?.typeName === `Zod${typeName}`) {
return true;
}
// Check v4 style (with type)
const v4TypeMap = {
Object: 'object',
Array: 'array',
Number: 'number',
String: 'string',
Boolean: 'boolean',
Date: 'date',
Union: 'union',
Optional: 'optional',
Nullable: 'nullable',
Default: 'default'
};
const v4Type = v4TypeMap[typeName];
if (v4Type && schema._def?.type === v4Type) {
return true;
}
// Also check def without underscore (v4)
if (v4Type && schema.def?.type === v4Type) {
return true;
}
return false;
}
// Export convenience type checkers
export const isZodObject = (schema) => isZodType(schema, 'Object');
export const isZodArray = (schema) => isZodType(schema, 'Array');
export const isZodNumber = (schema) => isZodType(schema, 'Number');
export const isZodString = (schema) => isZodType(schema, 'String');
export const isZodBoolean = (schema) => isZodType(schema, 'Boolean');
export const isZodDate = (schema) => isZodType(schema, 'Date');
export const isZodUnion = (schema) => isZodType(schema, 'Union');
export const isZodOptional = (schema) => isZodType(schema, 'Optional');
export const isZodNullable = (schema) => isZodType(schema, 'Nullable');
export const isZodDefault = (schema) => isZodType(schema, 'Default');