sanity
Version:
Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches
109 lines (85 loc) • 3.01 kB
text/typescript
import {
isArrayOfBlocksSchemaType,
type Path,
type PathSegment,
type Validators,
} from '@sanity/types'
import {deepEquals} from '../util/deepEquals'
import {genericValidators} from './genericValidator'
export const arrayValidators: Validators = {
...genericValidators,
min: (minLength, value, message, {i18n, type}) => {
if (!value || value.length >= minLength) {
return true
}
const context = isArrayOfBlocksSchemaType(type) ? 'blocks' : undefined
return message || i18n.t('validation:array.minimum-length', {minLength, context})
},
max: (maxLength, value, message, {i18n, type}) => {
if (!value || value.length <= maxLength) {
return true
}
const context = isArrayOfBlocksSchemaType(type) ? 'blocks' : undefined
return message || i18n.t('validation:array.maximum-length', {maxLength, context})
},
length: (wantedLength, value, message, {i18n, type}) => {
if (!value || value.length === wantedLength) {
return true
}
const context = isArrayOfBlocksSchemaType(type) ? 'blocks' : undefined
return message || i18n.t('validation:array.exact-length', {wantedLength, context})
},
presence: (flag, value, message, {i18n}) => {
if (flag === 'required' && !value) {
return message || i18n.t('validation:generic.required', {context: 'array'})
}
return true
},
valid: (allowedValues, values, message, {i18n}) => {
const valueType = typeof values
if (valueType === 'undefined') {
return true
}
const paths: Path[] = []
for (let i = 0; i < values.length; i++) {
const value = values[i]
if (allowedValues.some((expected) => deepEquals(expected, value))) {
continue
}
const pathSegment: PathSegment = value && value._key ? {_key: value._key} : i
paths.push([pathSegment])
}
// we emit the same message for each path we find in this array
const sharedMessage = message || i18n.t('validation:generic.not-allowed')
return paths.map((path) => ({message: sharedMessage, path}))
},
unique: (_unused, value, message, {i18n}) => {
const dupeIndices = []
if (!value) {
return true
}
for (let x = 0; x < value.length; x++) {
for (let y = x + 1; y < value.length; y++) {
const itemA = value[x]
const itemB = value[y]
if (!deepEquals(itemA, itemB)) {
continue
}
if (dupeIndices.indexOf(x) === -1) {
dupeIndices.push(x)
}
if (dupeIndices.indexOf(y) === -1) {
dupeIndices.push(y)
}
}
}
const paths = dupeIndices.map((idx) => {
const item = value[idx]
const pathSegment = item && item._key ? {_key: item._key} : idx
return [pathSegment]
})
// we emit the same message for each path we find in this array
const sharedMessage = message || i18n.t('validation:array.item-duplicate')
return paths.map((path) => ({message: sharedMessage, path}))
},
}