UNPKG

prisma-zod-generator

Version:

Prisma 2+ generator to emit Zod schemas from your Prisma schema

2,662 lines 93.9 kB
"use strict";
/**
 * Zod Comment Parser
 *
 * Parses @zod validation annotations from Prisma schema field comments
 * to generate enhanced Zod validation schemas.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractFieldComments = extractFieldComments;
exports.extractFieldComment = extractFieldComment;
exports.normalizeComment = normalizeComment;
exports.detectZodAnnotations = detectZodAnnotations;
exports.validateCommentStructure = validateCommentStructure;
exports.getExtractionStatistics = getExtractionStatistics;
exports.filterZodComments = filterZodComments;
exports.getAllExtractionErrors = getAllExtractionErrors;
exports.parseZodAnnotations = parseZodAnnotations;
exports.validateZodAnnotations = validateZodAnnotations;
exports.mapAnnotationsToZodSchema = mapAnnotationsToZodSchema;
exports.generateCompleteZodSchema = generateCompleteZodSchema;
exports.getBaseZodType = getBaseZodType;
exports.getBaseZodTypeForField = getBaseZodTypeForField;
exports.getRequiredImports = getRequiredImports;
exports.detectCustomImports = detectCustomImports;
exports.parseCustomImports = parseCustomImports;
exports.extractModelCustomImports = extractModelCustomImports;
exports.extractFieldCustomImports = extractFieldCustomImports;
const logger_1 = require("../utils/logger");
/**
 * Extract comments from Prisma DMMF model fields
 *
 * This function processes Prisma model field definitions and extracts
 * comment data with proper normalization and context for downstream parsing.
 *
 * @param models - Array of Prisma DMMF models
 * @returns Array of extracted field comments with context
 */
function extractFieldComments(models) {
    const extractedComments = [];
    for (const model of models) {
        for (const field of model.fields) {
            const comment = field.documentation || '';
            // Skip fields without comments
            if (!comment.trim()) {
                continue;
            }
            try {
                const context = {
                    modelName: model.name,
                    fieldName: field.name,
                    fieldType: field.type,
                    comment: comment,
                    isOptional: !field.isRequired,
                    isList: field.isList,
                };
                const extractedComment = extractFieldComment(context);
                extractedComments.push(extractedComment);
            }
            catch (error) {
                // Add error handling for individual field processing failures
                extractedComments.push({
                    context: {
                        modelName: model.name,
                        fieldName: field.name,
                        fieldType: field.type,
                        comment: comment,
                        isOptional: !field.isRequired,
                        isList: field.isList,
                    },
                    normalizedComment: '',
                    hasZodAnnotations: false,
                    extractionErrors: [
                        `Failed to extract comment: ${error instanceof Error ? error.message : String(error)}`,
                    ],
                });
            }
        }
    }
    return extractedComments;
}
/**
 * Extract and normalize a single field comment
 *
 * @param context - Field context with comment and metadata
 * @returns Extracted comment with processing information
 */
function extractFieldComment(context) {
    const errors = [];
    try {
        // Normalize whitespace and handle multi-line comments
        const normalizedComment = normalizeComment(context.comment);
        // Check if comment contains @zod annotations
        const hasZodAnnotations = detectZodAnnotations(normalizedComment);
        // Validate comment structure
        const structureErrors = validateCommentStructure(normalizedComment, context);
        errors.push(...structureErrors);
        return {
            context,
            normalizedComment,
            hasZodAnnotations,
            extractionErrors: errors,
        };
    }
    catch (error) {
        errors.push(`Comment extraction failed: ${error instanceof Error ? error.message : String(error)}`);
        return {
            context,
            normalizedComment: '',
            hasZodAnnotations: false,
            extractionErrors: errors,
        };
    }
}
/**
 * Normalize comment content for consistent processing
 *
 * Handles multi-line comments, whitespace normalization, and ensures
 * consistent format for downstream parsing.
 *
 * @param comment - Raw comment from Prisma field
 * @returns Normalized comment string
 */
function normalizeComment(comment) {
    if (!comment || typeof comment !== 'string') {
        return '';
    }
    // Handle multi-line comments by preserving @zod annotations on separate lines
    const lines = comment.split(/\r?\n/).map((line) => line.trim());
    // Remove empty lines and normalize whitespace
    const normalizedLines = lines
        .filter((line) => line.length > 0)
        .map((line) => line.replace(/\s+/g, ' ').trim());
    return normalizedLines.join(' ');
}
/**
 * Detect if comment contains @zod validation annotations
 *
 * Performs a quick check to determine if the comment contains
 * any @zod annotations before more expensive parsing operations.
 *
 * @param comment - Normalized comment string
 * @returns True if @zod annotations are detected
 */
function detectZodAnnotations(comment) {
    if (!comment) {
        return false;
    }
    // Look for @zod patterns (case-insensitive) - allow optional whitespace between @zod and .
    const zodPattern = /@zod\s*\./i;
    return zodPattern.test(comment);
}
/**
 * Validate comment structure and detect potential issues
 *
 * Performs structural validation to catch common comment formatting
 * issues that could cause parsing problems later.
 *
 * @param comment - Normalized comment string
 * @param context - Field context for error reporting
 * @returns Array of validation errors
 */
function validateCommentStructure(comment, context) {
    const errors = [];
    if (!comment) {
        return errors; // Empty comments are valid
    }
    // Check for unmatched parentheses in @zod annotations
    if (detectZodAnnotations(comment)) {
        const parenthesesErrors = validateParentheses(comment, context);
        errors.push(...parenthesesErrors);
        // Check for malformed @zod syntax
        const syntaxErrors = validateZodSyntax(comment, context);
        errors.push(...syntaxErrors);
    }
    return errors;
}
/**
 * Validate parentheses matching in comments
 *
 * @param comment - Comment to validate
 * @param context - Field context for error reporting
 * @returns Array of parentheses validation errors
 */
function validateParentheses(comment, context) {
    const errors = [];
    let parenCount = 0;
    for (let i = 0; i < comment.length; i++) {
        const char = comment[i];
        if (char === '(') {
            parenCount++;
        }
        else if (char === ')') {
            parenCount--;
            if (parenCount < 0) {
                errors.push(`${context.modelName}.${context.fieldName}: Unmatched closing parenthesis at position ${i}`);
                break;
            }
        }
    }
    if (parenCount > 0) {
        errors.push(`${context.modelName}.${context.fieldName}: ${parenCount} unmatched opening parenthesis(es)`);
    }
    return errors;
}
/**
 * Extract @zod annotations from comment with proper nested parentheses handling
 *
 * @param comment - Comment string
 * @returns Array of annotation strings
 */
function extractZodAnnotations(comment) {
    const annotations = [];
    const zodRegex = /@zod\s*\./gi;
    let match;
    while ((match = zodRegex.exec(comment)) !== null) {
        const startIndex = match.index;
        // Find the method name
        let currentIndex = startIndex + match[0].length;
        // Skip whitespace
        while (currentIndex < comment.length && /\s/.test(comment[currentIndex])) {
            currentIndex++;
        }
        // Extract method name
        const methodStart = currentIndex;
        while (currentIndex < comment.length && /[a-zA-Z0-9_]/.test(comment[currentIndex])) {
            currentIndex++;
        }
        if (currentIndex === methodStart)
            continue; // No method name found
        // Skip whitespace
        while (currentIndex < comment.length && /\s/.test(comment[currentIndex])) {
            currentIndex++;
        }
        // Optional generic args after method name (e.g., <"UserId">)
        if (currentIndex < comment.length && comment[currentIndex] === '<') {
            const genericRes = parseGenericArgs(comment, currentIndex);
            if (genericRes) {
                currentIndex = genericRes.nextIndex;
                // Skip whitespace after generics
                while (currentIndex < comment.length && /\s/.test(comment[currentIndex])) {
                    currentIndex++;
                }
            }
        }
        // Check if there are parameters
        if (currentIndex < comment.length && comment[currentIndex] === '(') {
            // Find the matching closing parenthesis with proper nesting
            let depth = 0;
            let inString = false;
            let stringChar = '';
            while (currentIndex < comment.length) {
                const char = comment[currentIndex];
                if (!inString && (char === '"' || char === "'")) {
                    inString = true;
                    stringChar = char;
                }
                else if (inString && char === stringChar && comment[currentIndex - 1] !== '\\') {
                    inString = false;
                    stringChar = '';
                }
                else if (!inString) {
                    if (char === '(') {
                        depth++;
                    }
                    else if (char === ')') {
                        depth--;
                        if (depth === 0) {
                            currentIndex++; // Include the closing parenthesis
                            break;
                        }
                    }
                }
                currentIndex++;
            }
            if (depth === 0) {
                annotations.push(comment.substring(startIndex, currentIndex));
            }
        }
        else {
            // No parameters, just method name (and possible generics)
            annotations.push(comment.substring(startIndex, currentIndex));
        }
    }
    return annotations;
}
/**
 * Parse @zod annotations with proper nested parentheses support
 *
 * @param comment - Comment string
 * @param context - Field context
 * @returns Parse result with annotations and errors
 */
function parseZodAnnotationsWithNestedParentheses(comment, context) {
    const result = {
        annotations: [],
        parseErrors: [],
        isValid: true,
    };
    const annotationStrings = extractZodAnnotations(comment);
    for (const annotationStr of annotationStrings) {
        try {
            const methodMatch = annotationStr.match(/@zod\s*\.([a-zA-Z_][a-zA-Z0-9_]*)/i);
            if (!methodMatch)
                continue;
            const methodName = methodMatch[1];
            // Scan after method token for optional generics before params
            const methodToken = methodMatch[0];
            const methodTokenIndex = annotationStr.indexOf(methodToken);
            let scanIndex = methodTokenIndex + methodToken.length;
            while (scanIndex < annotationStr.length && /\s/.test(annotationStr[scanIndex]))
                scanIndex++;
            let genericArgs = '';
            if (annotationStr[scanIndex] === '<') {
                const genericRes = parseGenericArgs(annotationStr, scanIndex);
                if (genericRes) {
                    genericArgs = genericRes.segment;
                    scanIndex = genericRes.nextIndex;
                    while (scanIndex < annotationStr.length && /\s/.test(annotationStr[scanIndex]))
                        scanIndex++;
                }
            }
            const paramStart = annotationStr.indexOf('(', scanIndex);
            if (paramStart !== -1) {
                const paramEnd = annotationStr.lastIndexOf(')');
                if (paramEnd !== -1) {
                    const parameterString = annotationStr.substring(paramStart, paramEnd + 1);
                    const fakeMatch = [
                        annotationStr,
                        methodName,
                        parameterString,
                    ];
                    fakeMatch.index = 0;
                    const annotation = parseZodAnnotation(fakeMatch, context, genericArgs);
                    result.annotations.push(annotation);
                }
            }
            else {
                const fakeMatch = [annotationStr, methodName, ''];
                fakeMatch.index = 0;
                const annotation = parseZodAnnotation(fakeMatch, context, genericArgs);
                result.annotations.push(annotation);
            }
        }
        catch (error) {
            const errorMessage = `Failed to parse @zod annotation "${annotationStr}": ${error instanceof Error ? error.message : String(error)}`;
            result.parseErrors.push(errorMessage);
            result.isValid = false;
        }
    }
    return result;
}
/**
 * Validate basic @zod annotation syntax
 *
 * @param comment - Comment to validate
 * @param context - Field context for error reporting
 * @returns Array of syntax validation errors
 */
function validateZodSyntax(comment, context) {
    const errors = [];
    // Look for @zod annotations and validate basic structure - allow optional whitespace
    const zodMatches = extractZodAnnotations(comment);
    if (zodMatches) {
        zodMatches.forEach((match, _index) => {
            // Check for common syntax issues
            if (match.includes('..')) {
                errors.push(`${context.modelName}.${context.fieldName}: Invalid double dots in @zod annotation: ${match}`);
            }
            if (match.includes('@zod.()')) {
                errors.push(`${context.modelName}.${context.fieldName}: Empty method name in @zod annotation: ${match}`);
            }
        });
    }
    return errors;
}
/**
 * Get comment extraction statistics for debugging
 *
 * @param extractedComments - Array of extracted comments
 * @returns Statistics about comment extraction
 */
function getExtractionStatistics(extractedComments) {
    const totalFields = extractedComments.length;
    const fieldsWithComments = extractedComments.filter((ec) => ec.normalizedComment.length > 0).length;
    const fieldsWithZodAnnotations = extractedComments.filter((ec) => ec.hasZodAnnotations).length;
    const extractionErrors = extractedComments.filter((ec) => ec.extractionErrors.length > 0).length;
    return {
        totalFields,
        fieldsWithComments,
        fieldsWithZodAnnotations,
        extractionErrors,
    };
}
/**
 * Filter extracted comments to only those with @zod annotations
 *
 * @param extractedComments - Array of all extracted comments
 * @returns Array of comments containing @zod annotations
 */
function filterZodComments(extractedComments) {
    return extractedComments.filter((ec) => ec.hasZodAnnotations && ec.extractionErrors.length === 0);
}
/**
 * Get all extraction errors across multiple field comments
 *
 * @param extractedComments - Array of extracted comments
 * @returns Array of all errors with field context
 */
function getAllExtractionErrors(extractedComments) {
    return extractedComments
        .filter((ec) => ec.extractionErrors.length > 0)
        .map((ec) => ({
        modelName: ec.context.modelName,
        fieldName: ec.context.fieldName,
        errors: ec.extractionErrors,
    }));
}
/**
 * Parse @zod annotations from normalized comment text
 *
 * Extracts and parses all @zod annotations including method names,
 * parameters, and handles complex chaining scenarios.
 *
 * @param comment - Normalized comment string
 * @param context - Field context for error reporting
 * @returns Parse result with annotations and errors
 */
function parseZodAnnotations(comment, context) {
    const result = {
        annotations: [],
        parseErrors: [],
        isValid: true,
    };
    if (!comment || !detectZodAnnotations(comment)) {
        return result;
    }
    try {
        // Check if we have multiple method calls (chained) by counting dots after @zod
        const methodCount = (comment.match(/\.([a-zA-Z_][a-zA-Z0-9_]*)/g) || []).length;
        const hasChainedAnnotations = methodCount > 1;
        if (hasChainedAnnotations) {
            // Handle chained @zod annotations (e.g., @zod.min(1).max(100))
            const chainedAnnotations = parseChainedZodAnnotations(comment, context);
            result.annotations.push(...chainedAnnotations.annotations);
            result.parseErrors.push(...chainedAnnotations.parseErrors);
            if (chainedAnnotations.parseErrors.length > 0) {
                result.isValid = false;
            }
        }
        else {
            // Handle simple @zod annotations with proper nested parentheses support
            const annotations = parseZodAnnotationsWithNestedParentheses(comment, context);
            result.annotations.push(...annotations.annotations);
            result.parseErrors.push(...annotations.parseErrors);
            if (annotations.parseErrors.length > 0) {
                result.isValid = false;
            }
        }
        // Validate and filter the parsed annotations
        if (result.annotations.length > 0) {
            const validAnnotations = [];
            const validationErrors = [];
            for (const annotation of result.annotations) {
                try {
                    validateZodMethod(annotation, context);
                    validAnnotations.push(annotation);
                }
                catch (error) {
                    validationErrors.push(`${context.modelName}.${context.fieldName}: ${error instanceof Error ? error.message : String(error)}`);
                    // Continue processing other annotations instead of failing completely
                }
            }
            // Only fail if no annotations are valid
            if (validAnnotations.length === 0 && validationErrors.length > 0) {
                result.parseErrors.push(...validationErrors);
                result.isValid = false;
            }
            else {
                result.annotations = validAnnotations;
                // Log validation errors but don't fail if we have some valid annotations
                if (validationErrors.length > 0) {
                    logger_1.logger.warn('Some @zod annotations were invalid and filtered out:', validationErrors);
                }
            }
        }
    }
    catch (error) {
        result.parseErrors.push(`General parsing error: ${error instanceof Error ? error.message : String(error)}`);
        result.isValid = false;
    }
    return result;
}
/**
 * Parse a single @zod annotation match
 *
 * @param match - Regex match result
 * @param context - Field context for error reporting
 * @returns Parsed annotation object
 */
function parseZodAnnotation(match, context, genericArgs = '') {
    const [fullMatch, methodName, parameterString] = match;
    const position = match.index || 0;
    // Parse parameters if present
    let parameters = [];
    if (parameterString && parameterString.trim() !== '()') {
        try {
            parameters = parseZodParameters(parameterString, context, methodName);
        }
        catch (error) {
            throw new Error(`Invalid parameters in ${methodName}: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    return {
        method: methodName,
        parameters,
        rawMatch: fullMatch,
        position,
        genericArgs: genericArgs || undefined,
    };
}
/**
 * Parse chained @zod annotations like @zod.min(1).max(100)
 *
 * @param comment - Comment string
 * @param context - Field context
 * @returns Parse result for chained annotations
 */
function parseChainedZodAnnotations(comment, context) {
    const result = {
        annotations: [],
        parseErrors: [],
        isValid: true,
    };
    try {
        // Find the @zod annotation start
        const zodIndex = comment.indexOf('@zod');
        if (zodIndex === -1) {
            return result;
        }
        // Extract the entire @zod chain starting from @zod
        const zodChain = comment.slice(zodIndex);
        // Use the improved parseMethodChain function to handle the entire chain
        const chainedMethods = parseMethodChain(zodChain, context);
        result.annotations.push(...chainedMethods);
    }
    catch (error) {
        result.parseErrors.push(`Failed to parse chained annotation: ${error instanceof Error ? error.message : String(error)}`);
        result.isValid = false;
    }
    return result;
}
/**
 * Parse a method chain like .min(1).max(100)
 *
 * @param chainString - The chained method string
 * @param context - Field context
 * @returns Array of parsed annotations
 */
function parseMethodChain(chainString, context) {
    const annotations = [];
    // Parse method calls manually to handle nested parentheses properly
    let i = 0;
    while (i < chainString.length) {
        // Find next method call starting with a dot
        const dotIndex = chainString.indexOf('.', i);
        if (dotIndex === -1)
            break;
        // Extract method name
        const methodStart = dotIndex + 1;
        let methodEnd = methodStart;
        while (methodEnd < chainString.length && /[a-zA-Z0-9_]/.test(chainString[methodEnd])) {
            methodEnd++;
        }
        if (methodEnd === methodStart) {
            i = dotIndex + 1;
            continue;
        }
        const methodName = chainString.slice(methodStart, methodEnd);
        // Skip whitespace
        while (methodEnd < chainString.length && /\s/.test(chainString[methodEnd])) {
            methodEnd++;
        }
        // Optional generic arguments after method name
        let iAfterName = methodEnd;
        while (iAfterName < chainString.length && /\s/.test(chainString[iAfterName])) {
            iAfterName++;
        }
        let genericArgs = '';
        if (iAfterName < chainString.length && chainString[iAfterName] === '<') {
            const genRes = parseGenericArgs(chainString, iAfterName);
            if (genRes) {
                genericArgs = genRes.segment;
                iAfterName = genRes.nextIndex;
                while (iAfterName < chainString.length && /\s/.test(chainString[iAfterName])) {
                    iAfterName++;
                }
            }
        }
        // Check for parameter list
        let parameterString = '';
        if (iAfterName < chainString.length && chainString[iAfterName] === '(') {
            // Find matching closing parenthesis, handling nested parentheses
            let parenCount = 1;
            const paramStart = iAfterName + 1;
            let paramEnd = paramStart;
            while (paramEnd < chainString.length && parenCount > 0) {
                const char = chainString[paramEnd];
                if (char === '(') {
                    parenCount++;
                }
                else if (char === ')') {
                    parenCount--;
                }
                paramEnd++;
            }
            if (parenCount === 0) {
                parameterString = chainString.slice(iAfterName, paramEnd);
                i = paramEnd;
            }
            else {
                throw new Error(`Unmatched parentheses in method ${methodName}`);
            }
        }
        else {
            // No parameters
            parameterString = '()';
            i = iAfterName;
        }
        // Parse parameters
        let parameters = [];
        if (parameterString && parameterString.trim() !== '()') {
            try {
                parameters = parseZodParameters(parameterString, context, methodName);
            }
            catch (error) {
                throw new Error(`Invalid parameters in chained method ${methodName}: ${error instanceof Error ? error.message : String(error)}`);
            }
        }
        annotations.push({
            method: methodName,
            parameters,
            rawMatch: `.${methodName}${genericArgs}${parameterString}`,
            position: dotIndex,
            genericArgs: genericArgs || undefined,
        });
    }
    return annotations;
}
/**
 * Parse parameter string from @zod annotation
 *
 * Handles various parameter types: numbers, strings, booleans, arrays, objects
 *
 * @param parameterString - Parameter string including parentheses
 * @param context - Field context for error reporting
 * @param methodName - Method name for error context
 * @returns Array of parsed parameters
 */
function parseZodParameters(parameterString, context, methodName) {
    // Remove outer parentheses
    const innerParams = parameterString.slice(1, -1).trim();
    if (!innerParams) {
        return [];
    }
    try {
        // Handle simple cases first
        if (isSimpleParameter(innerParams)) {
            return [parseSimpleParameter(innerParams)];
        }
        // Handle multiple parameters - split by commas not inside quotes or nested structures
        const paramParts = splitParameters(innerParams);
        return paramParts.map((part) => parseSimpleParameter(part.trim()));
    }
    catch (error) {
        throw new Error(`Failed to parse parameters "${innerParams}" for ${methodName}: ${error instanceof Error ? error.message : String(error)}`);
    }
}
/**
 * Check if parameter string is a simple single parameter
 *
 * @param paramString - Parameter string
 * @returns True if simple parameter
 */
function isSimpleParameter(paramString) {
    const trimmed = paramString.trim();
    // Check if it contains unquoted commas (indicating multiple parameters)
    let inQuotes = false;
    let quoteChar = '';
    let bracketDepth = 0;
    for (let i = 0; i < trimmed.length; i++) {
        const char = trimmed[i];
        if (!inQuotes && (char === '"' || char === "'")) {
            inQuotes = true;
            quoteChar = char;
        }
        else if (inQuotes && char === quoteChar && trimmed[i - 1] !== '\\') {
            inQuotes = false;
            quoteChar = '';
        }
        else if (!inQuotes) {
            if (char === '[' || char === '{' || char === '(') {
                bracketDepth++;
            }
            else if (char === ']' || char === '}' || char === ')') {
                bracketDepth--;
            }
            else if (char === ',' && bracketDepth === 0) {
                return false; // Multiple parameters
            }
        }
    }
    return true;
}
/**
 * Parse generic arguments starting at the given index (which must point to '<').
 * Handles nested angle brackets and quoted strings.
 * Returns the captured segment and the index immediately after it.
 */
function parseGenericArgs(source, startIndex) {
    if (source[startIndex] !== '<')
        return null;
    let i = startIndex;
    let depth = 0;
    let inString = false;
    let stringChar = '';
    while (i < source.length) {
        const char = source[i];
        if (!inString && (char === '"' || char === "'")) {
            inString = true;
            stringChar = char;
        }
        else if (inString && char === stringChar && source[i - 1] !== '\\') {
            inString = false;
            stringChar = '';
        }
        else if (!inString) {
            if (char === '<') {
                depth++;
            }
            else if (char === '>') {
                depth--;
                if (depth === 0) {
                    const segment = source.slice(startIndex, i + 1);
                    return { segment, nextIndex: i + 1 };
                }
            }
        }
        i++;
    }
    return null;
}
/**
 * Split parameter string by commas, respecting quotes and nested structures
 *
 * @param paramString - Parameter string
 * @returns Array of parameter parts
 */
function splitParameters(paramString) {
    const parts = [];
    let current = '';
    let inQuotes = false;
    let quoteChar = '';
    let bracketDepth = 0;
    for (let i = 0; i < paramString.length; i++) {
        const char = paramString[i];
        if (!inQuotes && (char === '"' || char === "'")) {
            inQuotes = true;
            quoteChar = char;
            current += char;
        }
        else if (inQuotes && char === quoteChar && paramString[i - 1] !== '\\') {
            inQuotes = false;
            quoteChar = '';
            current += char;
        }
        else if (!inQuotes) {
            if (char === '[' || char === '{' || char === '(') {
                bracketDepth++;
                current += char;
            }
            else if (char === ']' || char === '}' || char === ')') {
                bracketDepth--;
                current += char;
            }
            else if (char === ',' && bracketDepth === 0) {
                parts.push(current);
                current = '';
            }
            else {
                current += char;
            }
        }
        else {
            current += char;
        }
    }
    if (current) {
        parts.push(current);
    }
    return parts;
}
/**
 * Parse a single parameter value
 *
 * @param paramValue - Parameter string value
 * @returns Parsed parameter value
 */
function parseSimpleParameter(paramValue) {
    const trimmed = paramValue.trim();
    if (!trimmed) {
        throw new Error('Empty parameter value');
    }
    // Boolean values
    if (trimmed === 'true')
        return true;
    if (trimmed === 'false')
        return false;
    // Null and undefined
    if (trimmed === 'null')
        return null;
    if (trimmed === 'undefined')
        return undefined;
    // Numbers (integers and floats)
    if (/^-?\d+$/.test(trimmed)) {
        return parseInt(trimmed, 10);
    }
    if (/^-?\d*\.\d+$/.test(trimmed)) {
        return parseFloat(trimmed);
    }
    // Quoted strings
    if ((trimmed.startsWith('"') && trimmed.endsWith('"')) ||
        (trimmed.startsWith("'") && trimmed.endsWith("'"))) {
        return trimmed.slice(1, -1);
    }
    // Regular expressions
    if (trimmed.startsWith('/') && trimmed.match(/\/[gimuy]*$/)) {
        try {
            const lastSlash = trimmed.lastIndexOf('/');
            const pattern = trimmed.slice(1, lastSlash);
            const flags = trimmed.slice(lastSlash + 1);
            return new RegExp(pattern, flags);
        }
        catch {
            throw new Error(`Invalid regex: ${trimmed}`);
        }
    }
    // Arrays (basic support)
    if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
        try {
            return JSON.parse(trimmed);
        }
        catch {
            throw new Error(`Invalid array: ${trimmed}`);
        }
    }
    // Objects (JavaScript object literal support)
    if (trimmed.startsWith('{') && trimmed.endsWith('}')) {
        try {
            // First try standard JSON parsing for simple cases
            return JSON.parse(trimmed);
        }
        catch {
            // For complex JavaScript object literals, return as-is as a special marker
            // This will be handled specially in formatParameters to preserve JavaScript expressions
            return { __js_object_literal__: trimmed };
        }
    }
    // Complex expressions (like new Date(), function calls, etc.)
    if (trimmed.includes('(') && trimmed.includes(')')) {
        // Pass through complex expressions as-is for evaluation at runtime
        return trimmed;
    }
    // Unquoted strings (identifiers, etc.)
    if (/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(trimmed)) {
        return trimmed;
    }
    // If we can't parse it, pass it through as a raw string
    // This allows complex expressions to be used in the generated code
    return trimmed;
}
/**
 * Validate parsed @zod annotations for semantic correctness
 *
 * @param annotations - Array of parsed annotations
 * @param context - Field context
 * @returns Array of validation errors
 */
function validateZodAnnotations(annotations, context) {
    const errors = [];
    for (const annotation of annotations) {
        try {
            validateZodMethod(annotation, context);
        }
        catch (error) {
            errors.push(`${context.modelName}.${context.fieldName}: ${error instanceof Error ? error.message : String(error)}`);
        }
    }
    return errors;
}
/**
 * Validate a single @zod method annotation
 *
 * @param annotation - Parsed annotation
 * @param context - Field context
 */
function validateZodMethod(annotation, context) {
    const { method, parameters } = annotation;
    // Common string validation methods
    const stringMethods = [
        'min',
        'max',
        'length',
        'email',
        'url',
        'uuid',
        'regex',
        'includes',
        'startsWith',
        'endsWith',
        'trim',
        'toLowerCase',
        'toUpperCase',
        'datetime',
        'ip',
        'cidr',
        'date',
        'time',
        'duration',
        'normalize',
        'uppercase',
        'lowercase',
        // New Zod v4 string format methods - Issue #233
        'httpUrl',
        'hostname',
        'guid',
        'nanoid',
        'cuid',
        'cuid2',
        'ulid',
        'base64',
        'base64url',
        'hex',
        'jwt',
        'hash',
        'ipv4',
        'ipv6',
        'cidrv4',
        'cidrv6',
        'emoji',
        'isoDate',
        'isoTime',
        'isoDatetime',
        'isoDuration',
    ];
    // Common number validation methods
    const numberMethods = [
        'min',
        'max',
        'int',
        'positive',
        'negative',
        'nonnegative',
        'nonpositive',
        'finite',
        'multipleOf',
        'step',
        'gt',
        'gte',
        'lt',
        'lte',
    ];
    // Common array validation methods
    const arrayMethods = ['min', 'max', 'length', 'nonempty'];
    // Methods that require parameters
    const requiresParams = [
        'min',
        'max',
        'length',
        'regex',
        'includes',
        'startsWith',
        'endsWith',
        'enum',
        'default',
        'refine',
        'transform',
        'multipleOf',
        'step',
        'gt',
        'gte',
        'lt',
        'lte',
        'catch',
        'pipe',
        // New methods that require parameters - Issue #233
        'hash', // hash requires algorithm parameter like "sha256"
        'custom', // custom requires object/array schema parameter
    ];
    // Methods that don't allow parameters
    const noParams = [
        'nonempty',
        'trim',
        'toLowerCase',
        'toUpperCase',
        'uppercase',
        'lowercase',
        'brand',
        'readonly',
    ];
    // Methods that accept optional parameters (format validation methods)
    const optionalParams = [
        // Existing string format methods
        'email',
        'url',
        'uuid',
        'datetime',
        'ip',
        'cidr',
        'date',
        'time',
        'duration',
        'normalize',
        // New Zod v4 string format methods - Issue #233
        'httpUrl',
        'hostname',
        'guid',
        'nanoid',
        'cuid',
        'cuid2',
        'ulid',
        'base64',
        'base64url',
        'hex',
        'jwt',
        'ipv4',
        'ipv6',
        'cidrv4',
        'cidrv6',
        'emoji',
        'isoDate',
        'isoTime',
        'isoDatetime',
        'isoDuration',
    ];
    // Methods that accept optional error message parameter
    const optionalErrorMessage = [
        'int',
        'positive',
        'negative',
        'nonnegative',
        'nonpositive',
        'finite',
    ];
    // Additional known methods not covered above
    const additionalMethods = [
        'default',
        'optional',
        'nullable',
        'nullish',
        'refine',
        'transform',
        'enum',
        'object',
        'array',
        'record',
        'json',
        'catch',
        'pipe',
        'brand',
        'readonly',
    ];
    // Check if method is known
    const allKnownMethods = [
        ...new Set([
            ...stringMethods,
            ...numberMethods,
            ...arrayMethods,
            ...additionalMethods,
            ...optionalErrorMessage,
            ...optionalParams,
            ...requiresParams, // Add methods that require parameters
        ]),
    ];
    if (!allKnownMethods.includes(method)) {
        throw new Error(`Unknown @zod method: ${method} - this method is not supported`);
    }
    // Validate parameter requirements
    if (requiresParams.includes(method) && parameters.length === 0) {
        throw new Error(`Method ${method} requires parameters`);
    }
    if (noParams.includes(method) && parameters.length > 0) {
        throw new Error(`Method ${method} does not accept parameters`);
    }
    // Validate optional parameter methods (string format methods)
    if (optionalParams.includes(method) && parameters.length > 1) {
        throw new Error(`Method ${method} accepts at most one parameter`);
    }
    // Validate optional error message methods
    if (optionalErrorMessage.includes(method) && parameters.length > 1) {
        throw new Error(`Method ${method} accepts at most one parameter (error message)`);
    }
    if (optionalErrorMessage.includes(method) &&
        parameters.length === 1 &&
        typeof parameters[0] !== 'string') {
        throw new Error(`Method ${method} error message parameter must be a string`);
    }
    // Type-specific validations based on field type
    if (context.fieldType === 'String') {
        validateStringMethodParameters(method, parameters);
    }
    else if (context.fieldType === 'Int' || context.fieldType === 'Float') {
        validateNumberMethodParameters(method, parameters);
    }
}
/**
 * Validate parameters for string validation methods
 *
 * @param method - Validation method name
 * @param parameters - Method parameters
 */
function validateStringMethodParameters(method, parameters) {
    switch (method) {
        case 'min':
        case 'max':
        case 'length':
            // Allow 1 parameter (number) or 2 parameters (number + error message)
            if (parameters.length < 1 ||
                parameters.length > 2 ||
                typeof parameters[0] !== 'number' ||
                parameters[0] < 0 ||
                (parameters.length === 2 && typeof parameters[1] !== 'string')) {
                throw new Error(`${method} requires a non-negative number parameter and optional error message`);
            }
            break;
        case 'regex':
            if (parameters.length < 1 || parameters.length > 2) {
                throw new Error(`regex requires 1-2 parameters (RegExp/string, optional error message)`);
            }
            if (!(parameters[0] instanceof RegExp || typeof parameters[0] === 'string')) {
                throw new Error(`regex first parameter must be a RegExp or string`);
            }
            if (parameters.length === 2 && typeof parameters[1] !== 'string') {
                throw new Error(`regex second parameter (error message) must be a string`);
            }
            break;
        case 'includes':
        case 'startsWith':
        case 'endsWith':
            if (parameters.length !== 1 || typeof parameters[0] !== 'string') {
                throw new Error(`${method} requires a string parameter`);
            }
            break;
        case 'hash':
            if (parameters.length !== 1 || typeof parameters[0] !== 'string') {
                throw new Error(`hash requires a single string algorithm parameter (e.g., 'sha256')`);
            }
            break;
    }
}
/**
 * Validate parameters for number validation methods
 *
 * @param method - Validation method name
 * @param parameters - Method parameters
 */
function validateNumberMethodParameters(method, parameters) {
    switch (method) {
        case 'min':
        case 'max':
            // Allow 1 parameter (number) or 2 parameters (number + error message)
            if (parameters.length < 1 ||
                parameters.length > 2 ||
                typeof parameters[0] !== 'number' ||
                (parameters.length === 2 && typeof parameters[1] !== 'string')) {
                throw new Error(`${method} requires a number parameter and optional error message`);
            }
            break;
        case 'positive':
        case 'negative':
        case 'int':
        case 'finite':
            // Allow 0 parameters or 1 parameter (error message)
            if (parameters.length > 1 || (parameters.length === 1 && typeof parameters[0] !== 'string')) {
                throw new Error(`${method} accepts optional error message parameter`);
            }
            break;
        case 'multipleOf':
        case 'step':
            // Allow 1 parameter (number) or 2 parameters (number + error message)
            if (parameters.length < 1 ||
                parameters.length > 2 ||
                typeof parameters[0] !== 'number' ||
                (parameters.length === 2 && typeof parameters[1] !== 'string')) {
                throw new Error(`${method} requires a number parameter and optional error message`);
            }
            break;
    }
}
/**
 * Map parsed @zod annotations to Zod schema method calls
 *
 * Converts parsed annotations into actual Zod validation method chains
 * that can be used in generated schema code.
 *
 * @param annotations - Array of parsed annotations
 * @param context - Field context for validation
 * @param zodVersion - Zod version target ('v3', 'v4', or 'auto')
 * @returns Schema generation result with method chain and imports
 */
function mapAnnotationsToZodSchema(annotations, context, zodVersion = 'auto') {
    const result = {
        schemaChain: '',
        imports: new Set(),
        errors: [],
        isValid: true,
    };
    if (annotations.length === 0) {
        return result;
    }
    try {
        const methodCalls = [];
        const methodResults = [];
        for (const annotation of annotations) {
            try {
                const methodResult = mapAnnotationToZodMethod(annotation, context, zodVersion);
                // Skip empty fallbacks (e.g., v3 for v4-only base methods)
                if (methodResult.methodCall) {
                    methodCalls.push(methodResult.methodCall);
                }
                methodResults.push(methodResult);
                // Add any required imports
                if (methodResult.requiredImport) {
                    result.imports.add(methodResult.requiredImport);
                }
            }
            catch (error) {
                result.errors.push(`Failed to map ${annotation.method}: ${error instanceof Error ? error.message : String(error)}`);
                result.isValid = false;
            }
        }
        // Join all method calls into a chain
        if (methodCalls.length > 0) {
            // Check if we have a base replacement method (like z.email() in v4)
            const baseReplacements = methodResults.filter((result) => result.isBaseReplacement);
            const regularReplacements = methodCalls.filter((call) => {
                var _a;
                return call &&
                    !call.startsWith('.') &&
                    !((_a = methodResults.find((r) => r.methodCall === call)) === null || _a === void 0 ? void 0 : _a.isBaseReplacement);
            });
            const chainMethods = methodCalls.filter((call) => call.startsWith('.'));
            if (baseReplacements.length > 0) {
                // Handle base replacement (like z.email() in v4) with chaining
                if (baseReplacements.length > 1) {
                    result.errors.push('Multiple base replacement methods detected - only one allowed per field');
                    result.isValid = false;
                }
                else {
                    result.schemaChain = baseReplacements[0].methodCall + chainMethods.join('');
                }
            }
            else if (regularReplacements.length > 0) {
                // Handle regular replacement methods (json, enum)
                const chainMethods = methodCalls.filter((call) => call.startsWith('.'));
                if (regularReplacements.length > 1) {
                    result.errors.push('Multiple replacement methods detected - only one allowed per field');
                    result.isValid = false;
                }
                else {
                    // Use the replacement method as base, then chain any additional methods
                    result.schemaChain = regularReplacements[0] + chainMethods.join('');
                }
            }
            else {
                // All are chaining methods, join normally
                result.schemaChain = methodCalls.join('');
            }
        }
    }
    catch (error) {
        result.errors.push(`Schema generation failed: ${error instanceof Error ? error.message : String(error)}`);
        result.isValid = false;
    }
    return result;
}
/**
 * Resolve the target Zod version for syntax generation
 *
 * @param zodVersion - Version specification ('auto', 'v3', or 'v4')
 * @returns Resolved version ('v3' or 'v4')
 */
function resolveZodVersion(zodVersion) {
    var _a;
    if (zodVersion === 'v3' || zodVersion === 'v4') {
        return zodVersion;
    }
    // Auto-detect Zod version
    try {
        // Try to detect Zod version by loading zod package.json
        // eslint-disable-next-line @typescript-eslint/no-require-imports
        const zodPackage = require('zod/package.json');
        const version = zodPackage.version;
        if (version) {
            const majorVersion = parseInt(version.split('.')[0], 10);
            return majorVersion >= 4 ? 'v4' : 'v3';
        }
    }
    catch {
        // If we can't load zod package.json, try alternative detection
        try {
            // Try to detect by checking if z.email() method exists as standalone
            // eslint-disable-next-line @typescript-eslint/no-require-imports
            const zod = require('zod');
            if (typeof ((_a = zod.z) === null || _a === void 0 ? void 0 : _a.email) === 'function') {
                return 'v4';
            }
        }
        catch {
            // Ignore inner detection errors
        }
    }
    // Fallback to v3 if detection fails
    logger_1.logger.warn('Failed to detect Zod version, defaulting to v3 syntax');
    return 'v3';
}
/**
 * Map a single annotation to a Zod method call
 *
 * @param annotation - Parsed annotation
 * @param context - Field context
 * @param zodVersion - Zod version target for version-specific handling
 * @returns Method mapping result
 */
function mapAnnotationToZodMethod(annotation, context, zodVersion = 'auto') {
    var _a;
    const { method, parameters } = annotation;
    // Get method configuration. For list fields, validations like .min/.max/.length should
    // target the array wrapper rather than the element type, so treat the effective
    // field type as 'Array' for compatibility checks.
    const effectiveFieldType = context.isList ? 'Array' : context.fieldType;
    const methodConfig = getValidationMethodConfig(method, effectiveFieldType);
    if (!methodConfig) {
        // Unknown method - pass through as-is with warning
        logger_1.logger.warn(`Unknown @zod method: ${method} - generating as-is`);
        return {
            methodCall: `.${method}(${formatParameters(parameters)})`,
        };
    }
    // Validate field type compatibility
    if (methodConfig.fieldTypeCompatibility &&
        !methodConfig.fieldTypeCompatibility.includes(effectiveFieldType)) {
        throw new Error(`Method ${method} is not compatible with field type ${effectiveFieldType}. Compatible types: ${methodConfig.fieldTypeCompatibility.join(', ')}`);
    }
    // Validate parameter count
    if (methodConfig.parameterCount !== 'variable') {
        if (parameters.length !== methodConfig.parameterCount) {
            throw new Error(`Method ${method} expects ${methodConfig.parameterCount} parameters, got ${parameters.length}`);
        }
    }
    // Handle special cases that replace the base type rather than chain
    if (method === 'json') {
        // z.json() replaces the base type entirely
        return {
            methodCall: 'z.json()',
            requiredImport: methodConfig.requiresImport,
        };
    }
    if (method === 'enum') {
        // z.enum() replaces the base type entirely
        const formattedParams = formatParameters(parameters);
        return {
            methodCall: `z.enum(${formattedParams})`,
            requiredImport: methodConfig.requiresImport,
        };
    }
    if (method === 'custom') {
        if (parameters.length === 0) {
            throw new Error('Method custom requires parameters');
        }
        const [schemaCandidate] = parameters;
        if (schemaCandidate &&
            typeof schemaCandidate === 'object' &&
            !('__js_object_literal__' in schemaCandidate)) {
            const zodSchema = inferZodTypeFromValue(schemaCandidate);
            return {
                methodCall: zodSchema,
                requiredImport: methodConfig.requiresImport,
                isBaseReplacement: true,
            };
        }
        if (typeof schemaCandidate === 'string') {
            try {
                const zodSchema = inferZodTypeFromValue(JSON.parse(schemaCandidate));
                return {
                    methodCall: zodSchema,
                    requiredImport: methodConfig.requiresImport,
                    isBaseReplacement: true,
                };
            }
            catch {
                // Fall through; formatParameters will handle non-JSON literals.
            }
        }
        const formattedParams = formatParameters(parameters);
        return {
            methodCall: `z.object(${formattedParams})`,
            requiredImport: methodConfig.requiresImport,
            isBaseReplacement: true,
        };
    }
    // Handle new Zod v4 string format methods - Issue #233
    const newStringFormatMethods = [
        'email',
        'url',
        'uuid',
        'httpUrl',
        'hostname',
        'guid',
        'nanoid',
        'cuid',
        'cuid2',
        'ulid',
        'base64',
        'base64url',
        'hex',
        'jwt',
        'ipv4',
        'ipv6',
        'cidrv4',
        'cidrv6',
        'emoji',
        // Note: 'regex' is not included because z.regex() is not a valid method
        // regex should use z.string().regex() syntax instead
        'isoDate',
        'isoTime',
        'isoDatetime',
        'isoDuration',
    ];
    if (newStringFormatMethods.includes(method)) {
        const resolvedVersion = resolveZodVersion(zodVersion);
        if (resolvedVersion === 'v4') {
            // In Zod v4, prefer base types like z.httpUrl(), z.base64(), etc.
            // Don't chain unnecessarily!
            const formattedParams = formatV4StringFormatParameters(method, parameters);
            // Special handling for ISO methods which use z.iso.xxx() syntax
            const isoMethods = ['isoDate', 'isoTime', 'isoDatetime', 'isoDuration'];
            let methodCall;
            if (isoMethods.includes(method)) {
                const isoMethodMap = {
                    isoDate: 'z.iso.date',
                    isoTime: 'z.iso.time',
                    isoDatetime: 'z.iso.datetime',
                    isoDuration: 'z.iso.duration',
                };
                methodCall = formattedParams
                    ? `${isoMethodMap[method]}(${formattedParams})`
                    : `${isoMethodMap[method]}()`;
            }
            else {
                methodCall = formattedParams ? `z.${method}(${formattedParams})` : `z.${method}()`;
            }
            return {
                methodCall,
                requiredImport: methodConfig.requiresImport,
                isBaseReplacement: true,
            };
        }
        // For v3, fallback to z.string() since these methods likely don't exist
        logger_1.logger.warn(`[zod-comments] Method ${method} not supported in Zod v3; falling back to z.string()`);
        return {
            methodCall: '', // Will result in base z.string() only
            requiredImport: methodConfig.requiresImport,
        };
    }
    // Handle hash method with parameter (special case)
    if (method === 'hash') {
        const resolvedVersion = resolveZodVersion(zodVersion);
        if (resolvedVersion === 'v4') {
            // In Zod v4, z.hash(algorithm) is a base type
            const formattedParams = formatParameters(parameters);
            return {
                methodCall: `z.hash(${formattedParams})`,
                requiredImport: methodConfig.requiresImport,
                isBaseReplacement: true,
            };
        }
        // For v3, fallback to z.string() since hash() likely doesn't exist
        logger_1.logger.warn(`[zod-comments] Method hash not supported in Zod v3; falling back to z.string()`);
        return {
            methodCall: '', // Will result in base z.string() only
            requiredImport: methodConfig.requiresImport,
        };
    }
    // Generate the method call for regular chaining methods
    const formattedParams = formatParameters(parameters);
    const methodCall = `.${methodConfig.zodMethod}${(_a = annotation.genericArgs) !== null && _a !== void 0 ? _a : ''}(${formattedParams})`;
    return {
        methodCall,
        requiredImport: methodConfig.requiresImport,
    };
}
/**
 * Get validation method configuration
 *
 * @param methodName - Name of the validation method
 * @param fieldType - Field type for compatibility checking
 * @returns Method configuration or null if unknown
 */
function getValidationMethodConfig(methodName, fieldType) {
    const configs = [
        // String validation methods
        {
            methodName: 'min',
            zodMethod: 'min',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'max',
            zodMethod: 'max',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'length',
            zodMethod: 'length',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'email',
            zodMethod: 'email',
            parameterCount: 'variable', // 0 or 1 parameter
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'url',
            zodMethod: 'url',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'uuid',
            zodMethod: 'uuid',
            parameterCount: 'variable', // 0 or 1 parameter
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'regex',
            zodMethod: 'regex',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'includes',
            zodMethod: 'includes',
            parameterCount: 1,
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'startsWith',
            zodMethod: 'startsWith',
            parameterCount: 1,
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'endsWith',
            zodMethod: 'endsWith',
            parameterCount: 1,
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'trim',
            zodMethod: 'trim',
            parameterCount: 0,
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'toLowerCase',
            zodMethod: 'toLowerCase',
            parameterCount: 0,
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'toUpperCase',
            zodMethod: 'toUpperCase',
            parameterCount: 0,
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'ip',
            zodMethod: 'ip',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'cidr',
            zodMethod: 'cidr',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'date',
            zodMethod: 'date',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'time',
            zodMethod: 'time',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'duration',
            zodMethod: 'duration',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'normalize',
            zodMethod: 'normalize',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'uppercase',
            zodMethod: 'uppercase',
            parameterCount: 0,
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'lowercase',
            zodMethod: 'lowercase',
            parameterCount: 0,
            fieldTypeCompatibility: ['String'],
        },
        // Number validation methods
        {
            methodName: 'min',
            zodMethod: 'min',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Int', 'Float', 'BigInt'],
        },
        {
            methodName: 'max',
            zodMethod: 'max',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Int', 'Float', 'BigInt'],
        },
        {
            methodName: 'int',
            zodMethod: 'int',
            parameterCount: 0,
            fieldTypeCompatibility: ['Int', 'Float'],
        },
        {
            methodName: 'positive',
            zodMethod: 'positive',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Int', 'Float', 'BigInt'],
        },
        {
            methodName: 'negative',
            zodMethod: 'negative',
            parameterCount: 0,
            fieldTypeCompatibility: ['Int', 'Float', 'BigInt'],
        },
        {
            methodName: 'nonnegative',
            zodMethod: 'nonnegative',
            parameterCount: 0,
            fieldTypeCompatibility: ['Int', 'Float', 'BigInt'],
        },
        {
            methodName: 'nonpositive',
            zodMethod: 'nonpositive',
            parameterCount: 0,
            fieldTypeCompatibility: ['Int', 'Float', 'BigInt'],
        },
        {
            methodName: 'finite',
            zodMethod: 'finite',
            parameterCount: 0,
            fieldTypeCompatibility: ['Float'],
        },
        {
            methodName: 'safe',
            zodMethod: 'safe',
            parameterCount: 0,
            fieldTypeCompatibility: ['Int', 'Float'],
        },
        {
            methodName: 'multipleOf',
            zodMethod: 'multipleOf',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Int', 'Float'],
        },
        {
            methodName: 'gt',
            zodMethod: 'gt',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Int', 'Float', 'BigInt'],
        },
        {
            methodName: 'gte',
            zodMethod: 'gte',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Int', 'Float', 'BigInt'],
        },
        {
            methodName: 'lt',
            zodMethod: 'lt',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Int', 'Float', 'BigInt'],
        },
        {
            methodName: 'lte',
            zodMethod: 'lte',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Int', 'Float', 'BigInt'],
        },
        {
            methodName: 'step',
            zodMethod: 'multipleOf',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Int', 'Float'],
        },
        // Array validation methods
        // Array validation methods accept an optional error message parameter in Zod
        {
            methodName: 'min',
            zodMethod: 'min',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Array'],
        },
        {
            methodName: 'max',
            zodMethod: 'max',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Array'],
        },
        {
            methodName: 'length',
            zodMethod: 'length',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Array'],
        },
        {
            methodName: 'nonempty',
            zodMethod: 'nonempty',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['Array'],
        },
        // Date validation methods
        {
            methodName: 'min',
            zodMethod: 'min',
            parameterCount: 1,
            fieldTypeCompatibility: ['DateTime'],
        },
        {
            methodName: 'max',
            zodMethod: 'max',
            parameterCount: 1,
            fieldTypeCompatibility: ['DateTime'],
        },
        // Custom validation methods
        { methodName: 'refine', zodMethod: 'refine', parameterCount: 'variable' },
        { methodName: 'transform', zodMethod: 'transform', parameterCount: 1 },
        { methodName: 'optional', zodMethod: 'optional', parameterCount: 0 },
        { methodName: 'nullable', zodMethod: 'nullable', parameterCount: 0 },
        { methodName: 'nullish', zodMethod: 'nullish', parameterCount: 0 },
        { methodName: 'default', zodMethod: 'default', parameterCount: 1 },
        {
            methodName: 'enum',
            zodMethod: 'enum',
            parameterCount: 1,
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'datetime',
            zodMethod: 'datetime',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        { methodName: 'object', zodMethod: 'object', parameterCount: 0 },
        { methodName: 'array', zodMethod: 'array', parameterCount: 'variable' },
        {
            methodName: 'json',
            zodMethod: 'json',
            parameterCount: 0,
            fieldTypeCompatibility: ['Json'],
        },
        { methodName: 'catch', zodMethod: 'catch', parameterCount: 1 },
        { methodName: 'pipe', zodMethod: 'pipe', parameterCount: 1 },
        { methodName: 'brand', zodMethod: 'brand', parameterCount: 0 },
        { methodName: 'readonly', zodMethod: 'readonly', parameterCount: 0 },
        // New Zod v4 string format methods - Issue #233
        // Network/URL validation methods
        {
            methodName: 'httpUrl',
            zodMethod: 'httpUrl',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'hostname',
            zodMethod: 'hostname',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        // Identifier validation methods
        {
            methodName: 'guid',
            zodMethod: 'guid',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'nanoid',
            zodMethod: 'nanoid',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'cuid',
            zodMethod: 'cuid',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'cuid2',
            zodMethod: 'cuid2',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'ulid',
            zodMethod: 'ulid',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        // Encoding validation methods
        {
            methodName: 'base64',
            zodMethod: 'base64',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'base64url',
            zodMethod: 'base64url',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'hex',
            zodMethod: 'hex',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        // Security/Crypto validation methods
        {
            methodName: 'jwt',
            zodMethod: 'jwt',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'hash',
            zodMethod: 'hash',
            parameterCount: 1, // Takes algorithm parameter like "sha256"
            fieldTypeCompatibility: ['String'],
        },
        // Network validation methods
        {
            methodName: 'ipv4',
            zodMethod: 'ipv4',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'ipv6',
            zodMethod: 'ipv6',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'cidrv4',
            zodMethod: 'cidrv4',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'cidrv6',
            zodMethod: 'cidrv6',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        // Character validation methods
        {
            methodName: 'emoji',
            zodMethod: 'emoji',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        // ISO date/time validation methods
        {
            methodName: 'isoDate',
            zodMethod: 'iso.date',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'isoTime',
            zodMethod: 'iso.time',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'isoDatetime',
            zodMethod: 'iso.datetime',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        {
            methodName: 'isoDuration',
            zodMethod: 'iso.duration',
            parameterCount: 'variable',
            fieldTypeCompatibility: ['String'],
        },
        // Custom schema method - replaces base type entirely
        {
            methodName: 'custom',
            zodMethod: 'object', // This will be overridden in special handling
            parameterCount: 1,
            fieldTypeCompatibility: ['Json'], // Only works with Json fields
        },
        // Note: Removed 'string', 'number', 'boolean' method configs as they duplicate base types
    ];
    // If field type is provided, find the config that matches both method name and field type
    if (fieldType) {
        const compatibleConfigs = configs.filter((config) => config.methodName === methodName &&
            (!config.fieldTypeCompatibility || config.fieldTypeCompatibility.includes(fieldType)));
        if (compatibleConfigs.length > 0) {
            return compatibleConfigs[0];
        }
    }
    // Fallback to first match by method name only
    return configs.find((config) => config.methodName === methodName) || null;
}
/**
 * Format parameters for Zod method calls
 *
 * @param parameters - Array of parameter values
 * @returns Formatted parameter string
 */
function formatParameters(parameters) {
    if (parameters.length === 0) {
        return '';
    }
    return parameters.map((param) => formatSingleParameter(param)).join(', ');
}
/**
 * Format parameters specifically for Zod v4 string format methods
 * These methods expect either string error messages or validation parameter objects
 *
 * From the Zod v4 API: all string format methods expect `string | core.$ZodCheck{Method}Params`
 * These are validation parameters, not configuration parameters
 *
 * @param method - The method name (nanoid, jwt, base64, etc.)
 * @param parameters - Array of parameters to format
 * @returns Formatted parameter string or empty string
 */
function formatV4StringFormatParameters(_method, parameters) {
    if (!parameters || parameters.length === 0) {
        return '';
    }
    const firstParam = parameters[0];
    // For numeric/boolean parameters, preserve them as-is to maintain user intent
    // Even if they result in invalid TypeScript, let the user fix their annotations
    if (typeof firstParam === 'number' || typeof firstParam === 'boolean') {
        return formatParameters(parameters);
    }
    // For string parameters, format them normally as error messages
    if (typeof firstParam === 'string') {
        return formatSingleParameter(firstParam);
    }
    // For other types (objects, arrays), format normally
    return formatParameters(parameters);
}
/**
 * Format a single parameter for Zod method calls
 *
 * @param param - Parameter value
 * @returns Formatted parameter string
 */
function formatSingleParameter(param) {
    if (param === null) {
        return 'null';
    }
    if (param === undefined) {
        return 'undefined';
    }
    if (typeof param === 'string') {
        // Check if it's a complex expression that shouldn't be quoted
        // Complex expressions include: new Date(), RegExp(), function calls,
        // arrow functions (value => ... or (value) => ...), async arrows, or dotted calls like z.string()
        const isDottedOrCtorCall = /^(new\s+\w+\(|RegExp\(|[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*\()/;
        const isFunctionKeyword = /^\s*function\b/;
        const isArrowFunction = /^\s*(?:async\s+)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/;
        if (isDottedOrCtorCall.test(param) ||
            isFunctionKeyword.test(param) ||
            isArrowFunction.test(param)) {
            return param; // Return complex/functional expressions as-is
        }
        // Prefer double quotes for URL-like strings (e.g., https://) to match snapshots
        if (/^https?:\/\//i.test(param)) {
            const escapedDq = param.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
            return `"${escapedDq}"`;
        }
        // Default: single quotes for plain strings (error messages, enum values, etc.)
        const escapedSq = param.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
        return `'${escapedSq}'`;
    }
    if (typeof param === 'number' || typeof param === 'boolean') {
        return String(param);
    }
    if (param instanceof RegExp) {
        return param.toString();
    }
    if (Array.isArray(param)) {
        return `[${param.map(formatSingleParameter).join(', ')}]`;
    }
    if (typeof param === 'object') {
        // Handle special JavaScript object literal marker
        if (param && typeof param === 'object' && '__js_object_literal__' in param) {
            // Return the raw JavaScript object literal without quotes
            return param.__js_object_literal__;
        }
        return JSON.stringify(param);
    }
    // Fallback for unknown types
    return String(param);
}
/**
 * Generate complete Zod schema with base type and validations
 *
 * @param baseType - Base Zod type (e.g., 'z.string()', 'z.number()')
 * @param annotations - Parsed annotations
 * @param context - Field context
 * @returns Complete schema generation result
 */
function generateCompleteZodSchema(baseType, annotations, context, zodVersion = 'auto') {
    const validationResult = mapAnnotationsToZodSchema(annotations, context, zodVersion);
    if (!validationResult.isValid) {
        return validationResult;
    }
    // Combine base type with validation chain
    let schemaChain = validationResult.schemaChain;
    // Check if the schema chain is a replacement method (doesn't start with dot)
    const hasChain = schemaChain.length > 0;
    const isReplacementSchema = hasChain && !schemaChain.startsWith('.');
    let fullSchema;
    if (!hasChain) {
        // No validations to apply; return base type untouched
        fullSchema = baseType;
    }
    else if (isReplacementSchema) {
        // For replacement schemas (json, enum), use them directly instead of concatenating
        fullSchema = schemaChain;
    }
    else {
        // Remove redundant .optional() calls if base type already includes .optional()
        if (baseType.includes('.optional()') && schemaChain.includes('.optional()')) {
            // Remove all .optional() calls from the chain since base type already handles optionality
            schemaChain = schemaChain.replace(/\.optional\(\)/g, '');
        }
        // Regular concatenation for chaining methods
        fullSchema = baseType + schemaChain;
    }
    return {
        schemaChain: fullSchema,
        imports: validationResult.imports,
        errors: validationResult.errors,
        isValid: validationResult.isValid,
    };
}
/**
 * Get base Zod type for Prisma field type
 *
 * @param fieldType - Prisma field type
 * @param isOptional - Whether field is optional
 * @param isList - Whether field is a list
 * @returns Base Zod type string
 */
function getBaseZodType(fieldType, isOptional, isList) {
    let baseType;
    switch (fieldType) {
        case 'String':
            baseType = 'z.string()';
            break;
        case 'Int':
            baseType = 'z.number().int()';
            break;
        case 'Float':
            baseType = 'z.number()';
            break;
        case 'Boolean':
            baseType = 'z.boolean()';
            break;
        case 'DateTime':
            baseType = 'z.date()';
            break;
        case 'BigInt':
            baseType = 'z.bigint()';
            break;
        case 'Decimal':
            baseType = 'z.number()'; // or custom decimal schema
            break;
        case 'Json':
            baseType = 'z.unknown()'; // or z.record(z.unknown())
            break;
        case 'Bytes':
            baseType = 'z.instanceof(Uint8Array)'; // or custom bytes schema
            break;
        default:
            // Unknown complex type (enum or relation). Without field kind context use a safe fallback.
            baseType = 'z.unknown()';
            break;
    }
    // Handle arrays
    if (isList) {
        baseType = `z.array(${baseType})`;
    }
    // Handle optional fields
    if (isOptional) {
        baseType = `${baseType}.optional()`;
    }
    return baseType;
}
/**
 * Field-aware base type resolver to correctly handle enums vs relations.
 */
function getBaseZodTypeForField(field, isOptionalOverride) {
    const isOptional = typeof isOptionalOverride === 'boolean' ? isOptionalOverride : !field.isRequired;
    let baseType;
    switch (field.type) {
        case 'String':
            baseType = 'z.string()';
            break;
        case 'Int':
            baseType = 'z.number().int()';
            break;
        case 'Float':
            baseType = 'z.number()';
            break;
        case 'Boolean':
            baseType = 'z.boolean()';
            break;
        case 'DateTime':
            baseType = 'z.date()';
            break;
        case 'BigInt':
            baseType = 'z.bigint()';
            break;
        case 'Decimal':
            baseType = 'z.number()';
            break;
        case 'Json':
            baseType = 'z.unknown()';
            break;
        case 'Bytes':
            baseType = 'z.instanceof(Uint8Array)';
            break;
        default:
            if (field.kind === 'enum') {
                baseType = `${String(field.type)}Schema`;
            }
            else {
                baseType = 'z.unknown()';
            }
            break;
    }
    if (field.isList) {
        baseType = `z.array(${baseType})`;
    }
    if (isOptional) {
        baseType = `${baseType}.optional()`;
    }
    return baseType;
}
/**
 * Get required imports for Zod schema generation
 *
 * @param fieldType - Prisma field type
 * @param customImports - Additional imports from validation methods
 * @returns Set of import statements
 */
function getRequiredImports(fieldType, customImports = new Set()) {
    const imports = new Set(customImports);
    // Always need zod
    imports.add('z');
    // Special imports for certain types
    switch (fieldType) {
        case 'Bytes':
            // Uint8Array is built-in, no import needed
            break;
        case 'DateTime':
            // Date is built-in, no import needed
            break;
        default:
            // Check if it's an enum type (would need enum import)
            if (!['String', 'Int', 'Float', 'Boolean', 'BigInt', 'Decimal', 'Json'].includes(fieldType)) {
                // This is likely an enum or model type - might need custom import
                imports.add(`${fieldType} // Enum import may be needed`);
            }
            break;
    }
    return imports;
}
/**
 * Convert a JavaScript object to a Zod object schema string
 */
function convertObjectToZodSchema(obj) {
    const properties = [];
    for (const [key, value] of Object.entries(obj)) {
        const zodType = inferZodTypeFromValue(value);
        properties.push(`${JSON.stringify(key)}: ${zodType}`);
    }
    return `{ ${properties.join(', ')} }`;
}
/**
 * Infer Zod type from a JavaScript value
 */
function inferZodTypeFromValue(value) {
    if (typeof value === 'string') {
        return `z.string()`;
    }
    else if (typeof value === 'number') {
        return Number.isInteger(value) ? `z.number().int()` : `z.number()`;
    }
    else if (typeof value === 'boolean') {
        return `z.boolean()`;
    }
    else if (Array.isArray(value)) {
        if (value.length === 0) {
            return `z.array(z.unknown())`;
        }
        const firstElementType = inferZodTypeFromValue(value[0]);
        return `z.array(${firstElementType})`;
    }
    else if (value && typeof value === 'object') {
        return `z.object(${convertObjectToZodSchema(value)})`;
    }
    else if (value === null) {
        return `z.null()`;
    }
    else {
        return `z.unknown()`;
    }
}
/**
 * Detect if comment contains @zod.import annotations
 *
 * @param comment - Normalized comment string
 * @returns True if @zod.import annotations are detected
 */
function detectCustomImports(comment) {
    if (!comment) {
        return false;
    }
    // Look for @zod.import patterns (case-insensitive)
    const importPattern = /@zod\s*\.\s*import\s*\(/i;
    return importPattern.test(comment);
}
/**
 * Parse custom imports from a comment string
 *
 * Supports both model-level and field-level imports:
 * - @zod.import(["import { myFunction } from 'mypackage'"])
 * - @zod.import(["import myFunction from 'mypackage'"])
 * - @zod.import(["import * as myModule from 'mypackage'"])
 *
 * @param comment - Comment string to parse
 * @param context - Context for error reporting (model or field)
 * @returns Custom imports parsing result
 */
function parseCustomImports(comment, context) {
    const result = {
        imports: [],
        parseErrors: [],
        isValid: true,
        customSchema: undefined,
    };
    if (!comment || !detectCustomImports(comment)) {
        return result;
    }
    try {
        // Simpler approach: find @zod.import patterns and check for .custom.use after
        const importPattern = /@zod\s*\.\s*import\s*\(([^)]+(?:\([^)]*\)[^)]*)*)\)/gi;
        let match;
        while ((match = importPattern.exec(comment)) !== null) {
            const importArrayString = match[1].trim();
            const endOfImport = match.index + match[0].length;
            const afterImport = comment.substring(endOfImport);
            // Look for validation methods after the import - support both field and model level
            let customSchemaString = null;
            // Field-level: .custom.use(...)
            const customUseMatch = afterImport.match(/^\s*\.\s*custom\s*\.\s*use\s*\(/);
            if (customUseMatch) {
                const startPos = customUseMatch.index + customUseMatch[0].length;
                const endPos = findBalancedParentheses(afterImport, startPos);
                if (endPos !== -1) {
                    customSchemaString = afterImport.substring(startPos, endPos).trim();
                }
            }
            // Model-level: .refine(...), .transform(...), etc.
            if (!customSchemaString) {
                const modelValidationMatch = afterImport.match(/^\s*\.\s*(refine|transform|superRefine|pipe)\s*\(/);
                if (modelValidationMatch) {
                    const method = modelValidationMatch[1];
                    const startPos = modelValidationMatch.index + modelValidationMatch[0].length;
                    const endPos = findBalancedParentheses(afterImport, startPos);
                    if (endPos !== -1) {
                        const validationArgs = afterImport.substring(startPos, endPos).trim();
                        customSchemaString = `${method}(${validationArgs})`;
                    }
                }
            }
            try {
                // Parse the import array - it should be a JSON array of strings
                const importStatements = parseImportArray(importArrayString, context);
                // Process each import statement
                for (const importStatement of importStatements) {
                    const customImport = parseImportStatement(importStatement, context);
                    if (customImport) {
                        result.imports.push(customImport);
                    }
                }
                // Store the custom schema if present
                if (customSchemaString) {
                    result.customSchema = customSchemaString;
                }
            }
            catch (error) {
                const contextStr = 'fieldName' in context
                    ? `${context.modelName}.${context.fieldName}`
                    : `${context.modelName}`;
                result.parseErrors.push(`${contextStr}: Failed to parse import array: ${error instanceof Error ? error.message : String(error)}`);
                result.isValid = false;
            }
        }
        // Deduplicate imports by import statement
        result.imports = deduplicateImports(result.imports);
    }
    catch (error) {
        const contextStr = 'fieldName' in context ? `${context.modelName}.${context.fieldName}` : `${context.modelName}`;
        result.parseErrors.push(`${contextStr}: Failed to parse custom imports: ${error instanceof Error ? error.message : String(error)}`);
        result.isValid = false;
    }
    return result;
}
/**
 * Parse import array string into individual import statements
 *
 * @param importArrayString - String representation of import array
 * @param context - Context for error reporting
 * @returns Array of import statement strings
 */
function parseImportArray(importArrayString, _context) {
    const trimmedInput = importArrayString.trim();
    if (!trimmedInput) {
        return [];
    }
    const statementsFromJson = parseImportArrayJson(trimmedInput);
    if (statementsFromJson.length > 0) {
        return statementsFromJson;
    }
    const fallbackStatements = extractImportStatements(trimmedInput);
    if (fallbackStatements.length > 0) {
        return fallbackStatements;
    }
    throw new Error(`Invalid import array format. Expected JSON array of strings, got: ${importArrayString}`);
}
function extractImportStatements(segment) {
    const cleanedSegment = stripWrappingQuotes(segment);
    if (!cleanedSegment) {
        return [];
    }
    const statements = [];
    const importRegex = /import\s+(?:type\s+)?[\s\S]*?from\s+['"`][^'"`]+['"`](?:\s*;)?/g;
    let match;
    while ((match = importRegex.exec(cleanedSegment)) !== null) {
        const statement = match[0].trim();
        if (statement) {
            statements.push(statement);
        }
    }
    if (!statements.length && cleanedSegment.startsWith('import')) {
        statements.push(cleanedSegment);
    }
    return statements;
}
function stripWrappingQuotes(value) {
    const trimmed = value.trim();
    if (trimmed.length >= 2) {
        const first = trimmed[0];
        const last = trimmed[trimmed.length - 1];
        if (first === last && (first === '"' || first === "'" || first === '`')) {
            const inner = trimmed.slice(1, -1);
            return unescapeString(inner);
        }
    }
    return trimmed;
}
function unescapeString(value) {
    try {
        const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
        return JSON.parse(`"${escaped}"`);
    }
    catch {
        return value;
    }
}
function parseImportArrayJson(raw) {
    try {
        const jsonText = raw.startsWith('[') ? raw : `[${raw}]`;
        const parsed = JSON.parse(jsonText);
        if (!Array.isArray(parsed)) {
            return [];
        }
        const statements = [];
        for (const entry of parsed) {
            if (typeof entry !== 'string') {
                continue;
            }
            const extracted = extractImportStatements(entry);
            statements.push(...extracted);
        }
        return statements;
    }
    catch {
        return [];
    }
}
/**
 * Parse a single import statement into a CustomImport object
 *
 * @param importStatement - Import statement string
 * @param context - Context for error reporting
 * @returns CustomImport object or null if invalid
 */
function parseImportStatement(importStatement, context) {
    if (!importStatement || typeof importStatement !== 'string') {
        return null;
    }
    const trimmed = importStatement.trim();
    // Basic validation - must start with import and include a from-clause
    if (!/^\s*import\b/.test(trimmed) || !/\bfrom\s+['"`]/.test(trimmed)) {
        const contextStr = 'fieldName' in context ? `${context.modelName}.${context.fieldName}` : `${context.modelName}`;
        logger_1.logger.warn(`${contextStr}: Invalid import statement format: ${trimmed}`);
        return null;
    }
    // Detect type-only imports
    const isTypeOnly = /^\s*import\s+type\b/.test(trimmed);
    let hasValueSpecifier = false;
    let hasTypeSpecifier = isTypeOnly;
    // Extract source module
    const fromMatch = trimmed.match(/from\s+['"`]([^'"`]+)['"`]/);
    if (!fromMatch) {
        const contextStr = 'fieldName' in context ? `${context.modelName}.${context.fieldName}` : `${context.modelName}`;
        logger_1.logger.warn(`${contextStr}: Could not extract source module from: ${trimmed}`);
        return null;
    }
    const source = fromMatch[1];
    // Extract import clause (between the import keyword and the from-clause)
    const importPrefixMatch = trimmed.match(/^\s*import\s+(?:type\s+)?/);
    const clauseStart = importPrefixMatch ? importPrefixMatch[0].length : 0;
    const fromIndex = trimmed.search(/\bfrom\s+['"`]/);
    if (fromIndex === -1 || clauseStart === 0) {
        const contextStr = 'fieldName' in context ? `${context.modelName}.${context.fieldName}` : `${context.modelName}`;
        logger_1.logger.warn(`${contextStr}: Could not determine import clause: ${trimmed}`);
        return null;
    }
    const importClause = trimmed.substring(clauseStart, fromIndex).trim();
    // Determine import type(s) and extract imported items
    let isDefault = false;
    let isNamespace = false;
    const importedItems = [];
    if (importClause.startsWith('* as ')) {
        isNamespace = true;
        const nameMatch = importClause.match(/\*\s+as\s+([A-Za-z_$][\w$]*)/);
        if (nameMatch) {
            importedItems.push(nameMatch[1]);
        }
        hasValueSpecifier = true;
    }
    else {
        const namedStart = importClause.indexOf('{');
        const namedEnd = importClause.lastIndexOf('}');
        // Default portion (before named block)
        const defaultCandidate = namedStart > 0
            ? importClause
                .slice(0, namedStart)
                .replace(/,?\s*$/, '')
                .trim()
            : namedStart === -1
                ? importClause.trim().replace(/,?\s*$/, '')
                : '';
        if (defaultCandidate) {
            isDefault = true;
            hasValueSpecifier = true;
            importedItems.push(defaultCandidate);
        }
        // Named imports block
        if (namedStart !== -1 && namedEnd !== -1 && namedEnd > namedStart) {
            const namedBlock = importClause.slice(namedStart + 1, namedEnd);
            const namedItems = namedBlock
                .split(',')
                .map((item) => item.trim())
                .filter(Boolean)
                .map((item) => {
                var _a;
                const aliasMatch = item.match(/^([A-Za-z_$][\w$]*)\s+as\s+([A-Za-z_$][\w$]*)$/);
                if (aliasMatch) {
                    hasValueSpecifier = true;
                    return aliasMatch[2];
                }
                const typeMatch = item.match(/^type\s+([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/i);
                if (typeMatch) {
                    hasTypeSpecifier = true;
                    return (_a = typeMatch[2]) !== null && _a !== void 0 ? _a : typeMatch[1];
                }
                hasValueSpecifier = true;
                return item;
            });
            importedItems.push(...namedItems);
        }
    }
    const effectiveTypeOnly = isTypeOnly || (!hasValueSpecifier && hasTypeSpecifier);
    return {
        importStatement: trimmed,
        source,
        importedItems,
        isDefault,
        isNamespace,
        isTypeOnly: effectiveTypeOnly,
        originalStatement: importStatement,
    };
}
/**
 * Find balanced parentheses in a string starting from a given position
 *
 * @param text - Text to search in
 * @param startPos - Starting position (after opening parenthesis)
 * @returns End position of balanced parentheses or -1 if not found
 */
function findBalancedParentheses(text, startPos) {
    let depth = 1;
    let pos = startPos;
    let inString = false;
    let quote = '';
    while (pos < text.length && depth > 0) {
        const ch = text[pos];
        if (inString) {
            if (ch === '\\') {
                pos += 2;
                continue;
            }
            if (ch === quote) {
                inString = false;
                quote = '';
            }
            pos++;
            continue;
        }
        if (ch === '"' || ch === "'") {
            inString = true;
            quote = ch;
        }
        else if (ch === '(') {
            depth++;
        }
        else if (ch === ')') {
            depth--;
            if (depth === 0) {
                return pos;
            }
        }
        pos++;
    }
    return -1;
}
/**
 * Deduplicate imports by import statement
 *
 * @param imports - Array of CustomImport objects
 * @returns Deduplicated array of CustomImport objects
 */
function deduplicateImports(imports) {
    const seen = new Set();
    const deduplicated = [];
    for (const customImport of imports) {
        if (!seen.has(customImport.importStatement)) {
            seen.add(customImport.importStatement);
            deduplicated.push(customImport);
        }
    }
    return deduplicated;
}
/**
 * Extract custom imports from model documentation
 *
 * @param model - Prisma DMMF model
 * @returns Custom imports parsing result
 */
function extractModelCustomImports(model) {
    const modelComment = model.documentation || '';
    if (!modelComment.trim()) {
        return {
            imports: [],
            parseErrors: [],
            isValid: true,
            customSchema: undefined,
        };
    }
    const context = {
        modelName: model.name,
        comment: modelComment,
    };
    return parseCustomImports(modelComment, context);
}
/**
 * Extract custom imports from field documentation
 *
 * @param field - Prisma DMMF field
 * @param modelName - Name of the model containing the field
 * @returns Custom imports parsing result
 */
function extractFieldCustomImports(field, modelName) {
    const fieldComment = field.documentation || '';
    if (!fieldComment.trim()) {
        return {
            imports: [],
            parseErrors: [],
            isValid: true,
            customSchema: undefined,
        };
    }
    const context = {
        modelName,
        fieldName: field.name,
        fieldType: field.type,
        comment: fieldComment,
        isOptional: !field.isRequired,
        isList: field.isList,
    };
    return parseCustomImports(fieldComment, context);
}
//# sourceMappingURL=zod-comments.js.map