UNPKG

@sanity/types

Version:

Type definitions for common Sanity data structures

429 lines (398 loc) 11.7 kB
import {type SanityClient} from '@sanity/client' import {type SanityDocument} from '../documents' import {type ValidationMarker} from '../markers' import {type Path} from '../paths' import { type Schema, type SchemaType, type SchemaValidationValue, type SlugSchemaType, } from '../schema' import {type SlugParent} from '../slug' /** @public */ export type RuleTypeConstraint = 'Array' | 'Boolean' | 'Date' | 'Number' | 'Object' | 'String' /** @public */ export type FieldRules = {[fieldKey: string]: SchemaValidationValue} /** * Holds localized validation messages for a given field. * * @example Custom message for English (US) and Norwegian (Bokmål): * ``` * { * 'en-US': 'Needs to start with a capital letter', * 'no-NB': 'Må starte med stor bokstav', * } * ``` * @public */ export interface LocalizedValidationMessages { [locale: string]: string } /** * Note: `RuleClass` and `Rule` are split to fit the current `@sanity/types` * setup. Classes are a bit weird in the `@sanity/types` package because classes * create an actual javascript class while simultaneously creating a type * definition. * * This implicitly creates two types: * 1. the instance type — `Rule` and * 2. the static/class type - `RuleClass` * * The `RuleClass` type contains the static methods and the `Rule` instance * contains the instance methods. Downstream in the validation package, the Rule * implementation asserts the class declaration is of this type. * * @internal */ export interface RuleClass { FIELD_REF: symbol array: (def?: SchemaType) => Rule object: (def?: SchemaType) => Rule string: (def?: SchemaType) => Rule number: (def?: SchemaType) => Rule boolean: (def?: SchemaType) => Rule dateTime: (def?: SchemaType) => Rule valueOfField: Rule['valueOfField'] new (typeDef?: SchemaType): Rule } /** * Holds a reference to a different field * NOTE: Only use this through {@link Rule.valueOfField} * * @public */ export interface FieldReference { type: symbol path: string | string[] } /** @public */ export interface UriValidationOptions { scheme?: (string | RegExp) | Array<string | RegExp> allowRelative?: boolean relativeOnly?: boolean allowCredentials?: boolean } /** @public */ export interface Rule { // NOTE: these prop is not actually deprecated but there is better TS Doc // support for `@deprecated` than `@internal` /** * @internal * @deprecated internal use only */ _type: RuleTypeConstraint | undefined /** * @internal * @deprecated internal use only */ _level: 'error' | 'warning' | 'info' | undefined /** * @internal * @deprecated internal use only */ _required: 'required' | 'optional' | undefined /** * @internal * @deprecated internal use only */ _typeDef: SchemaType | undefined /** * @internal * @deprecated internal use only */ _message: string | LocalizedValidationMessages | undefined /** * @internal * @deprecated internal use only */ _rules: RuleSpec[] /** * @internal * @deprecated internal use only */ _fieldRules: FieldRules | undefined /** * Takes in a path and returns an object with a symbol. * * When the validation lib sees this symbol, it will use the provided path to * get a value from the current field's parent and use that value as the input * to the Rule. * * The path that's given is forwarded to `lodash/get` * * ```js * fields: [ * // ... * { * // ... * name: 'highestTemperature', * type: 'number', * validation: (Rule) => Rule.positive().min(Rule.valueOfField('lowestTemperature')), * // ... * }, * ] * ``` */ valueOfField: (path: string | string[]) => FieldReference error(message?: string | LocalizedValidationMessages): Rule warning(message?: string | LocalizedValidationMessages): Rule info(message?: string | LocalizedValidationMessages): Rule reset(): this isRequired(): boolean clone(): Rule cloneWithRules(rules: RuleSpec[]): Rule merge(rule: Rule): Rule type(targetType: RuleTypeConstraint | Lowercase<RuleTypeConstraint>): Rule all(children: Rule[]): Rule either(children: Rule[]): Rule optional(): Rule required(): Rule custom<T = unknown>(fn: CustomValidator<T>, options?: {bypassConcurrencyLimit?: boolean}): Rule min(len: number | string | FieldReference): Rule max(len: number | string | FieldReference): Rule length(len: number | FieldReference): Rule valid(value: unknown | unknown[]): Rule integer(): Rule precision(limit: number | FieldReference): Rule positive(): Rule negative(): Rule greaterThan(num: number | FieldReference): Rule lessThan(num: number | FieldReference): Rule uppercase(): Rule lowercase(): Rule regex(pattern: RegExp, name: string, options: {name?: string; invert?: boolean}): Rule regex(pattern: RegExp, options: {name?: string; invert?: boolean}): Rule regex(pattern: RegExp, name: string): Rule regex(pattern: RegExp): Rule email(): Rule uri(options?: UriValidationOptions): Rule unique(): Rule reference(): Rule fields(rules: FieldRules): Rule assetRequired(): Rule validate( value: unknown, options: ValidationContext & { /** * @deprecated Internal use only * @internal */ __internal?: { customValidationConcurrencyLimiter?: { ready: () => Promise<void> release: () => void } } }, ): Promise<ValidationMarker[]> } /** @public */ export type RuleSpec = | {flag: 'integer'} | {flag: 'email'} | {flag: 'unique'} | {flag: 'reference'} | {flag: 'type'; constraint: RuleTypeConstraint} | {flag: 'all'; constraint: Rule[]} | {flag: 'either'; constraint: Rule[]} | {flag: 'presence'; constraint: 'optional' | 'required'} | {flag: 'custom'; constraint: CustomValidator} | {flag: 'min'; constraint: number | string | FieldReference} | {flag: 'max'; constraint: number | string | FieldReference} | {flag: 'length'; constraint: number | FieldReference} | {flag: 'valid'; constraint: unknown[]} | {flag: 'precision'; constraint: number | FieldReference} | {flag: 'lessThan'; constraint: number | FieldReference} | {flag: 'greaterThan'; constraint: number | FieldReference} | {flag: 'stringCasing'; constraint: 'uppercase' | 'lowercase'} | {flag: 'assetRequired'; constraint: {assetType: 'asset' | 'image' | 'file'}} | { flag: 'regex' constraint: { pattern: RegExp name?: string invert: boolean } } | { flag: 'uri' constraint: { options: { scheme: RegExp[] allowRelative: boolean relativeOnly: boolean allowCredentials: boolean } } } /** * this is used to get allow index access (e.g. `RuleSpec['constraint']`) to * constraint when a rule spec might not have a `constraint` prop * * @internal */ export type ConditionalIndexAccess<T, U> = U extends keyof T ? T[U] : undefined /** @internal */ export type RuleSpecConstraint<T extends RuleSpec['flag']> = ConditionalIndexAccess< Extract<RuleSpec, {flag: T}>, 'constraint' > /** * A context object passed around during validation. This includes the * `Rule.custom` context. * * e.g. * * ```js * Rule.custom((_, validationContext) => { * // ... * })` * ``` * * @public */ export interface ValidationContext { getClient: (options: {apiVersion: string}) => SanityClient schema: Schema parent?: unknown type?: SchemaType document?: SanityDocument path?: Path getDocumentExists?: (options: {id: string}) => Promise<boolean> environment: 'cli' | 'studio' } /** * The base type for all validators in the validation library. Takes in a * `RuleSpec`'s constraint, the value to check, an optional override message, * and the validation context. * * @see Rule.validate from `sanity/src/core/validation/Rule` * * @internal */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export type Validator<T = any, Value = any> = ( constraint: T, value: Value, message: string | undefined, context: ValidationContext, ) => | ValidationError[] | ValidationError | string | true | Promise<ValidationError[] | ValidationError | string | true> /** * A type helper used to define a group of validators. The type of the * `RuleSpec` constraint is inferred via the key. * * E.g. * * ```ts * const booleanValidators: Validators = { * ...genericValidator, * * presence: (v, value, message) => { * if (v === 'required' && typeof value !== 'boolean') return message || 'Required' * return true * }, * } * ``` * * @internal */ export type Validators = Partial<{ [P in RuleSpec['flag']]: Validator< Exclude<ConditionalIndexAccess<Extract<RuleSpec, {flag: P}>, 'constraint'>, FieldReference> > }> /** @internal */ export interface ValidationErrorOptions { paths?: Path[] children?: ValidationMarker[] operation?: 'AND' | 'OR' } /** * This follows the same pattern as `RuleClass` and `Rule` above * Note: this class does not actually extend `Error` since it's never thrown * within the validation library * * @deprecated It is preferred to a plain object that adheres to `ValidationError` * @internal */ export interface ValidationErrorClass { new (message: string, options?: ValidationErrorOptions): ValidationError } /** * The shape that can be returned from a custom validator to be converted into * a validation marker by the validation logic. Inside of a custom validator, * you can return an array of these in order to specify multiple paths within * an object or array. * * @public */ export interface ValidationError { /** * The message describing why the value is not valid. This message will be * included in the validation markers after validation has finished running. */ message: string /** * If writing a custom validator, you can return validation messages to * specific path inside of the current value (object or array) by populating * this `path` prop. * * NOTE: This path is relative to the current value and _not_ relative to * the document. */ path?: Path /** * Same as `path` but allows more than one value. If provided, the same * message will create two markers from each path with the same message * provided. * * @deprecated prefer `path` */ paths?: Path[] /** * @deprecated Unused. Was used to store the results from `.either()` /`.all()` */ children?: ValidationMarker[] /** * @deprecated Unused. Was used to signal if this error came from an `.either()`/`.all()`. */ operation?: 'AND' | 'OR' /** * @deprecated Unused. Was relevant when validation error was used as a class. */ cloneWithMessage?(message: string): ValidationError } /** @public */ export type CustomValidatorResult = | true | string | ValidationError | ValidationError[] | LocalizedValidationMessages /** @public */ export interface CustomValidator<T = unknown> { (value: T, context: ValidationContext): CustomValidatorResult | Promise<CustomValidatorResult> bypassConcurrencyLimit?: boolean } /** @public */ export interface SlugValidationContext extends ValidationContext { parent: SlugParent type: SlugSchemaType defaultIsUnique: SlugIsUniqueValidator } /** @public */ export type SlugIsUniqueValidator = ( slug: string, context: SlugValidationContext, ) => boolean | Promise<boolean> /** @public */ export interface FormNodeValidation { level: 'error' | 'warning' | 'info' message: string path: Path }