diff --git a/TODO.md b/TODO.md index 7d9aa7b9..c63f4758 100644 --- a/TODO.md +++ b/TODO.md @@ -44,6 +44,7 @@ - [x] Nested to-one - [x] Incremental update for numeric fields - [x] Array update + - [ ] Strict typing for checked/unchecked input - [x] Upsert - [ ] Implement with "on conflict" - [x] Delete @@ -71,6 +72,7 @@ - [x] Custom table name - [x] Custom field name - [ ] Strict undefined checks + - [ ] DbNull vs JsonNull - [ ] Benchmark - [ ] Plugin - [ ] Post-mutation hooks should be called after transaction is committed diff --git a/package.json b/package.json index 1295655d..4e713d73 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zenstack-v3", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "description": "ZenStack", "packageManager": "pnpm@10.12.1", "scripts": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 1b3c92ed..3db5ea94 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -3,7 +3,7 @@ "publisher": "zenstack", "displayName": "ZenStack CLI", "description": "FullStack database toolkit with built-in access control and automatic API generation.", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "type": "module", "author": { "name": "ZenStack Team" diff --git a/packages/cli/src/actions/action-utils.ts b/packages/cli/src/actions/action-utils.ts index 2712e9cf..b11b671b 100644 --- a/packages/cli/src/actions/action-utils.ts +++ b/packages/cli/src/actions/action-utils.ts @@ -1,4 +1,3 @@ -import { findUp } from '@zenstackhq/common-helpers'; import { loadDocument } from '@zenstackhq/language'; import { PrismaSchemaGenerator } from '@zenstackhq/sdk'; import colors from 'colors'; @@ -53,10 +52,13 @@ export function handleSubProcessError(err: unknown) { } } -export async function generateTempPrismaSchema(zmodelPath: string) { +export async function generateTempPrismaSchema(zmodelPath: string, folder?: string) { const model = await loadSchemaDocument(zmodelPath); const prismaSchema = await new PrismaSchemaGenerator(model).generate(); - const prismaSchemaFile = path.resolve(path.dirname(zmodelPath), '~schema.prisma'); + if (!folder) { + folder = path.dirname(zmodelPath); + } + const prismaSchemaFile = path.resolve(folder, '~schema.prisma'); fs.writeFileSync(prismaSchemaFile, prismaSchema); return prismaSchemaFile; } @@ -83,3 +85,28 @@ export function getPkgJsonConfig(startPath: string) { return result; } + +type FindUpResult = Multiple extends true ? string[] | undefined : string | undefined; + +function findUp( + names: string[], + cwd: string = process.cwd(), + multiple: Multiple = false as Multiple, + result: string[] = [], +): FindUpResult { + if (!names.some((name) => !!name)) { + return undefined; + } + const target = names.find((name) => fs.existsSync(path.join(cwd, name))); + if (multiple === false && target) { + return path.join(cwd, target) as FindUpResult; + } + if (target) { + result.push(path.join(cwd, target)); + } + const up = path.resolve(cwd, '..'); + if (up === cwd) { + return (multiple && result.length > 0 ? result : undefined) as FindUpResult; + } + return findUp(names, up, multiple, result); +} diff --git a/packages/cli/src/actions/generate.ts b/packages/cli/src/actions/generate.ts index 3355d462..1729bdf3 100644 --- a/packages/cli/src/actions/generate.ts +++ b/packages/cli/src/actions/generate.ts @@ -24,7 +24,7 @@ export async function run(options: Options) { // generate TS schema const tsSchemaFile = path.join(outputPath, 'schema.ts'); - await new TsSchemaGenerator().generate(schemaFile, [], tsSchemaFile); + await new TsSchemaGenerator().generate(schemaFile, [], outputPath); await runPlugins(model, outputPath, tsSchemaFile); diff --git a/packages/cli/src/actions/migrate.ts b/packages/cli/src/actions/migrate.ts index bb13956d..6a667a16 100644 --- a/packages/cli/src/actions/migrate.ts +++ b/packages/cli/src/actions/migrate.ts @@ -1,9 +1,11 @@ import fs from 'node:fs'; +import path from 'node:path'; import { execPackage } from '../utils/exec-utils'; import { generateTempPrismaSchema, getSchemaFile } from './action-utils'; type CommonOptions = { schema?: string; + migrations?: string; }; type DevOptions = CommonOptions & { @@ -24,7 +26,8 @@ type StatusOptions = CommonOptions; */ export async function run(command: string, options: CommonOptions) { const schemaFile = getSchemaFile(options.schema); - const prismaSchemaFile = await generateTempPrismaSchema(schemaFile); + const prismaSchemaDir = options.migrations ? path.dirname(options.migrations) : undefined; + const prismaSchemaFile = await generateTempPrismaSchema(schemaFile, prismaSchemaDir); try { switch (command) { diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 61013bfe..d75cff32 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -60,12 +60,14 @@ export function createProgram() { .action(generateAction); const migrateCommand = program.command('migrate').description('Update the database schema with migrations.'); + const migrationsOption = new Option('--migrations ', 'path for migrations'); migrateCommand .command('dev') .addOption(schemaOption) .addOption(new Option('-n, --name ', 'migration name')) .addOption(new Option('--create-only', 'only create migration, do not apply')) + .addOption(migrationsOption) .description('Create a migration from changes in schema and apply it to the database.') .action((options) => migrateAction('dev', options)); @@ -73,18 +75,21 @@ export function createProgram() { .command('reset') .addOption(schemaOption) .addOption(new Option('--force', 'skip the confirmation prompt')) + .addOption(migrationsOption) .description('Reset your database and apply all migrations, all data will be lost.') .action((options) => migrateAction('reset', options)); migrateCommand .command('deploy') .addOption(schemaOption) + .addOption(migrationsOption) .description('Deploy your pending migrations to your production/staging database.') .action((options) => migrateAction('deploy', options)); migrateCommand .command('status') .addOption(schemaOption) + .addOption(migrationsOption) .description('check the status of your database migrations.') .action((options) => migrateAction('status', options)); diff --git a/packages/common-helpers/package.json b/packages/common-helpers/package.json index feec83e2..abf46539 100644 --- a/packages/common-helpers/package.json +++ b/packages/common-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/common-helpers", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "description": "ZenStack Common Helpers", "type": "module", "scripts": { diff --git a/packages/common-helpers/src/find-up.ts b/packages/common-helpers/src/find-up.ts deleted file mode 100644 index afb2fed9..00000000 --- a/packages/common-helpers/src/find-up.ts +++ /dev/null @@ -1,34 +0,0 @@ -import fs from 'fs'; -import path from 'path'; - -/** - * A type named FindUp that takes a type parameter e which extends boolean. - */ -export type FindUpResult = Multiple extends true ? string[] | undefined : string | undefined; - -/** - * Find and return file paths by searching parent directories based on the given names list and current working directory (cwd) path. - * Optionally return a single path or multiple paths. - * If multiple allowed, return all paths found. - * If no paths are found, return undefined. - * - * @param names An array of strings representing names to search for within the directory - * @param cwd A string representing the current working directory - * @param multiple A boolean flag indicating whether to search for multiple levels. Useful for finding node_modules directories... - * @param An array of strings representing the accumulated results used in multiple results - * @returns Path(s) to a specific file or folder within the directory or parent directories - */ -export function findUp( - names: string[], - cwd: string = process.cwd(), - multiple: Multiple = false as Multiple, - result: string[] = [], -): FindUpResult { - if (!names.some((name) => !!name)) return undefined; - const target = names.find((name) => fs.existsSync(path.join(cwd, name))); - if (multiple === false && target) return path.join(cwd, target) as FindUpResult; - if (target) result.push(path.join(cwd, target)); - const up = path.resolve(cwd, '..'); - if (up === cwd) return (multiple && result.length > 0 ? result : undefined) as FindUpResult; // it'll fail anyway - return findUp(names, up, multiple, result); -} diff --git a/packages/common-helpers/src/index.ts b/packages/common-helpers/src/index.ts index 3b0d2d3c..7f9c421b 100644 --- a/packages/common-helpers/src/index.ts +++ b/packages/common-helpers/src/index.ts @@ -1,4 +1,3 @@ -export * from './find-up'; export * from './is-plain-object'; export * from './lower-case-first'; export * from './param-case'; diff --git a/packages/create-zenstack/package.json b/packages/create-zenstack/package.json index b0206c89..db3c4242 100644 --- a/packages/create-zenstack/package.json +++ b/packages/create-zenstack/package.json @@ -1,6 +1,6 @@ { "name": "create-zenstack", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "description": "Create a new ZenStack project", "type": "module", "scripts": { diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 9d627ebd..312f2f1e 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/eslint-config", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "type": "module", "private": true, "license": "MIT" diff --git a/packages/ide/vscode/package.json b/packages/ide/vscode/package.json index d5eea4d4..e5f85293 100644 --- a/packages/ide/vscode/package.json +++ b/packages/ide/vscode/package.json @@ -1,7 +1,7 @@ { "name": "zenstack", "publisher": "zenstack", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "displayName": "ZenStack Language Tools", "description": "VSCode extension for ZenStack ZModel language", "private": true, diff --git a/packages/language/package.json b/packages/language/package.json index d3845d85..ec6cd744 100644 --- a/packages/language/package.json +++ b/packages/language/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/language", "description": "ZenStack ZModel language specification", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "license": "MIT", "author": "ZenStack Team", "files": [ diff --git a/packages/runtime/package.json b/packages/runtime/package.json index 825d0235..5b3cc6cd 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/runtime", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "description": "ZenStack Runtime", "type": "module", "scripts": { diff --git a/packages/runtime/src/client/contract.ts b/packages/runtime/src/client/contract.ts index 531f3fa7..53c8a36f 100644 --- a/packages/runtime/src/client/contract.ts +++ b/packages/runtime/src/client/contract.ts @@ -1,7 +1,7 @@ import type { Decimal } from 'decimal.js'; import { type GetModels, type ProcedureDef, type SchemaDef } from '../schema'; import type { AuthType } from '../schema/auth'; -import type { OrUndefinedIf, UnwrapTuplePromises } from '../utils/type-utils'; +import type { OrUndefinedIf, Simplify, UnwrapTuplePromises } from '../utils/type-utils'; import type { TRANSACTION_UNSUPPORTED_METHODS } from './constants'; import type { AggregateArgs, @@ -299,7 +299,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise[]>; + ): ZenStackPromise>[]>; /** * Returns a uniquely identified entity. @@ -309,7 +309,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise | null>; + ): ZenStackPromise> | null>; /** * Returns a uniquely identified entity or throws `NotFoundError` if not found. @@ -319,7 +319,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Returns the first entity. @@ -329,7 +329,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise | null>; + ): ZenStackPromise> | null>; /** * Returns the first entity or throws `NotFoundError` if not found. @@ -339,7 +339,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Creates a new entity. @@ -395,7 +395,7 @@ export interface ModelOperations>( args: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Creates multiple entities. Only scalar fields are allowed. @@ -446,7 +446,7 @@ export interface ModelOperations>( args?: SelectSubset>, - ): ZenStackPromise[]>; + ): ZenStackPromise>[]>; /** * Updates a uniquely identified entity. @@ -567,7 +567,7 @@ export interface ModelOperations>( args: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Updates multiple entities. @@ -617,7 +617,7 @@ export interface ModelOperations>( args: Subset>, - ): ZenStackPromise[]>; + ): ZenStackPromise>[]>; /** * Creates or updates an entity. @@ -641,7 +641,7 @@ export interface ModelOperations>( args: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Deletes a uniquely identifiable entity. @@ -664,7 +664,7 @@ export interface ModelOperations>( args: SelectSubset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Deletes multiple entities. @@ -709,7 +709,7 @@ export interface ModelOperations>( args?: Subset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Aggregates rows. @@ -730,7 +730,7 @@ export interface ModelOperations>( args: Subset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; /** * Groups rows by columns. @@ -766,7 +766,7 @@ export interface ModelOperations>( args: Subset>, - ): ZenStackPromise>; + ): ZenStackPromise>>; } //#endregion diff --git a/packages/runtime/src/client/crud-types.ts b/packages/runtime/src/client/crud-types.ts index 4643991d..e97d2e29 100644 --- a/packages/runtime/src/client/crud-types.ts +++ b/packages/runtime/src/client/crud-types.ts @@ -30,6 +30,7 @@ import type { NullableIf, Optional, OrArray, + Simplify, ValueOfPotentialTuple, WrapType, XOR, @@ -77,13 +78,21 @@ type ModelSelectResult RelationFieldType, FieldIsArray > - ? ModelResult< - Schema, - RelationFieldType, - Select[Key], - FieldIsOptional, - FieldIsArray - > + ? 'select' extends keyof Select[Key] + ? ModelResult< + Schema, + RelationFieldType, + Pick, + FieldIsOptional, + FieldIsArray + > + : ModelResult< + Schema, + RelationFieldType, + Pick, + FieldIsOptional, + FieldIsArray + > : DefaultModelResult< Schema, RelationFieldType, @@ -149,6 +158,14 @@ export type ModelResult< Array >; +export type SimplifiedModelResult< + Schema extends SchemaDef, + Model extends GetModels, + Args extends SelectIncludeOmit, + Optional = false, + Array = false, +> = Simplify>; + export type BatchResult = { count: number }; //#endregion @@ -207,11 +224,13 @@ type PrimitiveFilter = T extends 'St ? BooleanFilter : T extends 'DateTime' ? DateTimeFilter - : T extends 'Json' - ? 'Not implemented yet' // TODO: Json filter - : never; + : T extends 'Bytes' + ? BytesFilter + : T extends 'Json' + ? 'Not implemented yet' // TODO: Json filter + : never; -export type CommonPrimitiveFilter = { +type CommonPrimitiveFilter = { equals?: NullableIf; in?: DataType[]; notIn?: DataType[]; @@ -247,6 +266,7 @@ export type BytesFilter = notIn?: Uint8Array[]; not?: BytesFilter; }; + export type BooleanFilter = | NullableIf | { @@ -313,14 +333,14 @@ export type WhereUniqueInput['uniqueFields'], string> >; -type OmitFields> = { +export type OmitInput> = { [Key in NonRelationFields]?: true; }; export type SelectIncludeOmit, AllowCount extends boolean> = { - select?: Select; - include?: Include; - omit?: OmitFields; + select?: SelectInput; + include?: IncludeInput; + omit?: OmitInput; }; type Distinct> = { @@ -331,14 +351,14 @@ type Cursor> = { cursor?: WhereUniqueInput; }; -type Select< +export type SelectInput< Schema extends SchemaDef, Model extends GetModels, - AllowCount extends boolean, + AllowCount extends boolean = true, AllowRelation extends boolean = true, > = { [Key in NonRelationFields]?: true; -} & (AllowRelation extends true ? Include : {}) & // relation fields +} & (AllowRelation extends true ? IncludeInput : {}) & // relation fields // relation count (AllowCount extends true ? { _count?: SelectCount } : {}); @@ -354,7 +374,7 @@ type SelectCount> = }; }; -type Include> = { +export type IncludeInput> = { [Key in RelationFields]?: | boolean | FindArgs< @@ -423,7 +443,7 @@ type RelationFilter< //#region Field utils -export type MapFieldType< +type MapFieldType< Schema extends SchemaDef, Model extends GetModels, Field extends GetFields, @@ -435,7 +455,7 @@ type MapFieldDefType; -export type OptionalFieldsForCreate> = keyof { +type OptionalFieldsForCreate> = keyof { [Key in GetFields as FieldIsOptional extends true ? Key : FieldHasDefault extends true @@ -467,14 +487,14 @@ type OppositeRelation< : never : never; -export type OppositeRelationFields< +type OppositeRelationFields< Schema extends SchemaDef, Model extends GetModels, Field extends GetFields, Opposite = OppositeRelation, > = Opposite extends RelationInfo ? (Opposite['fields'] extends string[] ? Opposite['fields'] : []) : []; -export type OppositeRelationAndFK< +type OppositeRelationAndFK< Schema extends SchemaDef, Model extends GetModels, Field extends GetFields, @@ -513,6 +533,9 @@ export type FindArgs< Distinct & Cursor; +export type FindManyArgs> = FindArgs; +export type FindFirstArgs> = FindArgs; + export type FindUniqueArgs> = { where?: WhereUniqueInput; } & SelectIncludeOmit; @@ -523,9 +546,9 @@ export type FindUniqueArgs> = { data: CreateInput; - select?: Select; - include?: Include; - omit?: OmitFields; + select?: SelectInput; + include?: IncludeInput; + omit?: OmitInput; }; export type CreateManyArgs> = CreateManyInput; @@ -534,8 +557,8 @@ export type CreateManyAndReturnArgs & { - select?: Select; - omit?: OmitFields; + select?: SelectInput; + omit?: OmitInput; }; type OptionalWrap, T extends object> = Optional< @@ -625,20 +648,15 @@ type ConnectOrCreatePayload< create: CreateInput; }; -export type CreateManyInput< - Schema extends SchemaDef, - Model extends GetModels, - Without extends string = never, -> = { +type CreateManyInput, Without extends string = never> = { data: OrArray, Without> & Omit, Without>>; skipDuplicates?: boolean; }; -export type CreateInput< - Schema extends SchemaDef, - Model extends GetModels, - Without extends string = never, -> = XOR, Without>, Omit, Without>>; +type CreateInput, Without extends string = never> = XOR< + Omit, Without>, + Omit, Without> +>; type NestedCreateInput< Schema extends SchemaDef, @@ -662,9 +680,9 @@ type NestedCreateManyInput< export type UpdateArgs> = { data: UpdateInput; where: WhereUniqueInput; - select?: Select; - include?: Include; - omit?: OmitFields; + select?: SelectInput; + include?: IncludeInput; + omit?: OmitInput; }; export type UpdateManyArgs> = UpdateManyPayload< @@ -676,8 +694,8 @@ export type UpdateManyAndReturnArgs & { - select?: Select; - omit?: OmitFields; + select?: SelectInput; + omit?: OmitInput; }; type UpdateManyPayload, Without extends string = never> = { @@ -690,12 +708,12 @@ export type UpsertArgs create: CreateInput; update: UpdateInput; where: WhereUniqueInput; - select?: Select; - include?: Include; - omit?: OmitFields; + select?: SelectInput; + include?: IncludeInput; + omit?: OmitInput; }; -export type UpdateScalarInput< +type UpdateScalarInput< Schema extends SchemaDef, Model extends GetModels, Without extends string = never, @@ -728,7 +746,7 @@ type ScalarUpdatePayload< } : never); -export type UpdateRelationInput< +type UpdateRelationInput< Schema extends SchemaDef, Model extends GetModels, Without extends string = never, @@ -739,7 +757,7 @@ export type UpdateRelationInput< Without >; -export type UpdateInput< +type UpdateInput< Schema extends SchemaDef, Model extends GetModels, Without extends string = never, @@ -795,9 +813,9 @@ type ToOneRelationUpdateInput< export type DeleteArgs> = { where: WhereUniqueInput; - select?: Select; - include?: Include; - omit?: OmitFields; + select?: SelectInput; + include?: IncludeInput; + omit?: OmitInput; }; export type DeleteManyArgs> = { @@ -816,7 +834,7 @@ export type CountArgs> select?: CountAggregateInput | true; }; -export type CountAggregateInput> = { +type CountAggregateInput> = { [Key in NonRelationFields]?: true; } & { _all?: true }; diff --git a/packages/runtime/src/client/crud/operations/base.ts b/packages/runtime/src/client/crud/operations/base.ts index 59a31ecf..32775ef8 100644 --- a/packages/runtime/src/client/crud/operations/base.ts +++ b/packages/runtime/src/client/crud/operations/base.ts @@ -181,17 +181,18 @@ export abstract class BaseOperationHandler { } // select - if (args?.select) { + if (args && 'select' in args && args.select) { // select is mutually exclusive with omit - query = this.buildFieldSelection(model, query, args?.select, model); + query = this.buildFieldSelection(model, query, args.select, model); } else { // include all scalar fields except those in omit - query = this.buildSelectAllScalarFields(model, query, args?.omit); + query = this.buildSelectAllScalarFields(model, query, (args as any)?.omit); } // include - if (args?.include) { - query = this.buildFieldSelection(model, query, args?.include, model); + if (args && 'include' in args && args.include) { + // note that 'omit' is handled above already + query = this.buildFieldSelection(model, query, args.include, model); } if (args?.cursor) { @@ -1878,7 +1879,7 @@ export abstract class BaseOperationHandler { } protected trimResult(data: any, args: SelectIncludeOmit, boolean>) { - if (!args.select) { + if (!('select' in args) || !args.select) { return data; } return Object.keys(args.select).reduce((acc, field) => { @@ -1890,9 +1891,9 @@ export abstract class BaseOperationHandler { protected needReturnRelations(model: string, args: SelectIncludeOmit, boolean>) { let returnRelation = false; - if (args.include) { + if ('include' in args && args.include) { returnRelation = Object.keys(args.include).length > 0; - } else if (args.select) { + } else if ('select' in args && args.select) { returnRelation = Object.entries(args.select).some(([K, v]) => { const fieldDef = this.requireField(model, K); return fieldDef.relation && v; diff --git a/packages/runtime/src/client/crud/operations/find.ts b/packages/runtime/src/client/crud/operations/find.ts index 7834e58b..77bbb615 100644 --- a/packages/runtime/src/client/crud/operations/find.ts +++ b/packages/runtime/src/client/crud/operations/find.ts @@ -5,12 +5,12 @@ import { BaseOperationHandler, type CrudOperation } from './base'; export class FindOperationHandler extends BaseOperationHandler { async handle(operation: CrudOperation, args: unknown, validateArgs = true): Promise { // normalize args to strip `undefined` fields - const normalizeArgs = this.normalizeArgs(args); + const normalizedArgs = this.normalizeArgs(args); // parse args const parsedArgs = validateArgs - ? this.inputValidator.validateFindArgs(this.model, operation === 'findUnique', normalizeArgs) - : normalizeArgs; + ? this.inputValidator.validateFindArgs(this.model, operation === 'findUnique', normalizedArgs) + : normalizedArgs; // run query const result = await this.read( diff --git a/packages/runtime/src/client/crud/validator.ts b/packages/runtime/src/client/crud/validator.ts index 7d1aed7d..00dc4f2c 100644 --- a/packages/runtime/src/client/crud/validator.ts +++ b/packages/runtime/src/client/crud/validator.ts @@ -924,8 +924,8 @@ export class InputValidator { } private makeUpdateDataSchema(model: string, withoutFields: string[] = [], withoutRelationFields = false) { - const regularAndFkFields: any = {}; - const regularAndRelationFields: any = {}; + const uncheckedVariantFields: Record = {}; + const checkedVariantFields: Record = {}; const modelDef = requireModel(this.schema, model); const hasRelation = Object.entries(modelDef.fields).some( ([key, value]) => value.relation && !withoutFields.includes(key), @@ -957,7 +957,11 @@ export class InputValidator { if (fieldDef.optional && !fieldDef.array) { fieldSchema = fieldSchema.nullable(); } - regularAndRelationFields[field] = fieldSchema; + checkedVariantFields[field] = fieldSchema; + if (fieldDef.array || !fieldDef.relation.references) { + // non-owned relation + uncheckedVariantFields[field] = fieldSchema; + } } else { let fieldSchema: ZodType = this.makePrimitiveSchema(fieldDef.type).optional(); @@ -1000,17 +1004,18 @@ export class InputValidator { fieldSchema = fieldSchema.nullable(); } - regularAndFkFields[field] = fieldSchema; + uncheckedVariantFields[field] = fieldSchema; if (!fieldDef.foreignKeyFor) { - regularAndRelationFields[field] = fieldSchema; + // non-fk field + checkedVariantFields[field] = fieldSchema; } } }); if (!hasRelation) { - return z.object(regularAndFkFields).strict(); + return z.object(uncheckedVariantFields).strict(); } else { - return z.union([z.object(regularAndFkFields).strict(), z.object(regularAndRelationFields).strict()]); + return z.union([z.object(uncheckedVariantFields).strict(), z.object(checkedVariantFields).strict()]); } } diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts index 4f1cce44..b5018f05 100644 --- a/packages/runtime/src/index.ts +++ b/packages/runtime/src/index.ts @@ -1 +1,2 @@ export * from './client'; +export type { JsonArray, JsonObject, JsonValue } from './utils/type-utils'; diff --git a/packages/runtime/src/utils/type-utils.ts b/packages/runtime/src/utils/type-utils.ts index abd963a5..b28119a3 100644 --- a/packages/runtime/src/utils/type-utils.ts +++ b/packages/runtime/src/utils/type-utils.ts @@ -6,6 +6,16 @@ export type NullableIf = Condition extends true ? export type PartialRecord = Partial>; +type _Preserve = Date | Function | Decimal | Uint8Array | JsonObject | JsonValue; +type _Depth = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; +export type Simplify = D extends 0 + ? T + : T extends object + ? T extends _Preserve + ? T + : { [K in keyof T]: Simplify } & {} + : T; + export type WrapType = Optional extends true ? T | null : Array extends true @@ -24,17 +34,17 @@ export type MapBaseType = T extends 'String' ? Decimal : T extends 'DateTime' ? Date - : T extends 'Json' - ? JsonValue - : unknown; + : T extends 'Bytes' + ? Uint8Array + : T extends 'Json' + ? JsonValue + : unknown; export type JsonValue = string | number | boolean | null | JsonObject | JsonArray; export type JsonObject = { [key: string]: JsonValue }; export type JsonArray = Array; -export type Simplify = { [Key in keyof T]: T[Key] } & {}; - export function call(code: string) { return { code }; } diff --git a/packages/runtime/test/client-api/update.test.ts b/packages/runtime/test/client-api/update.test.ts index b65aa501..8c6ec359 100644 --- a/packages/runtime/test/client-api/update.test.ts +++ b/packages/runtime/test/client-api/update.test.ts @@ -170,6 +170,50 @@ describe.each(createClientSpecs(PG_DB_NAME))('Client update tests', ({ createCli }), ).resolves.toMatchObject({ age: null }); }); + + it('compiles with Prisma checked/unchecked typing', async () => { + const user = await client.user.create({ + data: { + email: 'u1@test.com', + posts: { + create: { + id: '1', + title: 'title', + }, + }, + }, + }); + + // fk and owned-relation are mutually exclusive + // TODO: @ts-expect-error + client.post.update({ + where: { id: '1' }, + data: { + authorId: user.id, + title: 'title', + author: { connect: { id: user.id } }, + }, + }); + + // fk can work with non-owned relation + const comment = await client.comment.create({ + data: { + content: 'comment', + }, + }); + await expect( + client.post.update({ + where: { id: '1' }, + data: { + authorId: user.id, + title: 'title', + comments: { + connect: { id: comment.id }, + }, + }, + }), + ).toResolveTruthy(); + }); }); describe('nested to-many', () => { diff --git a/packages/runtime/test/typing/generate.ts b/packages/runtime/test/typing/generate.ts index 4e7c9b10..3ac77565 100644 --- a/packages/runtime/test/typing/generate.ts +++ b/packages/runtime/test/typing/generate.ts @@ -8,7 +8,7 @@ async function main() { const dir = path.dirname(fileURLToPath(import.meta.url)); const zmodelPath = path.join(dir, 'typing-test.zmodel'); const tsPath = path.join(dir, 'schema.ts'); - await generator.generate(zmodelPath, [], tsPath); + await generator.generate(zmodelPath, [], dir); const content = fs.readFileSync(tsPath, 'utf-8'); fs.writeFileSync(tsPath, content.replace(/@zenstackhq\/runtime/g, '../../dist')); diff --git a/packages/runtime/test/typing/models.ts b/packages/runtime/test/typing/models.ts new file mode 100644 index 00000000..cb27df2a --- /dev/null +++ b/packages/runtime/test/typing/models.ts @@ -0,0 +1,18 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// + +/* eslint-disable */ + +import { type ModelResult } from "@zenstackhq/runtime"; +import { schema } from "./schema"; +export type Schema = typeof schema; +export type User = ModelResult; +export type Post = ModelResult; +export type Profile = ModelResult; +export type Tag = ModelResult; +export type Region = ModelResult; +export type Meta = ModelResult; +export const Role = schema.enums.Role; +export type Role = (typeof Role)[keyof typeof Role]; diff --git a/packages/runtime/test/typing/schema.ts b/packages/runtime/test/typing/schema.ts index 05d6166e..fb0db9e1 100644 --- a/packages/runtime/test/typing/schema.ts +++ b/packages/runtime/test/typing/schema.ts @@ -37,6 +37,11 @@ export const schema = { unique: true, attributes: [{ name: "@unique" }] }, + role: { + type: "Role", + attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("USER") }] }], + default: "USER" + }, posts: { type: "Post", array: true, @@ -241,6 +246,12 @@ export const schema = { } } }, + enums: { + Role: { + ADMIN: "ADMIN", + USER: "USER" + } + }, authType: "User", plugins: {} } as const satisfies SchemaDef; diff --git a/packages/runtime/test/typing/typing-test.zmodel b/packages/runtime/test/typing/typing-test.zmodel index 022bc1a9..84e9d9b2 100644 --- a/packages/runtime/test/typing/typing-test.zmodel +++ b/packages/runtime/test/typing/typing-test.zmodel @@ -3,12 +3,18 @@ datasource db { url = "file:./test.db" } +enum Role { + ADMIN + USER +} + model User { id Int @id @default(autoincrement()) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt name String email String @unique + role Role @default(USER) posts Post[] profile Profile? postCount Int @computed diff --git a/packages/runtime/test/typing/verify-typing.ts b/packages/runtime/test/typing/verify-typing.ts index e292d69c..86389616 100644 --- a/packages/runtime/test/typing/verify-typing.ts +++ b/packages/runtime/test/typing/verify-typing.ts @@ -1,6 +1,7 @@ +import SQLite from 'better-sqlite3'; import { ZenStackClient } from '../../dist'; +import { Role } from './models'; import { schema } from './schema'; -import SQLite from 'better-sqlite3'; const client = new ZenStackClient(schema, { dialectConfig: { @@ -26,12 +27,14 @@ async function main() { await aggregate(); await groupBy(); await queryBuilder(); + enums(); } async function find() { const user1 = await client.user.findFirst({ where: { name: 'Alex', + role: Role.USER, }, }); console.log(user1?.name); @@ -144,6 +147,45 @@ async function find() { }, }) ).profile?.region?.city; + + ( + await client.user.findFirstOrThrow({ + select: { + posts: { + where: { title: 'Foo' }, + select: { + author: { + select: { + id: true, + }, + }, + }, + }, + }, + }) + ).posts[0]?.author?.id; + + const u = await client.user.findFirstOrThrow({ + select: { + posts: { + where: { title: 'Foo' }, + select: { + author: { + include: { + profile: true, + }, + omit: { + email: true, + }, + }, + }, + }, + }, + }); + console.log(u.posts[0]?.author?.profile?.age); + console.log(u.posts[0]?.author?.role); + // @ts-expect-error + console.log(u.posts[0]?.author?.email); } async function create() { @@ -562,4 +604,12 @@ async function queryBuilder() { console.log(r.name); } +function enums() { + const a: Role = 'ADMIN'; + console.log(a); + let b = Role.ADMIN; + b = a; + console.log(b); +} + main(); diff --git a/packages/runtime/tsconfig.test.json b/packages/runtime/tsconfig.test.json index 014b54e4..de56945b 100644 --- a/packages/runtime/tsconfig.test.json +++ b/packages/runtime/tsconfig.test.json @@ -2,7 +2,8 @@ "extends": "@zenstackhq/typescript-config/base.json", "compilerOptions": { "noEmit": true, - "noImplicitAny": false + "noImplicitAny": false, + "rootDir": "." }, "include": ["test/**/*.ts"] } diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 85c98311..47d6ca7c 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/sdk", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "description": "ZenStack SDK", "type": "module", "scripts": { diff --git a/packages/sdk/src/ts-schema-generator.ts b/packages/sdk/src/ts-schema-generator.ts index 79c1ec4c..d8c90934 100644 --- a/packages/sdk/src/ts-schema-generator.ts +++ b/packages/sdk/src/ts-schema-generator.ts @@ -42,27 +42,36 @@ import { ModelUtils } from '.'; import { getAttribute, getAuthDecl, hasAttribute, isIdField, isUniqueField } from './model-utils'; export class TsSchemaGenerator { - public async generate(schemaFile: string, pluginModelFiles: string[], outputFile: string) { + public async generate(schemaFile: string, pluginModelFiles: string[], outputDir: string) { const loaded = await loadDocument(schemaFile, pluginModelFiles); if (!loaded.success) { throw new Error(`Error loading schema:${loaded.errors.join('\n')}`); } - const { model, warnings } = loaded; - const statements: ts.Statement[] = []; + const { model } = loaded; - this.generateSchemaStatements(model, statements); + fs.mkdirSync(outputDir, { recursive: true }); + + // the schema itself + this.generateSchema(model, outputDir); + + // the model types + this.generateModels(model, outputDir); + // the input types + this.generateInputTypes(model, outputDir); + } + + private generateSchema(model: Model, outputDir: string) { + const statements: ts.Statement[] = []; + this.generateSchemaStatements(model, statements); this.generateBannerComments(statements); - const sourceFile = ts.createSourceFile(outputFile, '', ts.ScriptTarget.ESNext, false, ts.ScriptKind.TS); + const schemaOutputFile = path.join(outputDir, 'schema.ts'); + const sourceFile = ts.createSourceFile(schemaOutputFile, '', ts.ScriptTarget.ESNext, false, ts.ScriptKind.TS); const printer = ts.createPrinter(); const result = printer.printList(ts.ListFormat.MultiLine, ts.factory.createNodeArray(statements), sourceFile); - - fs.mkdirSync(path.dirname(outputFile), { recursive: true }); - fs.writeFileSync(outputFile, result); - - return { model, warnings }; + fs.writeFileSync(schemaOutputFile, result); } private generateSchemaStatements(model: Model, statements: ts.Statement[]) { @@ -954,4 +963,282 @@ export class TsSchemaGenerator { throw new Error(`Unsupported literal type: ${type}`); }); } + + private generateModels(model: Model, outputDir: string) { + const statements: ts.Statement[] = []; + + // generate: import { schema as $schema, type SchemaType as $Schema } from './schema'; + statements.push(this.generateSchemaTypeImport(true, true)); + + // generate: import type { ModelResult as $ModelResult } from '@zenstackhq/runtime'; + statements.push( + ts.factory.createImportDeclaration( + undefined, + ts.factory.createImportClause( + false, + undefined, + ts.factory.createNamedImports([ + ts.factory.createImportSpecifier( + true, + undefined, + ts.factory.createIdentifier(`ModelResult as $ModelResult`), + ), + ]), + ), + ts.factory.createStringLiteral('@zenstackhq/runtime'), + ), + ); + + const dataModels = model.declarations.filter(isDataModel); + for (const dm of dataModels) { + // generate: export type Model = $ModelResult; + let modelType = ts.factory.createTypeAliasDeclaration( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + dm.name, + undefined, + ts.factory.createTypeReferenceNode('$ModelResult', [ + ts.factory.createTypeReferenceNode('$Schema'), + ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(dm.name)), + ]), + ); + if (dm.comments.length > 0) { + modelType = this.generateDocs(modelType, dm); + } + statements.push(modelType); + } + + // generate enums + const enums = model.declarations.filter(isEnum); + for (const e of enums) { + // generate: export const Enum = $schema.enums.Enum; + let enumDecl = ts.factory.createVariableStatement( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + e.name, + undefined, + undefined, + ts.factory.createPropertyAccessExpression( + ts.factory.createPropertyAccessExpression( + ts.factory.createIdentifier('$schema'), + ts.factory.createIdentifier('enums'), + ), + ts.factory.createIdentifier(e.name), + ), + ), + ], + ts.NodeFlags.Const, + ), + ); + if (e.comments.length > 0) { + enumDecl = this.generateDocs(enumDecl, e); + } + statements.push(enumDecl); + + // generate: export type Enum = (typeof Enum)[keyof typeof Enum]; + let typeAlias = ts.factory.createTypeAliasDeclaration( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + e.name, + undefined, + ts.factory.createIndexedAccessTypeNode( + ts.factory.createTypeQueryNode(ts.factory.createIdentifier(e.name)), + ts.factory.createTypeOperatorNode( + ts.SyntaxKind.KeyOfKeyword, + ts.factory.createTypeQueryNode(ts.factory.createIdentifier(e.name)), + ), + ), + ); + if (e.comments.length > 0) { + typeAlias = this.generateDocs(typeAlias, e); + } + statements.push(typeAlias); + } + + this.generateBannerComments(statements); + + // write to file + const outputFile = path.join(outputDir, 'models.ts'); + const sourceFile = ts.createSourceFile(outputFile, '', ts.ScriptTarget.ESNext, false, ts.ScriptKind.TS); + const printer = ts.createPrinter(); + const result = printer.printList(ts.ListFormat.MultiLine, ts.factory.createNodeArray(statements), sourceFile); + fs.writeFileSync(outputFile, result); + } + + private generateSchemaTypeImport(schemaObject: boolean, schemaType: boolean) { + const importSpecifiers = []; + + if (schemaObject) { + importSpecifiers.push( + ts.factory.createImportSpecifier( + false, + ts.factory.createIdentifier('schema'), + ts.factory.createIdentifier('$schema'), + ), + ); + } + + if (schemaType) { + importSpecifiers.push( + ts.factory.createImportSpecifier( + true, + ts.factory.createIdentifier('SchemaType'), + ts.factory.createIdentifier('$Schema'), + ), + ); + } + + return ts.factory.createImportDeclaration( + undefined, + ts.factory.createImportClause(false, undefined, ts.factory.createNamedImports(importSpecifiers)), + ts.factory.createStringLiteral('./schema'), + ); + } + + private generateDocs( + tsDecl: T, + decl: DataModel | Enum, + ): T { + return ts.addSyntheticLeadingComment( + tsDecl, + ts.SyntaxKind.MultiLineCommentTrivia, + `*\n * ${decl.comments.map((c) => c.replace(/^\s*\/*\s*/, '')).join('\n * ')}\n `, + true, + ); + } + + private generateInputTypes(model: Model, outputDir: string) { + const dataModels = model.declarations.filter(isDataModel); + const statements: ts.Statement[] = []; + + // generate: import { SchemaType as $Schema } from './schema'; + statements.push(this.generateSchemaTypeImport(false, true)); + + // generate: import { CreateArgs as $CreateArgs, ... } from '@zenstackhq/runtime'; + const inputTypes = [ + 'FindManyArgs', + 'FindUniqueArgs', + 'FindFirstArgs', + 'CreateArgs', + 'CreateManyArgs', + 'CreateManyAndReturnArgs', + 'UpdateArgs', + 'UpdateManyArgs', + 'UpdateManyAndReturnArgs', + 'UpsertArgs', + 'DeleteArgs', + 'DeleteManyArgs', + 'CountArgs', + 'AggregateArgs', + 'GroupByArgs', + 'WhereInput', + 'SelectInput', + 'IncludeInput', + 'OmitInput', + ]; + + const inputTypeNameFixes = { + SelectInput: 'Select', + IncludeInput: 'Include', + OmitInput: 'Omit', + }; + + // generate: import { CreateArgs as $CreateArgs, ... } from '@zenstackhq/runtime'; + statements.push( + ts.factory.createImportDeclaration( + undefined, + ts.factory.createImportClause( + true, + undefined, + ts.factory.createNamedImports( + inputTypes.map((inputType) => + ts.factory.createImportSpecifier( + false, + undefined, + ts.factory.createIdentifier(`${inputType} as $${inputType}`), + ), + ), + ), + ), + ts.factory.createStringLiteral('@zenstackhq/runtime'), + ), + ); + + // generate: import { type SelectIncludeOmit as $SelectIncludeOmit, type SimplifiedModelResult as $SimplifiedModelResult } from '@zenstackhq/runtime'; + statements.push( + ts.factory.createImportDeclaration( + undefined, + ts.factory.createImportClause( + true, + undefined, + ts.factory.createNamedImports([ + ts.factory.createImportSpecifier( + false, + undefined, + ts.factory.createIdentifier('SimplifiedModelResult as $SimplifiedModelResult'), + ), + ts.factory.createImportSpecifier( + false, + undefined, + ts.factory.createIdentifier('SelectIncludeOmit as $SelectIncludeOmit'), + ), + ]), + ), + ts.factory.createStringLiteral('@zenstackhq/runtime'), + ), + ); + + for (const dm of dataModels) { + // generate: export type ModelCreateArgs = $CreateArgs; + for (const inputType of inputTypes) { + const exportName = inputTypeNameFixes[inputType as keyof typeof inputTypeNameFixes] + ? `${dm.name}${inputTypeNameFixes[inputType as keyof typeof inputTypeNameFixes]}` + : `${dm.name}${inputType}`; + statements.push( + ts.factory.createTypeAliasDeclaration( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + exportName, + undefined, + ts.factory.createTypeReferenceNode(`$${inputType}`, [ + ts.factory.createTypeReferenceNode('$Schema'), + ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(dm.name)), + ]), + ), + ); + } + + // generate: export type ModelGetPayload> = $SimplifiedModelResult; + statements.push( + ts.factory.createTypeAliasDeclaration( + [ts.factory.createModifier(ts.SyntaxKind.ExportKeyword)], + `${dm.name}GetPayload`, + [ + ts.factory.createTypeParameterDeclaration( + undefined, + 'Args', + ts.factory.createTypeReferenceNode('$SelectIncludeOmit', [ + ts.factory.createTypeReferenceNode('$Schema'), + ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(dm.name)), + ts.factory.createLiteralTypeNode(ts.factory.createTrue()), + ]), + ), + ], + ts.factory.createTypeReferenceNode('$SimplifiedModelResult', [ + ts.factory.createTypeReferenceNode('$Schema'), + ts.factory.createLiteralTypeNode(ts.factory.createStringLiteral(dm.name)), + ts.factory.createTypeReferenceNode('Args'), + ]), + ), + ); + } + + this.generateBannerComments(statements); + + // write to file + const outputFile = path.join(outputDir, 'input.ts'); + const sourceFile = ts.createSourceFile(outputFile, '', ts.ScriptTarget.ESNext, false, ts.ScriptKind.TS); + const printer = ts.createPrinter(); + const result = printer.printList(ts.ListFormat.MultiLine, ts.factory.createNodeArray(statements), sourceFile); + fs.writeFileSync(outputFile, result); + } } diff --git a/packages/tanstack-query/package.json b/packages/tanstack-query/package.json index fc632591..395b39a5 100644 --- a/packages/tanstack-query/package.json +++ b/packages/tanstack-query/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/tanstack-query", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "description": "", "main": "index.js", "type": "module", diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 9334d684..4c9c95a8 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/testtools", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "description": "ZenStack Test Tools", "type": "module", "scripts": { diff --git a/packages/testtools/src/schema.ts b/packages/testtools/src/schema.ts index b5a6f7d3..fb02835c 100644 --- a/packages/testtools/src/schema.ts +++ b/packages/testtools/src/schema.ts @@ -44,8 +44,7 @@ export async function generateTsSchema( const pluginModelFiles = glob.sync(path.resolve(__dirname, '../../runtime/src/plugins/**/plugin.zmodel')); const generator = new TsSchemaGenerator(); - const tsPath = path.join(workDir, 'schema.ts'); - await generator.generate(zmodelPath, pluginModelFiles, tsPath); + await generator.generate(zmodelPath, pluginModelFiles, workDir); if (extraSourceFiles) { for (const [fileName, content] of Object.entries(extraSourceFiles)) { diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index b52c75f6..293a7af0 100644 --- a/packages/typescript-config/package.json +++ b/packages/typescript-config/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/typescript-config", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "private": true, "license": "MIT" } diff --git a/packages/zod/package.json b/packages/zod/package.json index adf86881..a46ae4bc 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/zod", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "description": "", "type": "module", "main": "index.js", diff --git a/samples/blog/package.json b/samples/blog/package.json index 16476949..3b462ced 100644 --- a/samples/blog/package.json +++ b/samples/blog/package.json @@ -1,6 +1,6 @@ { "name": "sample-blog", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "description": "", "main": "index.js", "scripts": { diff --git a/samples/blog/zenstack/input.ts b/samples/blog/zenstack/input.ts new file mode 100644 index 00000000..c6e7433e --- /dev/null +++ b/samples/blog/zenstack/input.ts @@ -0,0 +1,70 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// + +/* eslint-disable */ + +import { type SchemaType as $Schema } from "./schema"; +import type { FindManyArgs as $FindManyArgs, FindUniqueArgs as $FindUniqueArgs, FindFirstArgs as $FindFirstArgs, CreateArgs as $CreateArgs, CreateManyArgs as $CreateManyArgs, CreateManyAndReturnArgs as $CreateManyAndReturnArgs, UpdateArgs as $UpdateArgs, UpdateManyArgs as $UpdateManyArgs, UpdateManyAndReturnArgs as $UpdateManyAndReturnArgs, UpsertArgs as $UpsertArgs, DeleteArgs as $DeleteArgs, DeleteManyArgs as $DeleteManyArgs, CountArgs as $CountArgs, AggregateArgs as $AggregateArgs, GroupByArgs as $GroupByArgs, WhereInput as $WhereInput, SelectInput as $SelectInput, IncludeInput as $IncludeInput, OmitInput as $OmitInput } from "@zenstackhq/runtime"; +import type { SimplifiedModelResult as $SimplifiedModelResult, SelectIncludeOmit as $SelectIncludeOmit } from "@zenstackhq/runtime"; +export type UserFindManyArgs = $FindManyArgs<$Schema, "User">; +export type UserFindUniqueArgs = $FindUniqueArgs<$Schema, "User">; +export type UserFindFirstArgs = $FindFirstArgs<$Schema, "User">; +export type UserCreateArgs = $CreateArgs<$Schema, "User">; +export type UserCreateManyArgs = $CreateManyArgs<$Schema, "User">; +export type UserCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "User">; +export type UserUpdateArgs = $UpdateArgs<$Schema, "User">; +export type UserUpdateManyArgs = $UpdateManyArgs<$Schema, "User">; +export type UserUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "User">; +export type UserUpsertArgs = $UpsertArgs<$Schema, "User">; +export type UserDeleteArgs = $DeleteArgs<$Schema, "User">; +export type UserDeleteManyArgs = $DeleteManyArgs<$Schema, "User">; +export type UserCountArgs = $CountArgs<$Schema, "User">; +export type UserAggregateArgs = $AggregateArgs<$Schema, "User">; +export type UserGroupByArgs = $GroupByArgs<$Schema, "User">; +export type UserWhereInput = $WhereInput<$Schema, "User">; +export type UserSelect = $SelectInput<$Schema, "User">; +export type UserInclude = $IncludeInput<$Schema, "User">; +export type UserOmit = $OmitInput<$Schema, "User">; +export type UserGetPayload> = $SimplifiedModelResult<$Schema, "User", Args>; +export type ProfileFindManyArgs = $FindManyArgs<$Schema, "Profile">; +export type ProfileFindUniqueArgs = $FindUniqueArgs<$Schema, "Profile">; +export type ProfileFindFirstArgs = $FindFirstArgs<$Schema, "Profile">; +export type ProfileCreateArgs = $CreateArgs<$Schema, "Profile">; +export type ProfileCreateManyArgs = $CreateManyArgs<$Schema, "Profile">; +export type ProfileCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Profile">; +export type ProfileUpdateArgs = $UpdateArgs<$Schema, "Profile">; +export type ProfileUpdateManyArgs = $UpdateManyArgs<$Schema, "Profile">; +export type ProfileUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Profile">; +export type ProfileUpsertArgs = $UpsertArgs<$Schema, "Profile">; +export type ProfileDeleteArgs = $DeleteArgs<$Schema, "Profile">; +export type ProfileDeleteManyArgs = $DeleteManyArgs<$Schema, "Profile">; +export type ProfileCountArgs = $CountArgs<$Schema, "Profile">; +export type ProfileAggregateArgs = $AggregateArgs<$Schema, "Profile">; +export type ProfileGroupByArgs = $GroupByArgs<$Schema, "Profile">; +export type ProfileWhereInput = $WhereInput<$Schema, "Profile">; +export type ProfileSelect = $SelectInput<$Schema, "Profile">; +export type ProfileInclude = $IncludeInput<$Schema, "Profile">; +export type ProfileOmit = $OmitInput<$Schema, "Profile">; +export type ProfileGetPayload> = $SimplifiedModelResult<$Schema, "Profile", Args>; +export type PostFindManyArgs = $FindManyArgs<$Schema, "Post">; +export type PostFindUniqueArgs = $FindUniqueArgs<$Schema, "Post">; +export type PostFindFirstArgs = $FindFirstArgs<$Schema, "Post">; +export type PostCreateArgs = $CreateArgs<$Schema, "Post">; +export type PostCreateManyArgs = $CreateManyArgs<$Schema, "Post">; +export type PostCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Post">; +export type PostUpdateArgs = $UpdateArgs<$Schema, "Post">; +export type PostUpdateManyArgs = $UpdateManyArgs<$Schema, "Post">; +export type PostUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Post">; +export type PostUpsertArgs = $UpsertArgs<$Schema, "Post">; +export type PostDeleteArgs = $DeleteArgs<$Schema, "Post">; +export type PostDeleteManyArgs = $DeleteManyArgs<$Schema, "Post">; +export type PostCountArgs = $CountArgs<$Schema, "Post">; +export type PostAggregateArgs = $AggregateArgs<$Schema, "Post">; +export type PostGroupByArgs = $GroupByArgs<$Schema, "Post">; +export type PostWhereInput = $WhereInput<$Schema, "Post">; +export type PostSelect = $SelectInput<$Schema, "Post">; +export type PostInclude = $IncludeInput<$Schema, "Post">; +export type PostOmit = $OmitInput<$Schema, "Post">; +export type PostGetPayload> = $SimplifiedModelResult<$Schema, "Post", Args>; diff --git a/samples/blog/zenstack/models.ts b/samples/blog/zenstack/models.ts new file mode 100644 index 00000000..a55d9275 --- /dev/null +++ b/samples/blog/zenstack/models.ts @@ -0,0 +1,31 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// + +/* eslint-disable */ + +import { schema as $schema, type SchemaType as $Schema } from "./schema"; +import { type ModelResult as $ModelResult } from "@zenstackhq/runtime"; +/** + * User model + * + * Represents a user of the blog. + */ +export type User = $ModelResult<$Schema, "User">; +/** + * Profile model + */ +export type Profile = $ModelResult<$Schema, "Profile">; +/** + * Post model + */ +export type Post = $ModelResult<$Schema, "Post">; +/** + * User roles + */ +export const Role = $schema.enums.Role; +/** + * User roles + */ +export type Role = (typeof Role)[keyof typeof Role]; diff --git a/samples/blog/zenstack/schema.zmodel b/samples/blog/zenstack/schema.zmodel index c9cb02ee..bc4d3ed3 100644 --- a/samples/blog/zenstack/schema.zmodel +++ b/samples/blog/zenstack/schema.zmodel @@ -3,11 +3,15 @@ datasource db { url = 'file:./dev.db' } +/// User roles enum Role { ADMIN USER } +/// User model +/// +/// Represents a user of the blog. model User { id String @id @default(cuid()) createdAt DateTime @default(now()) @@ -20,6 +24,7 @@ model User { profile Profile? } +/// Profile model model Profile { id String @id @default(cuid()) bio String? @@ -28,6 +33,7 @@ model Profile { userId String? @unique } +/// Post model model Post { id String @id @default(cuid()) createdAt DateTime @default(now()) diff --git a/tests/e2e/package.json b/tests/e2e/package.json index ffb11c89..6798c745 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "e2e", - "version": "3.0.0-alpha.10", + "version": "3.0.0-alpha.11", "private": true, "scripts": { "test": "vitest run"