UNPKG

@prismicio/types-internal

Version:
314 lines (288 loc) 8.04 kB
import { either } from "fp-ts/lib/Either" import * as t from "io-ts" import { withFallback } from "io-ts-types/lib/withFallback" import { StringOrNull } from "../../../validators" const arrayString = ( entries: | string | string[] | { [x: string]: { name: string }[] } | undefined, ) => { if (entries instanceof Array) { const isValidEntries = entries.reduce( (acc, l) => acc && typeof l === "string", true, ) if (isValidEntries) return t.success(entries) } return } const plainString = ( entries: | string | string[] | { [x: string]: { name: string }[] } | undefined, ) => { if (typeof entries === "string") { return t.success([entries]) } return } const MasksArrayString = new t.Type<ReadonlyArray<string>, object, unknown>( "MasksArrayString", (u: unknown): u is ReadonlyArray<string> => { return u instanceof Array }, (u: unknown, context: t.Context) => { return either.chain( t.union([t.array(t.string), t.string]).validate(u, context), (masks) => { return arrayString(masks) || plainString(masks) || t.failure(u, context) }, ) }, (res) => res, ) // Field selection in content relationship fields doesn't support nested groups. const CustomTypeLevel2FieldGroupFields = new t.Type< readonly string[], readonly string[] >( "CustomTypeLevel2FieldGroupFields", (u: unknown): u is readonly string[] => Array.isArray(u) && u.every((item) => typeof item === "string"), (u: unknown, context: t.Context) => either.chain(t.array(t.string).validate(u, context), (fields) => { // duplicates not allowed const filtered = new Set(fields) return filtered.size === fields.length ? t.success(fields) : t.failure(u, context, "Fields have duplicates.") }), (a) => a, ) const CustomTypeLevel2Field = t.union([ t.strict({ id: t.string, fields: CustomTypeLevel2FieldGroupFields, }), t.string, ]) const CustomTypeLevel2Fields = new t.Type< readonly t.TypeOf<typeof CustomTypeLevel2Field>[], readonly t.OutputOf<typeof CustomTypeLevel2Field>[] >( "CustomTypeLevel2Fields", (u: unknown): u is readonly t.TypeOf<typeof CustomTypeLevel2Field>[] => Array.isArray(u) && u.every((item) => CustomTypeLevel2Field.is(item)), (u: unknown, context: t.Context) => either.chain( t.array(CustomTypeLevel2Field).validate(u, context), (fields) => { // duplicates not allowed const filtered = new Set( fields.map((field) => (typeof field === "string" ? field : field.id)), ) return filtered.size === fields.length ? t.success(fields) : t.failure(u, context, "Fields have duplicates.") }, ), (a) => a, ) const CustomTypeLevel2 = t.union([ t.string, t.strict({ id: t.string, fields: CustomTypeLevel2Fields, }), ]) const CustomTypesLevel2 = new t.Type< readonly t.TypeOf<typeof CustomTypeLevel2>[], readonly t.OutputOf<typeof CustomTypeLevel2>[] >( "CustomTypesLevel2", (u: unknown): u is readonly t.TypeOf<typeof CustomTypeLevel2>[] => Array.isArray(u) && u.every((item) => CustomTypeLevel2.is(item)), (u: unknown, context: t.Context) => either.chain(t.array(CustomTypeLevel2).validate(u, context), (cts) => { // duplicates not allowed const filtered = new Set( cts.map((ct) => (typeof ct === "string" ? ct : ct.id)), ) return filtered.size === cts.length ? t.success(cts) : t.failure(u, context, "Custom types have duplicates.") }), (a) => a, ) const CustomTypeLevel1FieldCustomTypes = t.strict({ id: t.string, customtypes: CustomTypesLevel2, }) const CustomTypeLevel1FieldGroupField = t.union([ CustomTypeLevel1FieldCustomTypes, t.string, ]) const CustomTypeLevel1FieldGroupFields = new t.Type< readonly t.TypeOf<typeof CustomTypeLevel1FieldGroupField>[], readonly t.OutputOf<typeof CustomTypeLevel1FieldGroupField>[] >( "CustomTypeLevel1FieldGroupFields", ( u: unknown, ): u is readonly t.TypeOf<typeof CustomTypeLevel1FieldGroupField>[] => Array.isArray(u) && u.every((item) => CustomTypeLevel1FieldGroupField.is(item)), (u: unknown, context: t.Context) => either.chain( t.array(CustomTypeLevel1FieldGroupField).validate(u, context), (fields) => { // duplicates not allowed const filtered = new Set( fields.map((field) => (typeof field === "string" ? field : field.id)), ) return filtered.size === fields.length ? t.success(fields) : t.failure(u, context, "Fields have duplicates.") }, ), (a) => a, ) const CustomTypeLevel1Field = t.union([ t.strict({ id: t.string, fields: CustomTypeLevel1FieldGroupFields, }), CustomTypeLevel1FieldCustomTypes, t.string, ]) const CustomTypeLevel1Fields = new t.Type< readonly t.TypeOf<typeof CustomTypeLevel1Field>[], readonly t.OutputOf<typeof CustomTypeLevel1Field>[] >( "CustomTypeLevel1Fields", (u: unknown): u is readonly t.TypeOf<typeof CustomTypeLevel1Field>[] => Array.isArray(u) && u.every((item) => CustomTypeLevel1Field.is(item)), (u: unknown, context: t.Context) => either.chain( t.array(CustomTypeLevel1Field).validate(u, context), (fields) => { // duplicates not allowed const filtered = new Set( fields.map((field) => (typeof field === "string" ? field : field.id)), ) return filtered.size === fields.length ? t.success(fields) : t.failure(u, context, "Fields have duplicates.") }, ), (a) => a, ) const CustomTypeLevel1 = t.union([ t.string, t.strict({ id: t.string, fields: CustomTypeLevel1Fields, }), ]) export const CustomTypes = new t.Type< readonly t.TypeOf<typeof CustomTypeLevel1>[], readonly t.OutputOf<typeof CustomTypeLevel1>[] >( "CustomTypes", (u: unknown): u is readonly t.TypeOf<typeof CustomTypeLevel1>[] => Array.isArray(u) && u.every((item) => CustomTypeLevel1.is(item)), (u: unknown, context: t.Context) => either.chain(t.array(CustomTypeLevel1).validate(u, context), (cts) => { // if a ct appears more than once as a string, we allow it (legacy) // if a ct appears once as a string and then again via object (or vice versa), we don't allow it // if a ct appears more than once as an object, we don't allow it const strings = new Set<string>() const objects = new Set<string>() for (const ct of cts) { let failed = false if (typeof ct === "string") { failed = objects.has(ct) strings.add(ct) } else { const { id } = ct failed = strings.has(id) || objects.has(id) objects.add(id) } if (failed) return t.failure(u, context, "Custom types have duplicates.") } if (objects.size > 1) { return t.failure( u, context, "Cannot have multiple custom types with fields selection.", ) } if (strings.size > 0 && objects.size > 0) { return t.failure( u, context, "Cannot mix custom types as strings and objects with fields.", ) } return t.success(cts) }), (a) => a, ) export const LinkFieldType = "Link" export const LinkConfig = t.exact( t.partial({ label: StringOrNull, useAsTitle: t.boolean, placeholder: t.string, select: withFallback( t.union([ t.literal("media"), t.literal("document"), t.literal("web"), t.null, ]), null, ), customtypes: CustomTypes, masks: MasksArrayString, // legacy definition of the custom types allowed tags: MasksArrayString, allowTargetBlank: t.boolean, allowText: t.boolean, /** * `repeat` property is used to allow multiple links to be added. * `undefined` means that the field is not repeatable (hence repeat = false). */ repeat: t.boolean, /** * `variants` allows an option to be picked from a list (e.g. "primary"). To * be considered, the list must have at least one item. */ variants: t.array(t.string), }), ) export type LinkConfig = t.TypeOf<typeof LinkConfig> export const Link = t.exact( t.intersection([ t.type({ type: t.literal(LinkFieldType), }), t.partial({ fieldset: StringOrNull, config: LinkConfig, }), ]), ) export type Link = t.TypeOf<typeof Link>