From f7fd4ac32e613cc75a78bd809ef1b529db3ed13c Mon Sep 17 00:00:00 2001 From: chrishoermann <53531653+chrishoermann@users.noreply.github.com> Date: Sun, 12 Mar 2023 19:42:12 +0100 Subject: [PATCH] implemented support for extendedWhereUniqueInput preview feature #91 --- Readme.md | 33 ++- packages/generator/Readme.md | 33 ++- packages/generator/package.json | 4 +- .../src/classes/extendedDMMFInputType.ts | 78 +++++- .../src/classes/extendedDMMFSchemaField.ts | 4 +- .../contentWriters/writeInputObjectType.ts | 242 ++++++++++++------ .../__tests__/getAllBoolCombinations.test.ts | 54 ++++ .../src/utils/getAllBoolCombinations.ts | 24 ++ .../src/utils/getPrismaClientOutputPath.ts | 23 -- packages/generator/src/utils/index.ts | 1 + packages/usage/package.json | 2 +- packages/usage/prisma/schema.prisma | 57 +++-- packages/usage/tests/implementations/xor.ts | 24 +- pnpm-lock.yaml | 12 +- 14 files changed, 431 insertions(+), 160 deletions(-) create mode 100644 packages/generator/src/utils/__tests__/getAllBoolCombinations.test.ts create mode 100644 packages/generator/src/utils/getAllBoolCombinations.ts diff --git a/Readme.md b/Readme.md index ca8c609d..7863163a 100644 --- a/Readme.md +++ b/Readme.md @@ -16,6 +16,10 @@ Since I'm maintaining the generator in my spare time consider buying me a coffee Be aware that some generator options have been removed, a few new have been added, the behaviour of custom imports has changed and ts-morph is no longer needed to generate files in v2.0.0. +## Known issues + +> Since `zod version 3.21.2` some schemas throw a typescript error. Please use `zod version 3.21.1` until this issue is resolved. + ## Table of contents - [About this project](#about-this-project) @@ -591,27 +595,38 @@ The above model would generate the following schema: // DECIMAL HELPERS //------------------------------------------------------ -export const DecimalJSLikeSchema = z.object({ +export const DecimalJSLikeSchema: z.ZodType = z.object({ d: z.array(z.number()), e: z.number(), s: z.number(), + toFixed: z.function().args().returns(z.string()), }); -export type DecimalJSLike = z.infer; +export const DecimalJSLikeListSchema: z.ZodType = z + .object({ + d: z.array(z.number()), + e: z.number(), + s: z.number(), + toFixed: z.function().args().returns(z.string()), + }) + .array(); export const DECIMAL_STRING_REGEX = /^[0-9.,e+-bxffo_cp]+$|Infinity|NaN/; export const isValidDecimalInput = ( - v?: null | string | number | DecimalJsLike, -) => { - if (!v) return false; + v?: null | string | number | Prisma.DecimalJsLike, +): v is string | number | Prisma.DecimalJsLike => { + if (v === undefined || v === null) return false; return ( - (typeof v === 'object' && 'd' in v && 'e' in v && 's' in v) || + (typeof v === 'object' && + 'd' in v && + 'e' in v && + 's' in v && + 'toFixed' in v) || (typeof v === 'string' && DECIMAL_STRING_REGEX.test(v)) || typeof v === 'number' ); }; - // SCHEMA //------------------------------------------------------ @@ -620,8 +635,8 @@ export const MyModelSchema = z.object({ decimal: z .union([z.number(), z.string(), DecimalJSLikeSchema]) .refine((v) => isValidDecimalInput(v), { - message: 'Field "decimal" must be a Decimal', - path: ['Models', 'DecimalModel'], + message: + "Field 'decimal' must be a Decimal. Location: ['Models', 'DecimalModel']", }), }); ``` diff --git a/packages/generator/Readme.md b/packages/generator/Readme.md index ca8c609d..7863163a 100644 --- a/packages/generator/Readme.md +++ b/packages/generator/Readme.md @@ -16,6 +16,10 @@ Since I'm maintaining the generator in my spare time consider buying me a coffee Be aware that some generator options have been removed, a few new have been added, the behaviour of custom imports has changed and ts-morph is no longer needed to generate files in v2.0.0. +## Known issues + +> Since `zod version 3.21.2` some schemas throw a typescript error. Please use `zod version 3.21.1` until this issue is resolved. + ## Table of contents - [About this project](#about-this-project) @@ -591,27 +595,38 @@ The above model would generate the following schema: // DECIMAL HELPERS //------------------------------------------------------ -export const DecimalJSLikeSchema = z.object({ +export const DecimalJSLikeSchema: z.ZodType = z.object({ d: z.array(z.number()), e: z.number(), s: z.number(), + toFixed: z.function().args().returns(z.string()), }); -export type DecimalJSLike = z.infer; +export const DecimalJSLikeListSchema: z.ZodType = z + .object({ + d: z.array(z.number()), + e: z.number(), + s: z.number(), + toFixed: z.function().args().returns(z.string()), + }) + .array(); export const DECIMAL_STRING_REGEX = /^[0-9.,e+-bxffo_cp]+$|Infinity|NaN/; export const isValidDecimalInput = ( - v?: null | string | number | DecimalJsLike, -) => { - if (!v) return false; + v?: null | string | number | Prisma.DecimalJsLike, +): v is string | number | Prisma.DecimalJsLike => { + if (v === undefined || v === null) return false; return ( - (typeof v === 'object' && 'd' in v && 'e' in v && 's' in v) || + (typeof v === 'object' && + 'd' in v && + 'e' in v && + 's' in v && + 'toFixed' in v) || (typeof v === 'string' && DECIMAL_STRING_REGEX.test(v)) || typeof v === 'number' ); }; - // SCHEMA //------------------------------------------------------ @@ -620,8 +635,8 @@ export const MyModelSchema = z.object({ decimal: z .union([z.number(), z.string(), DecimalJSLikeSchema]) .refine((v) => isValidDecimalInput(v), { - message: 'Field "decimal" must be a Decimal', - path: ['Models', 'DecimalModel'], + message: + "Field 'decimal' must be a Decimal. Location: ['Models', 'DecimalModel']", }), }); ``` diff --git a/packages/generator/package.json b/packages/generator/package.json index cf94d443..ef1842c7 100644 --- a/packages/generator/package.json +++ b/packages/generator/package.json @@ -1,6 +1,6 @@ { "name": "zod-prisma-types", - "version": "2.4.1", + "version": "2.5.0", "description": "Generates zod schemas from Prisma models with advanced validation", "author": "Chris Hörmann", "license": "MIT", @@ -49,6 +49,6 @@ "@prisma/generator-helper": "^4.11.0", "code-block-writer": "^11.0.3", "lodash": "^4.17.21", - "zod": "^3.21.4" + "zod": "^3.21.1" } } diff --git a/packages/generator/src/classes/extendedDMMFInputType.ts b/packages/generator/src/classes/extendedDMMFInputType.ts index 83aba827..ebb38a66 100644 --- a/packages/generator/src/classes/extendedDMMFInputType.ts +++ b/packages/generator/src/classes/extendedDMMFInputType.ts @@ -36,7 +36,9 @@ export class ExtendedDMMFInputType readonly isDecimalField: boolean; readonly omitFields: string[] = []; readonly imports: Set; + /** @deprecated */ readonly isWhereUniqueInput?: boolean; + readonly extendedWhereUniqueFields?: ExtendedDMMFSchemaArg[][]; constructor( readonly generatorConfig: GeneratorConfig, @@ -56,10 +58,9 @@ export class ExtendedDMMFInputType this.isDecimalField = this._setIsDecimalField(); this.omitFields = this._setOmitFields(); this.imports = this._setImports(); - - // if (this.name === 'ProfileWhereUniqueInput') { - // console.log(type); - // } + this.extendedWhereUniqueFields = this._setExtendedWhereUniqueFields( + type.fields, + ); } /** @@ -80,12 +81,6 @@ export class ExtendedDMMFInputType (modelField) => modelField.name === field.name, ); - // const hasConstraints = this.constraints.fields?.includes(field.name); - - // if (this.name === 'ProfileWhereUniqueInput') { - // console.log({ fieldname: field.name, hasConstraints }); - // } - // validators and omitField should only be written for create and update types. // this prevents validation in e.g. search queries in "where inputs", // where strings like email addresses can be incomplete. @@ -179,6 +174,69 @@ export class ExtendedDMMFInputType return new Set(fieldImports); } + private _getExtendedWhereUniqueFieldCombinations( + arr: DMMF.SchemaArg[], + ): DMMF.SchemaArg[][] { + const result: DMMF.SchemaArg[][] = []; + + function combine(start: number, soFar: DMMF.SchemaArg[]) { + if (soFar.length === arr.length) { + result.push(soFar.slice()); + return; + } + + // include current element + combine(start + 1, [...soFar, { ...arr[start], isRequired: true }]); + + // exclude current element + combine(start + 1, [...soFar, { ...arr[start], isRequired: false }]); + } + + combine(0, []); + return result; + } + + private _setExtendedWhereUniqueFields(fields: DMMF.SchemaArg[]) { + if (!this.constraints.fields || !this.name.includes('WhereUniqueInput')) { + return undefined; + } + + // get the DMMF.SchemaArg for all fields that are part of the constraints + // that are marked for the extended where unique input + const extendedWhereUniqueFields = this.constraints.fields + .map((fieldName) => { + return fields.find((field) => field.name === fieldName); + }) + .filter((field): field is DMMF.SchemaArg => field !== undefined); + + // get all combinations of bool values on isRequired fields + // for the provided set of fields + const combinations = this._getExtendedWhereUniqueFieldCombinations( + extendedWhereUniqueFields, + ); + + // filter out combinations where isRequired is False because + // these cominations are included in the all optional type that is + // later cominened with the generated union type. + const filteredCombinations = combinations.filter( + (combination) => !combination.every((field) => !field.isRequired), + ); + + // filter out all fields that are not required + // since they are added via the all optional type + const extendedFilterdCombinations = filteredCombinations.map( + (combination) => { + return combination.filter((field) => field.isRequired); + }, + ); + + // create an ExtendedDMMFSchemaArg for each combination field + // so the writer functions can be used as is + return extendedFilterdCombinations.map((combination) => { + return this._setFields(combination); + }); + } + hasOmitFields() { return this.omitFields.length > 0; } diff --git a/packages/generator/src/classes/extendedDMMFSchemaField.ts b/packages/generator/src/classes/extendedDMMFSchemaField.ts index ba07a434..5d04105c 100644 --- a/packages/generator/src/classes/extendedDMMFSchemaField.ts +++ b/packages/generator/src/classes/extendedDMMFSchemaField.ts @@ -425,7 +425,9 @@ export class ExtendedDMMFSchemaField private _getCustomArgsMultipleTypes(arg: ExtendedDMMFSchemaArg) { return arg.inputTypes .map((inputType) => { - return `z.infer`; + return `z.infer${ + inputType.isList ? '[]' : '' + }`; }) .join(' | '); } diff --git a/packages/generator/src/functions/contentWriters/writeInputObjectType.ts b/packages/generator/src/functions/contentWriters/writeInputObjectType.ts index a618bbf9..6b982cc3 100644 --- a/packages/generator/src/functions/contentWriters/writeInputObjectType.ts +++ b/packages/generator/src/functions/contentWriters/writeInputObjectType.ts @@ -1,7 +1,111 @@ +import CodeBlockWriter from 'code-block-writer'; + import { writeNonScalarType, writeScalarType, writeSpecialType } from '..'; -import { ExtendedDMMFInputType } from '../../classes'; +import { ExtendedDMMFInputType, ExtendedDMMFSchemaArg } from '../../classes'; import { type ContentWriterOptions } from '../../types'; +///////////////////////////////////////////// +// INTERFACE +///////////////////////////////////////////// + +interface WriteInputTypeFieldOptions { + writer: CodeBlockWriter; + field: ExtendedDMMFSchemaArg; + writeComma?: boolean; + writeValidation?: boolean; +} + +///////////////////////////////////////////// +// WRITER FUNCTION +///////////////////////////////////////////// + +const writeInputTypeField = ({ + writer, + field, + writeComma = false, + writeValidation = false, +}: WriteInputTypeFieldOptions) => { + const { + isNullable, + isOptional, + zodCustomErrors, + zodValidatorString, + zodCustomValidatorString, + } = field; + + if (field.zodOmitField) { + writer.write(`// omitted: `); + } + + writer.write(`${field.name}: `); + + if (field.hasMultipleTypes) { + writer.write(`z.union([ `); + + field.inputTypes.forEach((inputType, idx) => { + const writeComma = idx !== field.inputTypes.length - 1; + writeScalarType(writer, { + inputType, + zodCustomErrors, + zodValidatorString, + zodCustomValidatorString, + writeComma, + writeValidation, + }); + writeNonScalarType(writer, { + inputType, + writeComma, + }); + writeSpecialType(writer, { + inputType, + zodCustomErrors, + zodCustomValidatorString, + writeComma, + writeValidation, + }); + }); + + writer + .write(` ])`) + .conditionalWrite(!field.isRequired, `.optional()`) + .conditionalWrite(field.isNullable, `.nullable()`) + .write(`,`); + } else { + const inputType = field.inputTypes[0]; + writeScalarType(writer, { + inputType, + isNullable, + isOptional, + zodCustomErrors, + zodValidatorString, + zodCustomValidatorString, + writeValidation, + writeComma, + }); + writeNonScalarType(writer, { + inputType, + isNullable, + isOptional, + writeComma, + }); + writeSpecialType(writer, { + inputType, + zodCustomErrors, + zodCustomValidatorString, + isNullable, + isOptional, + writeValidation, + writeComma, + }); + } + + writer.newLine(); +}; + +///////////////////////////////////////////// +// MAIN FUNCTION +///////////////////////////////////////////// + export const writeInputObjectType = ( { fileWriter: { writer, writeImportSet }, @@ -24,87 +128,75 @@ export const writeInputObjectType = ( }, ${inputType.getOmitFieldsUnion()}>>` : `z.ZodType`; - writer - .blankLine() - .write(`export const ${inputType.name}Schema: ${type} = `) - .write(`z.object(`) - .inlineBlock(() => { - inputType.fields.forEach((field) => { - const { - isNullable, - isOptional, - zodCustomErrors, - zodValidatorString, - zodCustomValidatorString, - } = field; - - if (field.zodOmitField) { - writer.write(`// omitted: `); - } - - writer.write(`${field.name}: `); - - if (field.hasMultipleTypes) { - writer.write(`z.union([ `); - - field.inputTypes.forEach((inputType, idx) => { - const writeComma = idx !== field.inputTypes.length - 1; - writeScalarType(writer, { - inputType, - zodCustomErrors, - zodValidatorString, - zodCustomValidatorString, - writeComma, - writeValidation: addInputTypeValidation, - }); - writeNonScalarType(writer, { - inputType, - writeComma, - }); - writeSpecialType(writer, { - inputType, - zodCustomErrors, - zodCustomValidatorString, - writeComma, + writer.blankLine().write(`export const ${inputType.name}Schema: ${type} = `); + + const { extendedWhereUniqueFields } = inputType; + + const writeExtendedWhereUniqueInput = + Array.isArray(extendedWhereUniqueFields) && + extendedWhereUniqueFields.length !== 0; + + if (writeExtendedWhereUniqueInput) { + // if only one element is present in the array, + // a z.object is used instead of a z.union + if (extendedWhereUniqueFields.length === 1) { + writer + .write(`z.object(`) + .inlineBlock(() => { + extendedWhereUniqueFields[0].forEach((field, idx) => { + writeInputTypeField({ + writer, + field, + writeComma: idx !== extendedWhereUniqueFields[0].length - 1, writeValidation: addInputTypeValidation, }); }); - - writer - .write(` ])`) - .conditionalWrite(!field.isRequired, `.optional()`) - .conditionalWrite(field.isNullable, `.nullable()`) - .write(`,`); - } else { - const inputType = field.inputTypes[0]; - writeScalarType(writer, { - inputType, - isNullable, - isOptional, - zodCustomErrors, - zodValidatorString, - zodCustomValidatorString, - writeValidation: addInputTypeValidation, - }); - writeNonScalarType(writer, { - inputType, - isNullable, - isOptional, + }) + .write(`)`) + .newLine() + .write(`.and(`); + } else { + // now we need the union of z.objects + writer + .write(`z.union([`) + .newLine() + .withIndentationLevel(1, () => { + extendedWhereUniqueFields.forEach((field) => { + writer + .write(`z.object(`) + .inlineBlock(() => { + field.forEach((field, idx) => { + writeInputTypeField({ + writer, + field, + writeComma: idx !== extendedWhereUniqueFields[0].length - 1, + writeValidation: addInputTypeValidation, + }); + }); + }) + .write(`),`) + .newLine(); }); - writeSpecialType(writer, { - inputType, - zodCustomErrors, - zodCustomValidatorString, - isNullable, - isOptional, - writeValidation: addInputTypeValidation, - }); - } + }) + .writeLine(`])`) + .write(`.and(`); + } + } - writer.newLine(); + writer + .write(`z.object(`) + .inlineBlock(() => { + inputType.fields.forEach((field) => { + writeInputTypeField({ + writer, + field, + writeValidation: addInputTypeValidation, + writeComma: field !== inputType.fields[inputType.fields.length - 1], + }); }); }) - .write(`).strict();`); + .conditionalWrite(!writeExtendedWhereUniqueInput, `).strict();`) + .conditionalWrite(writeExtendedWhereUniqueInput, `).strict());`); if (useMultipleFiles && !getSingleFileContent) { writer.blankLine().writeLine(`export default ${inputType.name}Schema;`); diff --git a/packages/generator/src/utils/__tests__/getAllBoolCombinations.test.ts b/packages/generator/src/utils/__tests__/getAllBoolCombinations.test.ts new file mode 100644 index 00000000..00ba0e83 --- /dev/null +++ b/packages/generator/src/utils/__tests__/getAllBoolCombinations.test.ts @@ -0,0 +1,54 @@ +import { it, expect } from 'vitest'; + +import { getAllBoolCombinations } from '../getAllBoolCombinations'; + +it('should transform array', () => { + const permutations = getAllBoolCombinations([ + { elem: 'a', isRequired: true }, + { elem: 'b', isRequired: true }, + { elem: 'c', isRequired: true }, + ]); + + expect(permutations).toEqual([ + [ + { elem: 'a', isRequired: true }, + { elem: 'b', isRequired: true }, + { elem: 'c', isRequired: true }, + ], + [ + { elem: 'a', isRequired: true }, + { elem: 'b', isRequired: true }, + { elem: 'c', isRequired: false }, + ], + [ + { elem: 'a', isRequired: true }, + { elem: 'b', isRequired: false }, + { elem: 'c', isRequired: true }, + ], + [ + { elem: 'a', isRequired: true }, + { elem: 'b', isRequired: false }, + { elem: 'c', isRequired: false }, + ], + [ + { elem: 'a', isRequired: false }, + { elem: 'b', isRequired: true }, + { elem: 'c', isRequired: true }, + ], + [ + { elem: 'a', isRequired: false }, + { elem: 'b', isRequired: true }, + { elem: 'c', isRequired: false }, + ], + [ + { elem: 'a', isRequired: false }, + { elem: 'b', isRequired: false }, + { elem: 'c', isRequired: true }, + ], + [ + { elem: 'a', isRequired: false }, + { elem: 'b', isRequired: false }, + { elem: 'c', isRequired: false }, + ], + ]); +}); diff --git a/packages/generator/src/utils/getAllBoolCombinations.ts b/packages/generator/src/utils/getAllBoolCombinations.ts new file mode 100644 index 00000000..1c800e23 --- /dev/null +++ b/packages/generator/src/utils/getAllBoolCombinations.ts @@ -0,0 +1,24 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +type ObjectWithRequiredProperty = T & { isRequired: boolean }; + +export function getAllBoolCombinations>( + arr: ObjectWithRequiredProperty[], +): ObjectWithRequiredProperty[][] { + const result: ObjectWithRequiredProperty[][] = []; + + function combine(start: number, soFar: ObjectWithRequiredProperty[]) { + if (soFar.length === arr.length) { + result.push(soFar.slice()); + return; + } + + // include current element + combine(start + 1, [...soFar, { ...arr[start], isRequired: true }]); + + // exclude current element + combine(start + 1, [...soFar, { ...arr[start], isRequired: false }]); + } + + combine(0, []); + return result; +} diff --git a/packages/generator/src/utils/getPrismaClientOutputPath.ts b/packages/generator/src/utils/getPrismaClientOutputPath.ts index ae6eac77..6a14e8b9 100644 --- a/packages/generator/src/utils/getPrismaClientOutputPath.ts +++ b/packages/generator/src/utils/getPrismaClientOutputPath.ts @@ -1,8 +1,6 @@ import { GeneratorOptions } from '@prisma/generator-helper'; import path from 'path'; -// currently a bit hacky but does the job - export const getPrismaClientOutputPath = (options: GeneratorOptions) => { // find the prisma client config const prismaClientOptions = options.otherGenerators.find( @@ -22,27 +20,6 @@ export const getPrismaClientOutputPath = (options: GeneratorOptions) => { if (options.generator.config?.['prismaClientPath']) { return { prismaClientPath: options.generator.config?.['prismaClientPath'] }; } - // if (options.generator.config?.['prismaClientPath']) { - // const clientPath: string[] = [process.cwd()]; - - // // if the prisma schema is located in the prisma folder - // // the path needs to be adjusted - // if (prismaClientOptions.output.value.includes('prisma')) { - // clientPath.push('prisma'); - // } - - // clientPath.push(options.generator.config?.['prismaClientPath']); - - // const customPrismaClientPath = path - // .relative(options.generator.output.value, path.join(...clientPath)) - // .replace(/\\/g, '/'); - - // if (options.generator.config?.['useMultipleFiles']) { - // return { prismaClientPath: `../${customPrismaClientPath}` }; - // } - - // return { prismaClientPath: customPrismaClientPath }; - // } // get the relative path to the prisma schema const prismaClientPath = path diff --git a/packages/generator/src/utils/index.ts b/packages/generator/src/utils/index.ts index 38f6715c..d81611df 100644 --- a/packages/generator/src/utils/index.ts +++ b/packages/generator/src/utils/index.ts @@ -1,3 +1,4 @@ +export * from './getAllBoolCombinations'; export * from './getStringVariants'; export * from './getPrismaClientOutputPath'; export * from './getPrismaDbProvider'; diff --git a/packages/usage/package.json b/packages/usage/package.json index 99ec65ef..bc1fc439 100644 --- a/packages/usage/package.json +++ b/packages/usage/package.json @@ -29,7 +29,7 @@ "ts-node-dev": "^2.0.0", "validator": "^13.9.0", "vitest": "^0.25.8", - "zod": "^3.21.4", + "zod": "^3.21.1", "zod-prisma-types": "workspace:2.4.1" } } diff --git a/packages/usage/prisma/schema.prisma b/packages/usage/prisma/schema.prisma index eb1f5801..9978bd04 100644 --- a/packages/usage/prisma/schema.prisma +++ b/packages/usage/prisma/schema.prisma @@ -4,8 +4,8 @@ generator client { provider = "prisma-client-js" // output = "./generated/client" - // previewFeatures = ["views", "extendedWhereUnique"] - previewFeatures = ["views"] + previewFeatures = ["views", "extendedWhereUnique"] + // previewFeatures = ["views"] } datasource db { @@ -20,7 +20,7 @@ generator zod { provider = "ts-node-dev ../generator/src/bin.ts" // provider = "zod-prisma-types" output = "./generated/zod" // default is ./generated/zod - // useMultipleFiles = true // default is false + useMultipleFiles = true // default is false // createInputTypes = false // default is true // createModelTypes = false // default is true // addInputTypeValidation = false // default is true @@ -36,11 +36,6 @@ generator zod { // prismaClientPath = "./generated/client" // optional } -generator zodPrismaGen { - provider = "prisma-zod-generator" - output = "./generated/zodPrisma" -} - // MIXED CASE ENUMS AND MODELS // ----------------------------------------------- @@ -237,7 +232,7 @@ model DateModel { // Used for testing if the string regex validator is correctly transformed -/// @zod.import(["import * as s from '$lib/schemas'"]) +// @zod.import(["import * as s from '$lib/schemas'"]) model StringRegexModel { id Int @id @default(autoincrement()) string String /// @zod.string.regex(/^[a-z0-9]+$/i, { message: "error" }) @@ -266,6 +261,34 @@ model UserTwo { practice Practice? @relation(fields: [practiceId], references: [id]) } +// EXTENDED WHERE UNIQUE INPUT +// ----------------------------------------------- + +// The following models are used to test if the extended +// where unique input gets created correctly even with +// multiple unique fields. + +model ExtendedWUOne { + id Int @id @default(autoincrement()) + email String + username String + name String +} + +model ExtendedWUTwo { + id Int @id @default(autoincrement()) + title String @unique + content String? + authorId Int +} + +model ExtendedWUThree { + id Int @id @default(autoincrement()) + email String @unique + username String @unique + name String +} + // model Post { // id String @id @default(auto()) @map("_id") @db.ObjectId // title String @default("") @@ -298,22 +321,6 @@ model UserTwo { // count Int // } -// model User { -// id Int @id @default(autoincrement()) -// email String @unique -// name String - -// posts Post[] -// } - -// model Post { -// id Int @id @default(autoincrement()) -// title String -// content String? -// author User @relation(fields: [authorId], references: [id]) -// authorId Int -// } - // view AbcUser { // date DateTime @unique @db.Date // count Int diff --git a/packages/usage/tests/implementations/xor.ts b/packages/usage/tests/implementations/xor.ts index 558cb213..f98d9307 100644 --- a/packages/usage/tests/implementations/xor.ts +++ b/packages/usage/tests/implementations/xor.ts @@ -21,13 +21,35 @@ interface B { c: string; } -type C = Without; +type C = Without & B; + +type CB = C & B; + +const c: C = { + a: 'a', + c: 'c', + b: undefined, +}; + +type D = Without; + +type XOR = T extends object + ? U extends object + ? (Without & U) | (Without & T) + : U + : T; + +type CXor = XOR; type UserCreateWithout = Without< Prisma.UserCreateInput, Prisma.UserUncheckedCreateInput >; +const mergedSchema = UserCreateInputSchema.and(UserUncheckedCreateInputSchema); + +type user = z.infer; + export const UserCreateArgsSchema: z.ZodType = z .object({ select: UserSelectSchema.optional(), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d7fa34ce..df468c9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -25,7 +25,7 @@ importers: prettier: ^2.8.4 typescript: ^4.9.5 vitest: ^0.24.5 - zod: ^3.21.4 + zod: ^3.21.1 dependencies: '@prisma/generator-helper': 4.11.0 code-block-writer: 11.0.3 @@ -61,8 +61,8 @@ importers: typescript: ^4.9.5 validator: ^13.9.0 vitest: ^0.25.8 - zod: ^3.21.4 - zod-prisma-types: workspace:2.4.1-beta.2 + zod: ^3.21.1 + zod-prisma-types: workspace:2.4.1 dependencies: '@prisma/client': 4.11.0_prisma@4.11.0 '@trpc/client': 10.15.0_@trpc+server@10.15.0 @@ -75,7 +75,7 @@ importers: ts-node-dev: 2.0.0_lwgqdwokjtwlohdqtbb6s252kq validator: 13.9.0 vitest: 0.25.8 - zod: 3.21.4 + zod: 3.21.1 zod-prisma-types: link:../generator devDependencies: '@types/node': 18.15.0 @@ -3981,6 +3981,10 @@ packages: compress-commons: 4.1.1 readable-stream: 3.6.2 + /zod/3.21.1: + resolution: {integrity: sha512-+dTu2m6gmCbO9Ahm4ZBDapx2O6ZY9QSPXst2WXjcznPMwf2YNpn3RevLx4KkZp1OPW/ouFcoBtBzFz/LeY69oA==} + dev: false + /zod/3.21.4: resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} dev: false