From 0c4cb2e2b0b4fdb97b04d3fdb5ab3dd5708b9863 Mon Sep 17 00:00:00 2001 From: zomars Date: Fri, 7 Oct 2022 14:48:41 -0600 Subject: [PATCH] Added denullish utils for zod Needed for API refactoring --- packages/prisma/zod-utils.ts | 94 +++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 2 deletions(-) diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index 370c5e5a61..d8bf647375 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -1,6 +1,15 @@ -import { z } from "zod"; +import z, { ZodNullable, ZodObject, ZodOptional } from "zod"; + +/* eslint-disable no-underscore-dangle */ +import type { + objectInputType, + objectOutputType, + ZodNullableDef, + ZodOptionalDef, + ZodRawShape, + ZodTypeAny, +} from "zod"; -import { LocationType } from "@calcom/app-store/locations"; import dayjs from "@calcom/dayjs"; import { slugify } from "@calcom/lib/slugify"; @@ -147,3 +156,84 @@ export const successRedirectUrl = z .regex(/^http(s)?:\/\/.*/), ]) .optional(); + +export type ZodDenullish = T extends ZodNullable | ZodOptional + ? ZodDenullish + : T; + +export type ZodDenullishShape = { + [k in keyof T]: ZodDenullish; +}; + +export const denullish = (schema: T): ZodDenullish => + (schema instanceof ZodNullable || schema instanceof ZodOptional + ? denullish((schema._def as ZodNullableDef | ZodOptionalDef).innerType) + : schema) as ZodDenullish; + +type UnknownKeysParam = "passthrough" | "strict" | "strip"; + +/** + * @see https://github.com/3x071c/lsg-remix/blob/e2a9592ba3ec5103556f2cf307c32f08aeaee32d/app/lib/util/zod.ts + */ +export function denullishShape< + T extends ZodRawShape, + UnknownKeys extends UnknownKeysParam = "strip", + Catchall extends ZodTypeAny = ZodTypeAny, + Output = objectOutputType, + Input = objectInputType +>( + obj: ZodObject +): ZodObject, UnknownKeys, Catchall> { + const a = entries(obj.shape).map(([field, schema]) => [field, denullish(schema)] as const) as { + [K in keyof T]: [K, ZodDenullish]; + }[keyof T][]; + return new ZodObject({ + ...obj._def, + shape: () => fromEntries(a) as unknown as ZodDenullishShape, // TODO: Safely assert type + }); +} + +/** + * Like Object.entries, but with actually useful typings + * @param obj The object to turn into a tuple array (`[key, value][]`) + * @returns The constructed tuple array from the given object + * @see https://github.com/3x071c/lsg-remix/blob/e2a9592ba3ec5103556f2cf307c32f08aeaee32d/app/lib/util/entries.ts + */ +export const entries = ( + obj: O +): { + readonly [K in keyof O]: [K, O[K]]; +}[keyof O][] => { + return Object.entries(obj) as { + [K in keyof O]: [K, O[K]]; + }[keyof O][]; +}; + +/** + * Returns a type with all readonly notations removed (traverses recursively on an object) + */ +type DeepWriteable = T extends Readonly<{ + -readonly [K in keyof T]: T[K]; +}> + ? { + -readonly [K in keyof T]: DeepWriteable; + } + : T; /* Make it work with readonly types (this is not strictly necessary) */ + +type FromEntries = T extends [infer Keys, unknown][] + ? { [K in Keys & PropertyKey]: Extract[1] } + : never; + +/** + * Like Object.fromEntries, but with actually useful typings + * @param arr The tuple array (`[key, value][]`) to turn into an object + * @returns Object constructed from the given entries + * @see https://github.com/3x071c/lsg-remix/blob/e2a9592ba3ec5103556f2cf307c32f08aeaee32d/app/lib/util/fromEntries.ts + */ +export const fromEntries = < + E extends [PropertyKey, unknown][] | ReadonlyArray +>( + entries: E +): FromEntries> => { + return Object.fromEntries(entries) as FromEntries>; +};