UNPKG

@mmstack/form-validation

Version:

This is an internal library for providing localized validators :)

727 lines (692 loc) 23.5 kB
import { InjectionToken, LOCALE_ID, inject } from '@angular/core'; import { formatDate } from '@angular/common'; const INTERNAL_ERROR_MERGE_DELIM = '::INTERNAL_MMSTACK_MERGE_DELIM::'; function defaultMergeMessage(errors) { const first = errors.at(0); if (!first) return { error: '', tooltip: '', }; if (errors.length === 1) { return { error: first, tooltip: '', }; } return { error: `${first}, +${errors.length} issues`, tooltip: errors.join('\n'), }; } function toTooltipFn(merge) { return (errors) => { const result = merge(errors); if (typeof result === 'string') { return { error: result, tooltip: '', merged: errors.join(INTERNAL_ERROR_MERGE_DELIM), }; } return result; }; } function mergeValidators(...validators) { if (!validators.length) return () => []; return (value) => validators.map((val) => val(value)).filter(Boolean); } function createMergeValidators(merge) { const mergeFn = merge ? toTooltipFn(merge) : defaultMergeMessage; return (validators) => { const validate = mergeValidators(...validators); const fn = ((value) => validate(value).join(INTERNAL_ERROR_MERGE_DELIM)); fn.resolve = (mergedError) => { return mergeFn(mergedError.split(INTERNAL_ERROR_MERGE_DELIM)); }; return fn; }; } function defaultMaxLengthMessageFactory$1(max, elementsLabel) { return `Max ${max} ${elementsLabel}`; } function createMaxLengthValidator$1(createMsg) { return (max, elementsLabel = 'items') => { const msg = createMsg(max, elementsLabel); return (value) => { const length = value?.length ?? 0; if (length > max) return msg; return ''; }; }; } function defaultMinLengthMessageFactory$1(min, elementsLabel) { return `Min ${min} ${elementsLabel}`; } function createMinLengthValidator$1(createMsg) { return (min, elementsLabel = 'items') => { const msg = createMsg(min, elementsLabel); return (value) => { const length = value?.length ?? 0; if (length < min) return msg; return ''; }; }; } const DEFAULT_MESSAGES$5 = { minLength: defaultMinLengthMessageFactory$1, maxLength: defaultMaxLengthMessageFactory$1, }; function createArrayValidators(factories, merger = createMergeValidators()) { const t = { ...DEFAULT_MESSAGES$5, ...factories }; const base = { minLength: createMinLengthValidator$1(t.minLength), maxLength: createMaxLengthValidator$1(t.maxLength), }; return { ...base, all: (opt) => { const validators = []; if (opt.minLength !== undefined) validators.push(base.minLength(opt.minLength, opt.elementsLabel)); if (opt.maxLength !== undefined) validators.push(base.maxLength(opt.maxLength, opt.elementsLabel)); return merger(validators); }, }; } function defaultMustBeTreFactory() { return `Must be true`; } function createMustBeTrueValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value !== true) return msg; return ''; }; }; } const DEFAULT_MESSAGES$4 = { mustBeTrue: defaultMustBeTreFactory, }; function createBooleanValidators(factories) { const t = { ...DEFAULT_MESSAGES$4, ...factories }; return { mustBeTrue: createMustBeTrueValidator(t.mustBeTrue), }; } function defaultMustBeMessageFactory(valueLabel) { return `Must be ${valueLabel}`; } function createMustBeValidator(createMessage) { return (value, valueLabel = `${value}`, matcher = Object.is) => { const msg = createMessage(valueLabel); return (currentValue) => { if (!matcher(value, currentValue)) return msg; return ''; }; }; } function defaultMustBeEmptyMessageFactory() { return defaultMustBeMessageFactory('empty'); } function createMustBeEmptyValidator(createMessage) { return () => createMustBeValidator(createMessage)(null); } function defaultNotMessageFactory(valueLabel) { return `Cannot be ${valueLabel}`; } function createNotValidator(createMessage) { return (value, valueLabel = `${value}`, matcher = Object.is) => { const msg = createMessage(valueLabel); return (currentValue) => { if (matcher(value, currentValue)) return msg; return ''; }; }; } function defaultNotOneOfMessageFactory(values) { return `Cannot be one of: ${values}`; } function defaultToLabel$1(value) { return `${value}`; } function createNotOneOfValidator(createMessage) { return (values, toLabel = defaultToLabel$1, identity = defaultToLabel$1, delimiter = ', ') => { const valuesLabel = values.map(toLabel).join(delimiter); const msg = createMessage(valuesLabel); const map = new Map(values.map((v) => [identity(v), v])); return (currentValue) => { if (map.has(identity(currentValue))) return msg; return ''; }; }; } function defaultOneOfMessageFactory(values) { return `Must be one of: ${values}`; } function defaultToLabel(value) { return `${value}`; } function createOneOfValidator(createMessage) { return (values, toLabel = defaultToLabel, identity = defaultToLabel, delimiter = ', ') => { const valuesLabel = values.map(toLabel).join(delimiter); const msg = createMessage(valuesLabel); const map = new Map(values.map((v) => [identity(v), v])); return (currentValue) => { if (!map.has(identity(currentValue))) return msg; return ''; }; }; } function defaultRequiredMessageFactory(label = 'Field') { return `${label} is required`; } function requiredNumber(value) { return value !== null && value !== undefined; } function createRequiredValidator(createMsg) { return (label = 'Field') => { const msg = createMsg(label); return (value) => { if (typeof value === 'number' && !requiredNumber(value)) return msg; if (!value) return msg; return ''; }; }; } const DEFAULT_MESSAGES$3 = { required: defaultRequiredMessageFactory, mustBe: defaultMustBeMessageFactory, mustBeNull: defaultMustBeEmptyMessageFactory, not: defaultNotMessageFactory, oneOf: defaultOneOfMessageFactory, notOneOf: defaultNotOneOfMessageFactory, }; function createGeneralValidators(factories) { const t = { ...DEFAULT_MESSAGES$3, ...factories }; const mustBeNull = createMustBeEmptyValidator(t.mustBeNull); return { required: createRequiredValidator(t.required), mustBe: createMustBeValidator(t.mustBe), mustBeNull: createMustBeEmptyValidator(t.mustBeNull), not: createNotValidator(t.not), oneOf: (values, toLabel, identity, delimiter) => { if (!values.length) return mustBeNull(); return createOneOfValidator(t.oneOf)(values, toLabel, identity, delimiter); }, notOneOf: createNotOneOfValidator(t.notOneOf), }; } function defaultIsDateMessageFactory() { return `Must be a valid date`; } function createIsDateValidator(createMsg, toDate) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; const date = toDate(value); if (isNaN(date.getTime())) return msg; return ''; }; }; } function defaultMaxDateMessageFactory(dateLabel) { return `Must be before ${dateLabel}`; } function createMaxDateValidator(createMsg, toDate, formatDate, locale) { return (date) => { const d = toDate(date); const matchTime = d.getTime(); const msg = createMsg(formatDate(d, locale)); return (value) => { if (value === null) return ''; if (toDate(value).getTime() > matchTime) return msg; return ''; }; }; } function defaultMinDateMessageFactory(dateLabel) { return `Must be after ${dateLabel}`; } function createMinDateValidator(createMsg, toDate, formatDate, locale) { return (date) => { const d = toDate(date); const matchTime = d.getTime(); const msg = createMsg(formatDate(d, locale)); return (value) => { if (value === null) return ''; if (toDate(value).getTime() < matchTime) return msg; return ''; }; }; } function isLuxonLike(date) { if (!date) return false; return ('toJSDate' in date && 'toUnixInteger' in date && typeof date.toJSDate === 'function' && typeof date.toUnixInteger === 'function'); } function isMomentLike(date) { if (!date) return false; return ('toDate' in date && 'unix' in date && typeof date.toDate === 'function' && typeof date.unix === 'function'); } function defaultToDate(date) { if (date instanceof Date) return date; if (typeof date === 'string' || typeof date === 'number') return new Date(date); if (!date) return new Date(); if (typeof date !== 'object') throw new Error('Date is not number, string, null, undefined or object'); if (isMomentLike(date)) return date.toDate(); if (isLuxonLike(date)) return date.toJSDate(); return new Date(); } function defaultFormatDate(date, locale, format = 'mediumDate') { return formatDate(date, format, locale); } const DEFAULT_MESSAGES$2 = { min: defaultMinDateMessageFactory, max: defaultMaxDateMessageFactory, isDate: defaultIsDateMessageFactory, }; function createDateValidators(factories, toDate = (defaultToDate), formatDate = defaultFormatDate, locale = 'en-US', generalValidators = createGeneralValidators(), merger = createMergeValidators()) { const t = { ...DEFAULT_MESSAGES$2, ...factories }; const base = { min: createMinDateValidator(t.min, toDate, formatDate, locale), max: createMaxDateValidator(t.max, toDate, formatDate, locale), isDate: createIsDateValidator(t.isDate, toDate), }; const toLabel = (d) => d ? formatDate(toDate(d), locale) : 'null'; return { ...base, all: (opt) => { const validators = []; if (opt.required) validators.push(generalValidators.required()); validators.push(base.isDate()); if (opt.mustBe !== undefined) { if (opt.mustBe === null) validators.push(generalValidators.mustBeNull()); else { const d = toDate(opt.mustBe); const formatted = formatDate(d, locale); validators.push(generalValidators.mustBe(d, formatted, (a, b) => { if (!a && !b) return true; if (!a || !b) return false; return toDate(a).getTime() === toDate(b).getTime(); })); } } if (opt.not !== undefined) { if (opt.not === null) validators.push(generalValidators.not(null)); else { const d = toDate(opt.not); const formatted = formatDate(d, locale); validators.push(generalValidators.not(d, formatted, (a, b) => { if (!a && !b) return true; if (!a || !b) return false; return toDate(a).getTime() === toDate(b).getTime(); })); } } if (opt.min !== undefined) validators.push(base.min(opt.min)); if (opt.max !== undefined) validators.push(base.max(opt.max)); if (opt.oneOf) { const dates = opt.oneOf.map((d) => (d ? toDate(d) : null)); validators.push(generalValidators.oneOf(dates, toLabel, (a) => a === null || a === undefined ? 'null' : toDate(a).getTime().toString())); } if (opt.notOneOf) { const dates = opt.notOneOf.map((d) => (d ? toDate(d) : null)); validators.push(generalValidators.notOneOf(dates, toLabel, (a) => a === null || a === undefined ? 'null' : toDate(a).getTime().toString())); } return merger(validators); }, }; } function defaultIntegerMessageFactory() { return `Must be an integer`; } function createIntegerValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (!Number.isInteger(value)) return msg; return ''; }; }; } function defaultIsNumberMessageFactory() { return `Must be a number`; } function createIsNumberValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (typeof value !== 'number' || isNaN(value)) return msg; return ''; }; }; } function defaultMaxMessageFactory(max) { return `Must be at most ${max}`; } function createMaxValidator(createMsg) { return (max) => { const msg = createMsg(max); return (value) => { if (value === null) return ''; if (value > max) return msg; return ''; }; }; } function defaultMinMessageFactory(min) { return `Must be at least ${min}`; } function createMinValidator(createMsg) { return (min) => { const msg = createMsg(min); return (value) => { if (value === null) return ''; if (value < min) return msg; return ''; }; }; } function defaultMultipleOfMessageFactory(multipleOf) { return `Must be a multiple of ${multipleOf}`; } function createMultipleOfValidator(createMsg) { return (multipleOf) => { const msg = createMsg(multipleOf); return (value) => { if (value === null) return ''; if (value % multipleOf !== 0) return msg; return ''; }; }; } const DEFAULT_MESSAGES$1 = { min: defaultMinMessageFactory, max: defaultMaxMessageFactory, isNumber: defaultIsNumberMessageFactory, multipleOf: defaultMultipleOfMessageFactory, integer: defaultIntegerMessageFactory, }; function createNumberValidators(factories, generalValidators = createGeneralValidators(), merger = createMergeValidators()) { const t = { ...DEFAULT_MESSAGES$1, ...factories }; const base = { min: createMinValidator(t.min), max: createMaxValidator(t.max), isNumber: createIsNumberValidator(t.isNumber), multipleOf: createMultipleOfValidator(t.multipleOf), integer: createIntegerValidator(t.integer), }; return { ...base, all: (opt) => { const validators = []; if (opt.required) validators.push(generalValidators.required()); validators.push(base.isNumber()); if (opt.mustBe !== undefined) validators.push(generalValidators.mustBe(opt.mustBe)); if (opt.not !== undefined) validators.push(generalValidators.not(opt.not)); if (opt.integer) validators.push(base.integer()); if (opt.min !== undefined) validators.push(base.min(opt.min)); if (opt.max !== undefined) validators.push(base.max(opt.max)); if (opt.multipleOf !== undefined) validators.push(base.multipleOf(opt.multipleOf)); if (opt.oneOf) validators.push(generalValidators.oneOf(opt.oneOf)); if (opt.notOneOf) validators.push(generalValidators.notOneOf(opt.notOneOf)); return merger(validators); }, }; } const EMAIL_REGEXP = /^(?=.{1,254}$)(?=.{1,64}@)[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/; function defaultEmailMessageFactory() { return `Must be a valid email`; } function createEmailValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (!EMAIL_REGEXP.test(value)) return msg; return ''; }; }; } function defaultIsStringMessageFactory() { return `Must be a string`; } function createIsStringValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (typeof value !== 'string') return msg; return ''; }; }; } function defaultMaxLengthMessageFactory(max) { return defaultMaxLengthMessageFactory$1(max, 'characters'); } function createMaxLengthValidator(createMsg) { return createMaxLengthValidator$1((max) => createMsg(max)); } function defaultMinLengthMessageFactory(min) { return defaultMinLengthMessageFactory$1(min, 'characters'); } function createMinLengthValidator(createMsg) { return createMinLengthValidator$1((min) => createMsg(min)); } function defaultPatternMessageFactory(patternLabel) { return `Must match pattern ${patternLabel}`; } function createPatternValidator(createMsg) { return (pattern) => { const regex = new RegExp(pattern); const msg = createMsg(regex.source); return (value) => { if (value === null) return ''; if (!regex.test(value)) return msg; return ''; }; }; } function defaultTrimmedMessageFactory() { return `Cannot contain leading or trailing whitespace`; } function createTrimmedValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (value.trim().length !== value.length) return msg; return ''; }; }; } const URI_REGEXP = /^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$/; function defaultURIMessageFactory() { return `Must be a valid URI`; } function createURIValidator(createMsg) { return () => { const msg = createMsg(); return (value) => { if (value === null) return ''; if (!URI_REGEXP.test(value)) return msg; return ''; }; }; } const DEFAULT_MESSAGES = { email: defaultEmailMessageFactory, uri: defaultURIMessageFactory, pattern: defaultPatternMessageFactory, trimmed: defaultTrimmedMessageFactory, isString: defaultIsStringMessageFactory, minLength: defaultMinLengthMessageFactory, maxLength: defaultMaxLengthMessageFactory, }; function createStringValidators(factories, generalValidators = createGeneralValidators(), merger = createMergeValidators()) { const t = { ...DEFAULT_MESSAGES, ...factories }; const base = { email: createEmailValidator(t.email), uri: createURIValidator(t.uri), pattern: createPatternValidator(t.pattern), trimmed: createTrimmedValidator(t.trimmed), isString: createIsStringValidator(t.isString), minLength: createMinLengthValidator(t.minLength), maxLength: createMaxLengthValidator(t.maxLength), }; return { ...base, all: (opt) => { const validators = []; if (opt.required) validators.push(generalValidators.required()); validators.push(base.isString()); if (opt.mustBe !== undefined) validators.push(generalValidators.mustBe(opt.mustBe)); if (opt.not !== undefined) validators.push(generalValidators.not(opt.not)); if (opt.trimmed) validators.push(base.trimmed()); if (opt.oneOf) validators.push(generalValidators.oneOf(opt.oneOf)); if (opt.notOneOf) validators.push(generalValidators.notOneOf(opt.notOneOf)); if (opt.minLength !== undefined) validators.push(base.minLength(opt.minLength)); if (opt.maxLength) validators.push(base.maxLength(opt.maxLength)); if (opt.pattern) { switch (opt.pattern) { case 'email': validators.push(base.email()); break; case 'uri': validators.push(base.uri()); break; default: validators.push(base.pattern(opt.pattern)); break; } } return merger(validators); }, }; } const token = new InjectionToken('INTERNAL_MMSTACK_VALIDATORS'); let defaultValidators = null; function createDefaultValidators() { if (!defaultValidators) { defaultValidators = createValidators({}, defaultToDate, defaultFormatDate, 'en-US'); } return defaultValidators; } function createValidators(msg, toDate, formatDate, locale) { const general = createGeneralValidators(msg?.general); const merger = createMergeValidators(msg?.merge); return { general, string: createStringValidators(msg?.string, general, merger), number: createNumberValidators(msg?.number, general, merger), date: createDateValidators(msg?.date, toDate, formatDate, locale, general, merger), array: createArrayValidators(msg?.array, merger), boolean: createBooleanValidators(msg?.boolean), }; } function provideValidatorConfig(factory, toDate = (defaultToDate), formatDate = defaultFormatDate) { return { provide: token, useFactory: (locale) => createValidators(factory(locale), toDate, formatDate, locale), deps: [LOCALE_ID], }; } function injectValidators(injector) { const validators = injector ? injector.get(token, null, { optional: true, }) : inject(token, { optional: true }); return validators ?? createDefaultValidators(); } /** * Generated bundle index. Do not edit. */ export { injectValidators, provideValidatorConfig }; //# sourceMappingURL=mmstack-form-validation.mjs.map