55 lines
1.6 KiB
TypeScript
55 lines
1.6 KiB
TypeScript
import { z } from "zod"
|
|
|
|
import type {
|
|
DiscriminatedUnionError,
|
|
Option,
|
|
} from "@/types/discriminatedUnion"
|
|
|
|
/**
|
|
* This file is created to handle our discriminated unions
|
|
* validations primarily for union returns from Contentstacks
|
|
* GraphQL server.
|
|
*
|
|
* In the case of a new block being added to the union in Contenstack,
|
|
* Zod will throw because that typename is not expected ("invalid_union_discriminator").
|
|
* Therefore we add a safety that we return everytime we stumble upon
|
|
* the issue and then we filter it out in the transform.
|
|
*
|
|
* This replaces the `cleanEmptyObjects` function that would require
|
|
* everyone to never make a mistake in adding __typename to root
|
|
* anywhere (or any other potentially global fields in case the return type
|
|
* is an Interface e.g).
|
|
*/
|
|
|
|
export function discriminatedUnion<R>(options: Option[]) {
|
|
return z
|
|
.discriminatedUnion("__typename", [
|
|
z.object({ __typename: z.literal(undefined) }),
|
|
...options,
|
|
])
|
|
.catch(({ error }: DiscriminatedUnionError) => {
|
|
if (
|
|
error.issues.find(
|
|
(issue) => issue.code === "invalid_union_discriminator"
|
|
)
|
|
) {
|
|
return { __typename: undefined }
|
|
}
|
|
throw new Error(error.message)
|
|
})
|
|
.transform((data) => {
|
|
if (data.__typename === "undefined" || data.__typename === undefined) {
|
|
return null
|
|
}
|
|
return data as R
|
|
})
|
|
}
|
|
|
|
export function discriminatedUnionArray<T extends Option>(options: T[]) {
|
|
return z
|
|
.array(discriminatedUnion(options))
|
|
.transform((blocks) =>
|
|
blocks.filter((block) => !!block)
|
|
) as unknown as z.ZodEffects<z.ZodArray<T>>
|
|
}
|