"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.parseStringDef = exports.zodPatterns = void 0; const errorMessages_1 = require("../errorMessages.js"); let emojiRegex; /** * Generated from the regular expressions found here as of 2024-05-22: * https://github.com/colinhacks/zod/blob/master/src/types.ts. * * Expressions with /i flag have been changed accordingly. */ exports.zodPatterns = { /** * `c` was changed to `[cC]` to replicate /i flag */ cuid: /^[cC][^\s-]{8,}$/, cuid2: /^[0-9a-z]+$/, ulid: /^[0-9A-HJKMNP-TV-Z]{26}$/, /** * `a-z` was added to replicate /i flag */ email: /^(?!\.)(?!.*\.\.)([a-zA-Z0-9_'+\-\.]*)[a-zA-Z0-9_+-]@([a-zA-Z0-9][a-zA-Z0-9\-]*\.)+[a-zA-Z]{2,}$/, /** * Constructed a valid Unicode RegExp * * Lazily instantiate since this type of regex isn't supported * in all envs (e.g. React Native). * * See: * https://github.com/colinhacks/zod/issues/2433 * Fix in Zod: * https://github.com/colinhacks/zod/commit/9340fd51e48576a75adc919bff65dbc4a5d4c99b */ emoji: () => { if (emojiRegex === undefined) { emojiRegex = RegExp('^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$', 'u'); } return emojiRegex; }, /** * Unused */ uuid: /^[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{4}\b-[0-9a-fA-F]{12}$/, /** * Unused */ ipv4: /^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/, /** * Unused */ ipv6: /^(([a-f0-9]{1,4}:){7}|::([a-f0-9]{1,4}:){0,6}|([a-f0-9]{1,4}:){1}:([a-f0-9]{1,4}:){0,5}|([a-f0-9]{1,4}:){2}:([a-f0-9]{1,4}:){0,4}|([a-f0-9]{1,4}:){3}:([a-f0-9]{1,4}:){0,3}|([a-f0-9]{1,4}:){4}:([a-f0-9]{1,4}:){0,2}|([a-f0-9]{1,4}:){5}:([a-f0-9]{1,4}:){0,1})([a-f0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/, base64: /^([0-9a-zA-Z+/]{4})*(([0-9a-zA-Z+/]{2}==)|([0-9a-zA-Z+/]{3}=))?$/, nanoid: /^[a-zA-Z0-9_-]{21}$/, }; function parseStringDef(def, refs) { const res = { type: 'string', }; function processPattern(value) { return refs.patternStrategy === 'escape' ? escapeNonAlphaNumeric(value) : value; } if (def.checks) { for (const check of def.checks) { switch (check.kind) { case 'min': (0, errorMessages_1.setResponseValueAndErrors)(res, 'minLength', typeof res.minLength === 'number' ? Math.max(res.minLength, check.value) : check.value, check.message, refs); break; case 'max': (0, errorMessages_1.setResponseValueAndErrors)(res, 'maxLength', typeof res.maxLength === 'number' ? Math.min(res.maxLength, check.value) : check.value, check.message, refs); break; case 'email': switch (refs.emailStrategy) { case 'format:email': addFormat(res, 'email', check.message, refs); break; case 'format:idn-email': addFormat(res, 'idn-email', check.message, refs); break; case 'pattern:zod': addPattern(res, exports.zodPatterns.email, check.message, refs); break; } break; case 'url': addFormat(res, 'uri', check.message, refs); break; case 'uuid': addFormat(res, 'uuid', check.message, refs); break; case 'regex': addPattern(res, check.regex, check.message, refs); break; case 'cuid': addPattern(res, exports.zodPatterns.cuid, check.message, refs); break; case 'cuid2': addPattern(res, exports.zodPatterns.cuid2, check.message, refs); break; case 'startsWith': addPattern(res, RegExp(`^${processPattern(check.value)}`), check.message, refs); break; case 'endsWith': addPattern(res, RegExp(`${processPattern(check.value)}$`), check.message, refs); break; case 'datetime': addFormat(res, 'date-time', check.message, refs); break; case 'date': addFormat(res, 'date', check.message, refs); break; case 'time': addFormat(res, 'time', check.message, refs); break; case 'duration': addFormat(res, 'duration', check.message, refs); break; case 'length': (0, errorMessages_1.setResponseValueAndErrors)(res, 'minLength', typeof res.minLength === 'number' ? Math.max(res.minLength, check.value) : check.value, check.message, refs); (0, errorMessages_1.setResponseValueAndErrors)(res, 'maxLength', typeof res.maxLength === 'number' ? Math.min(res.maxLength, check.value) : check.value, check.message, refs); break; case 'includes': { addPattern(res, RegExp(processPattern(check.value)), check.message, refs); break; } case 'ip': { if (check.version !== 'v6') { addFormat(res, 'ipv4', check.message, refs); } if (check.version !== 'v4') { addFormat(res, 'ipv6', check.message, refs); } break; } case 'emoji': addPattern(res, exports.zodPatterns.emoji, check.message, refs); break; case 'ulid': { addPattern(res, exports.zodPatterns.ulid, check.message, refs); break; } case 'base64': { switch (refs.base64Strategy) { case 'format:binary': { addFormat(res, 'binary', check.message, refs); break; } case 'contentEncoding:base64': { (0, errorMessages_1.setResponseValueAndErrors)(res, 'contentEncoding', 'base64', check.message, refs); break; } case 'pattern:zod': { addPattern(res, exports.zodPatterns.base64, check.message, refs); break; } } break; } case 'nanoid': { addPattern(res, exports.zodPatterns.nanoid, check.message, refs); } case 'toLowerCase': case 'toUpperCase': case 'trim': break; default: ((_) => { })(check); } } } return res; } exports.parseStringDef = parseStringDef; const escapeNonAlphaNumeric = (value) => Array.from(value) .map((c) => (/[a-zA-Z0-9]/.test(c) ? c : `\\${c}`)) .join(''); const addFormat = (schema, value, message, refs) => { if (schema.format || schema.anyOf?.some((x) => x.format)) { if (!schema.anyOf) { schema.anyOf = []; } if (schema.format) { schema.anyOf.push({ format: schema.format, ...(schema.errorMessage && refs.errorMessages && { errorMessage: { format: schema.errorMessage.format }, }), }); delete schema.format; if (schema.errorMessage) { delete schema.errorMessage.format; if (Object.keys(schema.errorMessage).length === 0) { delete schema.errorMessage; } } } schema.anyOf.push({ format: value, ...(message && refs.errorMessages && { errorMessage: { format: message } }), }); } else { (0, errorMessages_1.setResponseValueAndErrors)(schema, 'format', value, message, refs); } }; const addPattern = (schema, regex, message, refs) => { if (schema.pattern || schema.allOf?.some((x) => x.pattern)) { if (!schema.allOf) { schema.allOf = []; } if (schema.pattern) { schema.allOf.push({ pattern: schema.pattern, ...(schema.errorMessage && refs.errorMessages && { errorMessage: { pattern: schema.errorMessage.pattern }, }), }); delete schema.pattern; if (schema.errorMessage) { delete schema.errorMessage.pattern; if (Object.keys(schema.errorMessage).length === 0) { delete schema.errorMessage; } } } schema.allOf.push({ pattern: processRegExp(regex, refs), ...(message && refs.errorMessages && { errorMessage: { pattern: message } }), }); } else { (0, errorMessages_1.setResponseValueAndErrors)(schema, 'pattern', processRegExp(regex, refs), message, refs); } }; // Mutate z.string.regex() in a best attempt to accommodate for regex flags when applyRegexFlags is true const processRegExp = (regexOrFunction, refs) => { const regex = typeof regexOrFunction === 'function' ? regexOrFunction() : regexOrFunction; if (!refs.applyRegexFlags || !regex.flags) return regex.source; // Currently handled flags const flags = { i: regex.flags.includes('i'), m: regex.flags.includes('m'), s: regex.flags.includes('s'), // `.` matches newlines }; // The general principle here is to step through each character, one at a time, applying mutations as flags require. We keep track when the current character is escaped, and when it's inside a group /like [this]/ or (also) a range like /[a-z]/. The following is fairly brittle imperative code; edit at your peril! const source = flags.i ? regex.source.toLowerCase() : regex.source; let pattern = ''; let isEscaped = false; let inCharGroup = false; let inCharRange = false; for (let i = 0; i < source.length; i++) { if (isEscaped) { pattern += source[i]; isEscaped = false; continue; } if (flags.i) { if (inCharGroup) { if (source[i].match(/[a-z]/)) { if (inCharRange) { pattern += source[i]; pattern += `${source[i - 2]}-${source[i]}`.toUpperCase(); inCharRange = false; } else if (source[i + 1] === '-' && source[i + 2]?.match(/[a-z]/)) { pattern += source[i]; inCharRange = true; } else { pattern += `${source[i]}${source[i].toUpperCase()}`; } continue; } } else if (source[i].match(/[a-z]/)) { pattern += `[${source[i]}${source[i].toUpperCase()}]`; continue; } } if (flags.m) { if (source[i] === '^') { pattern += `(^|(?<=[\r\n]))`; continue; } else if (source[i] === '$') { pattern += `($|(?=[\r\n]))`; continue; } } if (flags.s && source[i] === '.') { pattern += inCharGroup ? `${source[i]}\r\n` : `[${source[i]}\r\n]`; continue; } pattern += source[i]; if (source[i] === '\\') { isEscaped = true; } else if (inCharGroup && source[i] === ']') { inCharGroup = false; } else if (!inCharGroup && source[i] === '[') { inCharGroup = true; } } try { const regexTest = new RegExp(pattern); } catch { console.warn(`Could not convert regex pattern at ${refs.currentPath.join('/')} to a flag-independent form! Falling back to the flag-ignorant source`); return regex.source; } return pattern; }; //# sourceMappingURL=string.js.map