import { z } from "zod/mini" import { NonEmptyStringSchema } from "../../common/nonEmptyString" export const LinkKind = { Image: "image", File: "file", Media: "media", Document: "document", External: "web", Any: "any", } as const export const LinkType = { Image: "ImageLink", File: "FileLink", Document: "DocumentLink", External: "ExternalLink", Media: "MediaLink", Any: "AnyLink", } as const // Image export const FilledImageLinkLegacyValueSchema = z.object({ // `kind` was normalized to the literal (legacy behavior) kind: z.pipe( z.string(), z.transform(() => LinkKind.Image), ), id: z.string(), url: z.string(), height: z.string(), width: z.string(), size: z.string(), name: z.string(), date: z.nullish(z.string()), }) export const EmptyImageLinkLegacyValueSchema = z.strictObject({ kind: z.literal(LinkKind.Image), }) // File export const FilledFileLinkLegacyValueSchema = z.object({ // `kind` was normalized to the literal (legacy behavior) kind: z.pipe( z.string(), z.transform(() => LinkKind.File), ), id: z.string(), url: z.string(), name: z.string(), size: z.transform((value) => (typeof value === "string" ? value : "0")), date: z.nullish(z.string()), }) export const EmptyFileLinkLegacyValueSchema = z.strictObject({ kind: z.literal(LinkKind.File), }) // Media // Media link only represent the empty state as they turn into // file or image links when they filled. export const EmptyMediaLinkLegacyValueSchema = z.strictObject({ kind: z.literal(LinkKind.Media), }) // Document export const FilledDocumentLinkLegacyValueSchema = z.object({ // `kind` is not part of the filled schema... (legacy behavior) // We transform it to the literal for consistency. kind: z.pipe( z.optional(z.literal(LinkKind.Document)), z.transform(() => LinkKind.Document), ), id: NonEmptyStringSchema, }) export const EmptyDocumentLinkLegacyValueSchema = z.strictObject({ kind: z.literal(LinkKind.Document), }) // External export const FilledExternalLinkLegacyValueSchema = z.object({ // It's odd that `kind` is optional here... (legacy behavior) // We transform it to the literal for consistency. kind: z.pipe( z.optional(z.literal(LinkKind.External)), z.transform(() => LinkKind.External), ), url: z.string(), target: z.nullish(z.string()), preview: z.nullish( z.strictObject({ title: z.optional(z.string()), }), ), }) export const EmptyExternalLinkLegacyValueSchema = z.strictObject({ kind: z.literal(LinkKind.External), }) // Any export const AnyLinkLegacySchema = z.strictObject({ kind: z.transform(() => LinkKind.Any), }) // All export const FilledLinkLegacyValueSchema: z.ZodMiniType = z.union([ FilledImageLinkLegacyValueSchema, FilledFileLinkLegacyValueSchema, FilledDocumentLinkLegacyValueSchema, FilledExternalLinkLegacyValueSchema, ]) type FilledImageLinkLegacyValue = z.infer type FilledFileLinkLegacyValue = z.infer type FilledDocumentLinkLegacyValue = z.infer type FilledExternalLinkLegacyValue = z.infer export type FilledLinkLegacyValue = | FilledImageLinkLegacyValue | FilledFileLinkLegacyValue | FilledDocumentLinkLegacyValue | FilledExternalLinkLegacyValue const EmptyLinkLegacyValueSchema: z.ZodMiniType = z.union([ EmptyImageLinkLegacyValueSchema, EmptyFileLinkLegacyValueSchema, EmptyMediaLinkLegacyValueSchema, EmptyDocumentLinkLegacyValueSchema, EmptyExternalLinkLegacyValueSchema, AnyLinkLegacySchema, ]) type EmptyImageLinkLegacyValue = z.infer type EmptyFileLinkLegacyValue = z.infer type EmptyMediaLinkLegacyValue = z.infer type EmptyDocumentLinkLegacyValue = z.infer type EmptyExternalLinkLegacyValue = z.infer type AnyLinkLegacy = z.infer export type EmptyLinkLegacyValue = | EmptyImageLinkLegacyValue | EmptyFileLinkLegacyValue | EmptyMediaLinkLegacyValue | EmptyDocumentLinkLegacyValue | EmptyExternalLinkLegacyValue | AnyLinkLegacy const LinkLegacyValueSchema: z.ZodMiniType = z.union([ FilledLinkLegacyValueSchema, EmptyLinkLegacyValueSchema, ]) export type LinkLegacyValue = FilledLinkLegacyValue | EmptyLinkLegacyValue // We cannot use z.extend here because we're working with unions. // z.intersection wouldn't work also for the same reason. export const LinkLegacySchema = z.pipe( z.pipe( z.pipe( z.record(z.string(), z.unknown()), z.transform( ({ key, text, variant, ...value }): { key?: unknown text?: unknown variant?: unknown value: Record } => ({ key, text, variant, value }), ), ), z.object({ key: z.optional(z.string()), text: z.optional(z.string()), variant: z.optional(z.string()), value: LinkLegacyValueSchema as z.ZodMiniType>, }), ), z.transform(({ key, text, variant, value }) => ({ key: z.uuidv4().safeParse(key).data ?? crypto.randomUUID(), ...(text ? { text } : {}), ...(variant ? { variant } : {}), ...value, })), ) export type LinkLegacy = z.infer