import { z } from 'zod' import { NEW_NAMESPACE_MARKER } from './CreateTableSheet.constants' const getValidRegex = (type: 'namespace' | 'table') => type === 'namespace' ? /^(?!aws)[a-z0-9](?:[a-z0-9_]*[a-z0-9])?$/ : /^[a-z0-9](?:[a-z0-9_]*[a-z0-9])?$/ const getErrorRegex = (type: 'namespace' | 'table') => type === 'namespace' ? /^(?:(?aws.*)|(?[^a-z0-9].*)|(?.*[^a-z0-9_].*)|(?.*[^a-z0-9]))$/ : /^(?:(?[^a-z0-9].*)|(?.*[^a-z0-9_].*)|(?.*[^a-z0-9]))$/ const validateName = ({ name, type }: { name: string; type: 'namespace' | 'table' }) => { const validRe = getValidRegex(type) if (validRe.test(name)) return undefined const errorRe = getErrorRegex(type) const match = name.match(errorRe)?.groups || {} if (match.starts_with_reserved) return "Namespace must not start with 'aws'" if (match.invalid_start) return 'Name must begin with a lowercase letter or number' if (match.invalid_end) return 'Name must end with a lowercase letter or number' if (match.invalid_char) return 'Name may only contain lowercase letters, numbers, and underscores' return 'Invalid name' } export const createFormSchema = () => z .object({ namespace: z.string().min(1, 'Please select a namespace'), newNamespace: z.string().max(255, 'Name must be within 255 characters').optional(), name: z .string() .min(1, 'Provide a name for your table') .max(255, 'Name must be within 255 characters'), columns: z .object({ name: z.string().min(1, 'Provide a name for your column'), type: z.string().min(1, 'Select a type for your column'), // For decimal type precision: z.number().optional(), scale: z.number().int().optional(), // For fixed type length: z.number().int().optional(), }) .array() .default([]), }) .superRefine((data, ctx) => { if (data.namespace === NEW_NAMESPACE_MARKER) { if (data.newNamespace) { const newNamespaceError = validateName({ name: data.newNamespace, type: 'namespace', }) if (newNamespaceError) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: newNamespaceError, path: ['newNamespace'], }) } } else { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Provide a name for your new namespace', path: ['newNamespace'], }) } } data.columns.forEach((column, index) => { if (column.type === 'decimal') { if (column.precision == null) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Required', path: [`columns.${index}.precision`], }) } if (column.scale == null) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Required', path: [`columns.${index}.scale`], }) } } if (column.type === 'fixed') { if (column.length == null) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Required', path: [`columns.${index}.length`], }) } } }) if (data.name) { const newTableError = validateName({ name: data.name, type: 'table' }) if (newTableError) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: newTableError, path: ['name'], }) } } })