UNPKG

veffect

Version:

powerful TypeScript validation library built on the robust foundation of Effect combining exceptional type safety, high performance, and developer experience. Taking inspiration from Effect's functional principles, VEffect delivers a balanced approach tha

604 lines (603 loc) 34.1 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.string = string; /** * String schema implementation */ const E = __importStar(require("../internal/effect")); const validator_1 = require("../validator"); const errors_1 = require("../errors"); /** * Create a string schema */ function string() { const validations = []; let errorMessage = undefined; const schema = { _tag: 'StringSchema', minLength: (min, message) => { validations.push((input, options) => input.length >= min ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || `String must be at least ${min} characters`, options === null || options === void 0 ? void 0 : options.path))); return schema; }, maxLength: (max, message) => { validations.push((input, options) => input.length <= max ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || `String must be at most ${max} characters`, options === null || options === void 0 ? void 0 : options.path))); return schema; }, length: (length, message) => { validations.push((input, options) => input.length === length ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || `String must be exactly ${length} characters`, options === null || options === void 0 ? void 0 : options.path))); return schema; }, regex: (pattern, message) => { validations.push((input, options) => pattern.test(input) ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || `String does not match pattern ${pattern}`, options === null || options === void 0 ? void 0 : options.path))); return schema; }, email: (message) => { const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return schema.regex(emailPattern, message || 'Invalid email address'); }, url: (message) => { const urlPattern = /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/; return schema.regex(urlPattern, message || 'Invalid URL'); }, uuid: (message) => { const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i; return schema.regex(uuidPattern, message || 'Invalid UUID'); }, cuid: (message) => { const cuidPattern = /^c[^\s-]{8,}$/i; return schema.regex(cuidPattern, message || 'Invalid CUID'); }, cuid2: (message) => { const cuid2Pattern = /^[a-z][a-z0-9]*$/; return schema.regex(cuid2Pattern, message || 'Invalid CUID2'); }, ulid: (message) => { const ulidPattern = /^[0-9A-HJKMNP-TV-Z]{26}$/; return schema.regex(ulidPattern, message || 'Invalid ULID'); }, startsWith: (substring, message) => { validations.push((input, options) => input.startsWith(substring) ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || `String must start with "${substring}"`, options === null || options === void 0 ? void 0 : options.path))); return schema; }, endsWith: (substring, message) => { validations.push((input, options) => input.endsWith(substring) ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || `String must end with "${substring}"`, options === null || options === void 0 ? void 0 : options.path))); return schema; }, includes: (substring, message) => { validations.push((input, options) => input.includes(substring) ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || `String must include "${substring}"`, options === null || options === void 0 ? void 0 : options.path))); return schema; }, trim: (message) => { // Create a transformation function and return a complete StringSchema const transformedValidator = (0, validator_1.createEffectValidator)((input, options) => { return E.pipe(schema.toValidator().validate(input, options), E.map(value => value.trim())); }); return { ...schema, toValidator: () => transformedValidator }; }, toLowerCase: () => { // Create a transformation function and return a complete StringSchema const transformedValidator = (0, validator_1.createEffectValidator)((input, options) => { return E.pipe(schema.toValidator().validate(input, options), E.map(value => value.toLowerCase())); }); return { ...schema, toValidator: () => transformedValidator }; }, toUpperCase: () => { // Create a transformation function and return a complete StringSchema const transformedValidator = (0, validator_1.createEffectValidator)((input, options) => { return E.pipe(schema.toValidator().validate(input, options), E.map(value => value.toUpperCase())); }); return { ...schema, toValidator: () => transformedValidator }; }, datetime: (options, message) => { const { offset = false, precision, local = false } = options || {}; validations.push((input, options) => { // ISO datetime regex patterns let pattern; if (local) { // Local datetime without timezone pattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?$/; } else if (offset) { // Datetime with timezone offset pattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?([+-]\d{1,2}(?::?\d{2})?|Z)$/; } else { // Only UTC datetime (with Z) pattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/; } // Check the format if (!pattern.test(input)) { return E.fail(new errors_1.StringValidationError(message || 'Invalid datetime string', options === null || options === void 0 ? void 0 : options.path)); } // Parse date components const [datePart, timePart] = input.split('T'); const [year, month, day] = datePart.split('-').map(Number); let [hoursStr, minutesStr, secondsStr] = timePart.split(':'); // Extract seconds without milliseconds const secondsMatch = secondsStr.match(/^(\d+)/); const hours = parseInt(hoursStr, 10); const minutes = parseInt(minutesStr, 10); const seconds = secondsMatch ? parseInt(secondsMatch[1], 10) : 0; // Check time values if (hours < 0 || hours > 23 || minutes < 0 || minutes > 59 || seconds < 0 || seconds > 59) { return E.fail(new errors_1.StringValidationError(message || 'Invalid time component in datetime', options === null || options === void 0 ? void 0 : options.path)); } // Check date validity using native Date const date = new Date(year, month - 1, day); if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) { return E.fail(new errors_1.StringValidationError(message || 'Invalid date in datetime string', options === null || options === void 0 ? void 0 : options.path)); } // Check timezone offset validity if present if (offset) { // Check for valid offset format if not Z if (input.includes('+') || input.includes('-')) { // Extract the offset part const offsetMatch = input.match(/[+-](\d{1,2})(?::?(\d{2}))?$/); if (offsetMatch) { const offsetHours = parseInt(offsetMatch[1], 10); const offsetMinutes = offsetMatch[2] ? parseInt(offsetMatch[2], 10) : 0; // Validate offset hours and minutes if (offsetHours > 23 || offsetMinutes > 59) { return E.fail(new errors_1.StringValidationError(message || 'Invalid timezone offset in datetime', options === null || options === void 0 ? void 0 : options.path)); } } } } // If precision is specified, check decimal precision of seconds if (precision !== undefined) { const decimalMatch = input.match(/\.(\d+)/); if (!decimalMatch && precision > 0) { return E.fail(new errors_1.StringValidationError(message || `Datetime requires ${precision} decimal places precision`, options === null || options === void 0 ? void 0 : options.path)); } if (decimalMatch) { const decimals = decimalMatch[1].length; if (decimals !== precision) { return E.fail(new errors_1.StringValidationError(message || `Datetime requires exactly ${precision} decimal places precision`, options === null || options === void 0 ? void 0 : options.path)); } } } return E.succeed(input); }); return schema; }, ip: (options, message) => { // IPv4 pattern without leading zeros (to match test expectations) const ipv4Pattern = /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$/; // Comprehensive IPv6 pattern that handles all valid forms including abbreviated const ipv6Pattern = /^(?:(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,7}:|(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}|(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}|(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}|(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})|:(?:(?::[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(?::[0-9a-fA-F]{1,4}){0,4}%[0-9a-zA-Z]+|::(?:ffff(?::0{1,4})?:)?(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])|(?:[0-9a-fA-F]{1,4}:){1,4}:(?:(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9])\.){3}(?:25[0-5]|(?:2[0-4]|1?[0-9])?[0-9]))$/i; validations.push((input, validatorOptions) => { // Determine the version parameter from different input types let version; if (typeof options === 'string') { // If options is a string value like "v4" or "v6" if (options === "v4" || options === "v6") { version = options; } else { // If it's not a valid version string, it's the message message = options; } } else if (options && typeof options === 'object') { version = options.version; } // Validate IPv4 if (version === 'v4') { return ipv4Pattern.test(input) ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || 'Invalid IPv4 address', validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); } // Validate IPv6 if (version === 'v6') { return ipv6Pattern.test(input) ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || 'Invalid IPv6 address', validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); } // Validate either IPv4 or IPv6 return ipv4Pattern.test(input) || ipv6Pattern.test(input) ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || 'Invalid IP address', validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); }); return schema; }, nonempty: (message) => { validations.push((input, options) => input.length > 0 ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || 'String must not be empty', options === null || options === void 0 ? void 0 : options.path))); return schema; }, // New validators emoji: (message) => { validations.push((input, options) => { // Use a more reliable approach to detect emoji const emojiRegex = /(\p{Emoji_Presentation}|\p{Extended_Pictographic})/u; // Check if the string contains at least one emoji and only consists of emojis and emoji modifiers const containsEmoji = emojiRegex.test(input); const nonEmojiContent = input.replace(/(\p{Emoji_Presentation}|\p{Extended_Pictographic}|\p{Emoji_Modifier}|\p{Emoji_Component}|\u200D|\uFE0F)/gu, ''); if (!containsEmoji || nonEmojiContent.length > 0) { return E.fail(new errors_1.StringValidationError(message || 'Invalid emoji string', options === null || options === void 0 ? void 0 : options.path)); } return E.succeed(input); }); return schema; }, nanoid: (message) => { // Nanoid format: URL-friendly symbols (A-Za-z0-9_-) const nanoidPattern = /^[A-Za-z0-9_-]+$/; validations.push((input, options) => nanoidPattern.test(input) ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || 'Invalid nanoid', options === null || options === void 0 ? void 0 : options.path))); return schema; }, cidr: (options, message) => { validations.push((input, validatorOptions) => { let version; // Handle options if (typeof options === 'string') { // If options is a string value like "v4" or "v6" if (options === "v4" || options === "v6") { version = options; } else { // If it's not a valid version string, it's the message message = options; } } else if (options && typeof options === 'object') { version = options.version; } // IPv4 CIDR validation const ipv4CidrRegex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$/; // IPv6 CIDR validation with improved validation const ipv6CidrRegex = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:(:[0-9a-fA-F]{1,4}){1,6}|:((:[0-9a-fA-F]{1,4}){1,7}|:))(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))$/; if (version === "v4") { // Only validate IPv4 if (!ipv4CidrRegex.test(input)) { return E.fail(new errors_1.StringValidationError(message || 'Invalid IPv4 CIDR notation', validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); } } else if (version === "v6") { // Only validate IPv6 if (!ipv6CidrRegex.test(input)) { return E.fail(new errors_1.StringValidationError(message || 'Invalid IPv6 CIDR notation', validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); } } else { // Validate both formats if (!ipv4CidrRegex.test(input) && !ipv6CidrRegex.test(input)) { return E.fail(new errors_1.StringValidationError(message || 'Invalid CIDR notation', validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); } } return E.succeed(input); }); return schema; }, base64: (options, message) => { validations.push((input, validatorOptions) => { // Default settings for standard base64 let paddingRequired = true; let urlSafe = false; // Handle options if (typeof options === 'object' && options !== null) { paddingRequired = options.padding !== false; // Default to true unless explicitly set to false urlSafe = options.urlSafe === true; // Default to false unless explicitly set to true } else if (typeof options === 'string') { // If options is a string, it's actually the message message = options; } // If the input is empty, it's not valid base64 if (input.length === 0) { return E.fail(new errors_1.StringValidationError(message || 'Base64 string cannot be empty', validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); } // Base64 characters - standard or URL-safe const base64Chars = urlSafe ? 'A-Za-z0-9\\-_' : 'A-Za-z0-9\\+\\/'; // Get padding (trailing '=' characters) const matches = input.match(/=*$/); const padding = matches && matches[0] ? matches[0].length : 0; const contentLength = input.length - padding; // Base rules: // 1. Content should only have valid base64 characters // 2. Padding, if present, should be 0, 1, or 2 characters // 3. Validate that padding is correct based on content length // 4. Content length without padding must be a multiple of 4 when padding is required // Check if characters are valid base64 characters const contentRegex = new RegExp(`^[${base64Chars}]+$`); if (!contentRegex.test(input.slice(0, contentLength))) { return E.fail(new errors_1.StringValidationError(message || 'Invalid base64 characters', validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); } // Check padding count if (padding > 2) { return E.fail(new errors_1.StringValidationError(message || 'Base64 padding too long (max 2 characters)', validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); } // Validate padding based on content length // Calculate expected padding based on content length modulo 4 const mod = contentLength % 4; // When padding is required, the total length must be a multiple of 4 if (paddingRequired) { if (input.length % 4 !== 0) { return E.fail(new errors_1.StringValidationError(message || `Invalid base64 format: length must be a multiple of 4 when padding is required`, validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); } // Expected padding based on content length mod 4 const expectedPadding = mod === 0 ? 0 : 4 - mod; if (padding !== expectedPadding) { return E.fail(new errors_1.StringValidationError(message || `Invalid base64 padding (expected ${expectedPadding}, got ${padding})`, validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); } } else if (mod === 1) { // Even without padding requirement, mod 1 is always invalid in base64 return E.fail(new errors_1.StringValidationError(message || 'Invalid base64 format: content length mod 4 cannot be 1', validatorOptions === null || validatorOptions === void 0 ? void 0 : validatorOptions.path)); } return E.succeed(input); }); return schema; }, date: (message) => { // ISO date format YYYY-MM-DD, allowing for multi-digit years const datePattern = /^\d{4,}-\d{2}-\d{2}$/; validations.push((input, options) => { // Check format if (!datePattern.test(input)) { return E.fail(new errors_1.StringValidationError(message || 'Invalid date string. Format must be YYYY-MM-DD', options === null || options === void 0 ? void 0 : options.path)); } // Check if it's a valid date const [year, month, day] = input.split('-').map(Number); // Simple validation for date range if (month < 1 || month > 12 || day < 1 || day > 31) { return E.fail(new errors_1.StringValidationError(message || 'Invalid date (month or day out of range)', options === null || options === void 0 ? void 0 : options.path)); } // Additional validation for specific months if ((month === 4 || month === 6 || month === 9 || month === 11) && day > 30) { return E.fail(new errors_1.StringValidationError(message || `Invalid date (${month} has only 30 days)`, options === null || options === void 0 ? void 0 : options.path)); } // February validation (including leap years) if (month === 2) { const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0); const maxDays = isLeapYear ? 29 : 28; if (day > maxDays) { return E.fail(new errors_1.StringValidationError(message || `Invalid date (February has ${maxDays} days in ${year})`, options === null || options === void 0 ? void 0 : options.path)); } } return E.succeed(input); }); return schema; }, time: (options, message) => { const { precision } = options || {}; validations.push((input, options) => { // Basic time format HH:MM:SS[.sss] - no timezone const baseTimePattern = /^([01]\d|2[0-3]):([0-5]\d):([0-5]\d)(?:\.(\d+))?$/; const match = input.match(baseTimePattern); if (!match) { return E.fail(new errors_1.StringValidationError(message || 'Invalid time string. Format must be HH:MM:SS[.sss]', options === null || options === void 0 ? void 0 : options.path)); } // Check precision if specified if (precision !== undefined) { const decimalPart = match[4]; if (!decimalPart && precision > 0) { return E.fail(new errors_1.StringValidationError(message || `Time requires ${precision} decimal places precision`, options === null || options === void 0 ? void 0 : options.path)); } if (decimalPart && decimalPart.length !== precision) { return E.fail(new errors_1.StringValidationError(message || `Time requires exactly ${precision} decimal places precision`, options === null || options === void 0 ? void 0 : options.path)); } } return E.succeed(input); }); return schema; }, duration: (message) => { // ISO 8601 duration format const durationPattern = /^P(?!$)(\d+Y)?(\d+M)?(\d+W)?(\d+D)?(T(?=\d)(\d+H)?(\d+M)?(\d+S)?)?$/; validations.push((input, options) => durationPattern.test(input) ? E.succeed(input) : E.fail(new errors_1.StringValidationError(message || 'Invalid ISO 8601 duration', options === null || options === void 0 ? void 0 : options.path))); return schema; }, // Refinement implementation refine: (refinement, message) => { // Check if the refinement is async const testIsAsync = refinement("") instanceof Promise; if (testIsAsync) { // For async refinement, return a special schema that handles async validation return { ...schema, _tag: 'StringSchema', toValidator: () => { const baseValidator = schema.toValidator(); return { ...baseValidator, // Override validateAsync to handle the async refinement validateAsync: async (input, options) => { // Validate the input type first if (typeof input !== 'string') { throw { _tag: 'TypeValidationError', message: errorMessage || 'Value must be a string', expected: 'string', received: typeof input, path: options === null || options === void 0 ? void 0 : options.path }; } // Run all the synchronous validations first let result = input; try { // Apply the async refinement const isValid = await refinement(result); if (!isValid) { throw { _tag: 'RefinementValidationError', message: typeof message === 'function' ? message(result) : message || 'Failed refinement', path: options === null || options === void 0 ? void 0 : options.path }; } return result; } catch (error) { // Any error in the refinement should result in a validation error throw { _tag: 'RefinementValidationError', message: typeof message === 'function' ? message(result) : message || 'Failed refinement', path: options === null || options === void 0 ? void 0 : options.path }; } } }; } }; } // For synchronous refinement, use the standard approach validations.push((input, options) => refinement(input) ? E.succeed(input) : E.fail({ _tag: 'RefinementValidationError', message: typeof message === 'function' ? message(input) : message || 'Failed refinement', path: options === null || options === void 0 ? void 0 : options.path })); return schema; }, // Predicate implementation (alias for refine) predicate: (predicate, message) => { return schema.refine(predicate, message); }, // Transform implementation transform: (transformer) => { return { _tag: 'TransformedSchema', toValidator: () => (0, validator_1.createEffectValidator)((input, options) => { return E.pipe(schema.toValidator().validate(input, options), E.map(value => transformer(value))); }) }; }, // Default value implementation default: (defaultValue) => { // Create a default validator function const defaultValidator = (0, validator_1.createEffectValidator)((input, options) => { if (input === undefined) { const value = typeof defaultValue === 'function' ? defaultValue() : defaultValue; return E.succeed(value); } return schema.toValidator().validate(input, options); }); // Return a new schema with all properties preserved return { ...schema, _tag: 'StringSchema', toValidator: () => defaultValidator }; }, // Nullable implementation nullable: () => { return { _tag: 'NullableSchema', toValidator: () => (0, validator_1.createEffectValidator)((input, options) => { if (input === null) { return E.succeed(null); } return schema.toValidator().validate(input, options); }) }; }, // Optional implementation optional: () => { return { _tag: 'OptionalSchema', toValidator: () => (0, validator_1.createEffectValidator)((input, options) => { if (input === undefined) { return E.succeed(undefined); } return schema.toValidator().validate(input, options); }) }; }, // Nullish implementation nullish: () => { return { _tag: 'NullishSchema', toValidator: () => (0, validator_1.createEffectValidator)((input, options) => { if (input === null || input === undefined) { return E.succeed(input); } return schema.toValidator().validate(input, options); }) }; }, // Custom error message error: (message) => { errorMessage = message; return schema; }, toValidator: () => (0, validator_1.createEffectValidator)((input, options) => { if (typeof input !== 'string') { return E.fail(new errors_1.TypeValidationError(errorMessage || 'Value must be a string', 'string', typeof input, options === null || options === void 0 ? void 0 : options.path)); } return validations.reduce((acc, validation) => E.flatMap(acc, val => validation(val, options)), E.succeed(input)); }) }; return schema; }