From 3faa2cffb537905f5a142de3dd164527739fe609 Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Thu, 21 Aug 2025 13:30:50 +0800 Subject: [PATCH 1/7] fix: output path handling for prisma plugin (#194) --- packages/cli/src/plugins/prisma.ts | 12 ++++++------ packages/cli/test/plugins/prisma-plugin.test.ts | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/plugins/prisma.ts b/packages/cli/src/plugins/prisma.ts index b471ec36..1ff60780 100644 --- a/packages/cli/src/plugins/prisma.ts +++ b/packages/cli/src/plugins/prisma.ts @@ -5,16 +5,16 @@ import path from 'node:path'; const plugin: CliPlugin = { name: 'Prisma Schema Generator', statusText: 'Generating Prisma schema', - async generate({ model, defaultOutputPath, pluginOptions }) { - let outDir = defaultOutputPath; + async generate({ model, schemaFile, defaultOutputPath, pluginOptions }) { + let outFile = path.join(defaultOutputPath, 'schema.prisma'); if (typeof pluginOptions['output'] === 'string') { - outDir = path.resolve(defaultOutputPath, pluginOptions['output']); - if (!fs.existsSync(outDir)) { - fs.mkdirSync(outDir, { recursive: true }); + outFile = path.resolve(path.dirname(schemaFile), pluginOptions['output']); + if (!fs.existsSync(path.dirname(outFile))) { + fs.mkdirSync(path.dirname(outFile), { recursive: true }); } } const prismaSchema = await new PrismaSchemaGenerator(model).generate(); - fs.writeFileSync(path.join(outDir, 'schema.prisma'), prismaSchema); + fs.writeFileSync(outFile, prismaSchema); }, }; diff --git a/packages/cli/test/plugins/prisma-plugin.test.ts b/packages/cli/test/plugins/prisma-plugin.test.ts index 06f252cb..23cb59f3 100644 --- a/packages/cli/test/plugins/prisma-plugin.test.ts +++ b/packages/cli/test/plugins/prisma-plugin.test.ts @@ -47,7 +47,7 @@ model User { const workDir = createProject(` plugin prisma { provider = '@core/prisma' - output = './prisma' + output = '../prisma/schema.prisma' } model User { @@ -55,6 +55,6 @@ model User { } `); runCli('generate', workDir); - expect(fs.existsSync(path.join(workDir, 'zenstack/prisma/schema.prisma'))).toBe(true); + expect(fs.existsSync(path.join(workDir, 'prisma/schema.prisma'))).toBe(true); }); }); From 8e7fa11a22269944dde03b3678a9340d7016c965 Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Thu, 21 Aug 2025 16:16:30 +0800 Subject: [PATCH 2/7] fix: crud operation typing (#195) --- packages/runtime/src/client/crud-types.ts | 15 +++++++-------- packages/runtime/test/schemas/typing/models.ts | 2 ++ packages/runtime/test/schemas/typing/schema.ts | 12 +++++++++++- .../runtime/test/schemas/typing/schema.zmodel | 11 +++++++++-- packages/runtime/test/schemas/typing/typecheck.ts | 5 ++++- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/packages/runtime/src/client/crud-types.ts b/packages/runtime/src/client/crud-types.ts index fd1918df..85cf3b56 100644 --- a/packages/runtime/src/client/crud-types.ts +++ b/packages/runtime/src/client/crud-types.ts @@ -7,7 +7,6 @@ import type { FieldIsDelegateDiscriminator, FieldIsDelegateRelation, FieldIsRelation, - FieldIsRelationArray, FieldType, ForeignKeyFields, GetEnum, @@ -218,11 +217,11 @@ export type WhereInput< : Key]?: Key extends RelationFields ? // relation RelationFilter - : // enum - GetModelFieldType extends GetEnums - ? EnumFilter, ModelFieldIsOptional> - : FieldIsArray extends true - ? ArrayFilter> + : FieldIsArray extends true + ? ArrayFilter> + : // enum + GetModelFieldType extends GetEnums + ? EnumFilter, ModelFieldIsOptional> : // primitive PrimitiveFilter< Schema, @@ -561,9 +560,9 @@ type OptionalFieldsForCreate extends true ? Key - : GetModelField['updatedAt'] extends true + : FieldIsArray extends true ? Key - : FieldIsRelationArray extends true + : GetModelField['updatedAt'] extends true ? Key : never]: GetModelField; }; diff --git a/packages/runtime/test/schemas/typing/models.ts b/packages/runtime/test/schemas/typing/models.ts index a30c2d6e..d5cb870c 100644 --- a/packages/runtime/test/schemas/typing/models.ts +++ b/packages/runtime/test/schemas/typing/models.ts @@ -17,3 +17,5 @@ export type Identity = $TypeDefResult<$Schema, "Identity">; export type IdentityProvider = $TypeDefResult<$Schema, "IdentityProvider">; export const Role = $schema.enums.Role; export type Role = (typeof Role)[keyof typeof Role]; +export const Status = $schema.enums.Status; +export type Status = (typeof Status)[keyof typeof Status]; diff --git a/packages/runtime/test/schemas/typing/schema.ts b/packages/runtime/test/schemas/typing/schema.ts index 2b331e0b..90a532e0 100644 --- a/packages/runtime/test/schemas/typing/schema.ts +++ b/packages/runtime/test/schemas/typing/schema.ts @@ -8,7 +8,7 @@ import { type SchemaDef, type OperandExpression, ExpressionUtils } from "../../../dist/schema"; export const schema = { provider: { - type: "sqlite" + type: "postgresql" }, models: { User: { @@ -49,6 +49,11 @@ export const schema = { attributes: [{ name: "@default", args: [{ name: "value", value: ExpressionUtils.literal("USER") }] }], default: "USER" }, + status: { + name: "status", + type: "Status", + array: true + }, posts: { name: "posts", type: "Post", @@ -325,6 +330,11 @@ export const schema = { Role: { ADMIN: "ADMIN", USER: "USER" + }, + Status: { + ACTIVE: "ACTIVE", + INACTIVE: "INACTIVE", + BANNED: "BANNED" } }, authType: "User", diff --git a/packages/runtime/test/schemas/typing/schema.zmodel b/packages/runtime/test/schemas/typing/schema.zmodel index 2cb789d7..32209ceb 100644 --- a/packages/runtime/test/schemas/typing/schema.zmodel +++ b/packages/runtime/test/schemas/typing/schema.zmodel @@ -1,6 +1,6 @@ datasource db { - provider = "sqlite" - url = "file:./test.db" + provider = 'postgresql' + url = env('DATABASE_URL') } enum Role { @@ -8,6 +8,12 @@ enum Role { USER } +enum Status { + ACTIVE + INACTIVE + BANNED +} + type Identity { providers IdentityProvider[] } @@ -24,6 +30,7 @@ model User { name String email String @unique role Role @default(USER) + status Status[] posts Post[] profile Profile? postCount Int @computed diff --git a/packages/runtime/test/schemas/typing/typecheck.ts b/packages/runtime/test/schemas/typing/typecheck.ts index c7d0cdb7..189d1c97 100644 --- a/packages/runtime/test/schemas/typing/typecheck.ts +++ b/packages/runtime/test/schemas/typing/typecheck.ts @@ -1,7 +1,7 @@ import SQLite from 'better-sqlite3'; import { SqliteDialect } from 'kysely'; import { ZenStackClient } from '../../../dist'; -import { Role, type Identity, type IdentityProvider } from './models'; +import { Role, Status, type Identity, type IdentityProvider } from './models'; import { schema } from './schema'; const client = new ZenStackClient(schema, { @@ -35,6 +35,9 @@ async function find() { where: { name: 'Alex', role: Role.USER, + status: { + has: Status.ACTIVE, + }, }, }); console.log(user1?.name); From f3e8d072f897d8d57483f947d1f8633cc6d51096 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 18:23:22 +0800 Subject: [PATCH 3/7] chore: bump version 3.0.0-alpha.30 (#196) Co-authored-by: ymc9 <104139426+ymc9@users.noreply.github.com> --- package.json | 2 +- packages/cli/package.json | 2 +- packages/common-helpers/package.json | 2 +- packages/create-zenstack/package.json | 2 +- packages/dialects/sql.js/package.json | 2 +- packages/eslint-config/package.json | 2 +- packages/ide/vscode/package.json | 2 +- packages/language/package.json | 2 +- packages/runtime/package.json | 2 +- packages/sdk/package.json | 2 +- packages/tanstack-query/package.json | 2 +- packages/testtools/package.json | 2 +- packages/typescript-config/package.json | 2 +- packages/vitest-config/package.json | 2 +- packages/zod/package.json | 2 +- samples/blog/package.json | 2 +- tests/e2e/package.json | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 71298a73..34f12bc5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zenstack-v3", - "version": "3.0.0-alpha.29", + "version": "3.0.0-alpha.30", "description": "ZenStack", "packageManager": "pnpm@10.12.1", "scripts": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 3d7f9d1c..9c620c3b 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.29", + "version": "3.0.0-alpha.30", "type": "module", "author": { "name": "ZenStack Team" diff --git a/packages/common-helpers/package.json b/packages/common-helpers/package.json index a0b8aeb5..ad3d08b5 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.29", + "version": "3.0.0-alpha.30", "description": "ZenStack Common Helpers", "type": "module", "scripts": { diff --git a/packages/create-zenstack/package.json b/packages/create-zenstack/package.json index 9021b8b9..fce02df8 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.29", + "version": "3.0.0-alpha.30", "description": "Create a new ZenStack project", "type": "module", "scripts": { diff --git a/packages/dialects/sql.js/package.json b/packages/dialects/sql.js/package.json index 898a2341..7dcdfcb6 100644 --- a/packages/dialects/sql.js/package.json +++ b/packages/dialects/sql.js/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/kysely-sql-js", - "version": "3.0.0-alpha.29", + "version": "3.0.0-alpha.30", "description": "Kysely dialect for sql.js", "type": "module", "scripts": { diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 0518a6f9..c4a9019e 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.29", + "version": "3.0.0-alpha.30", "type": "module", "private": true, "license": "MIT" diff --git a/packages/ide/vscode/package.json b/packages/ide/vscode/package.json index cfb4d730..a2b63079 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.29", + "version": "3.0.0-alpha.30", "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 0c2458f0..9afa35a7 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.29", + "version": "3.0.0-alpha.30", "license": "MIT", "author": "ZenStack Team", "files": [ diff --git a/packages/runtime/package.json b/packages/runtime/package.json index d6a694d2..b00d326a 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/runtime", - "version": "3.0.0-alpha.29", + "version": "3.0.0-alpha.30", "description": "ZenStack Runtime", "type": "module", "scripts": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 400c0cac..a5b08eda 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/sdk", - "version": "3.0.0-alpha.29", + "version": "3.0.0-alpha.30", "description": "ZenStack SDK", "type": "module", "scripts": { diff --git a/packages/tanstack-query/package.json b/packages/tanstack-query/package.json index 789a8fc1..2530d1e6 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.29", + "version": "3.0.0-alpha.30", "description": "", "main": "index.js", "type": "module", diff --git a/packages/testtools/package.json b/packages/testtools/package.json index 4bfa24a9..4a4c8d50 100644 --- a/packages/testtools/package.json +++ b/packages/testtools/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/testtools", - "version": "3.0.0-alpha.29", + "version": "3.0.0-alpha.30", "description": "ZenStack Test Tools", "type": "module", "scripts": { diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index 58de9be4..f3e1dc28 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.29", + "version": "3.0.0-alpha.30", "private": true, "license": "MIT" } diff --git a/packages/vitest-config/package.json b/packages/vitest-config/package.json index 0c739b29..c7c8335d 100644 --- a/packages/vitest-config/package.json +++ b/packages/vitest-config/package.json @@ -1,7 +1,7 @@ { "name": "@zenstackhq/vitest-config", "type": "module", - "version": "3.0.0-alpha.29", + "version": "3.0.0-alpha.30", "private": true, "license": "MIT", "exports": { diff --git a/packages/zod/package.json b/packages/zod/package.json index 376c279a..b6ff5943 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -1,6 +1,6 @@ { "name": "@zenstackhq/zod", - "version": "3.0.0-alpha.29", + "version": "3.0.0-alpha.30", "description": "", "type": "module", "main": "index.js", diff --git a/samples/blog/package.json b/samples/blog/package.json index a298cd96..999c3597 100644 --- a/samples/blog/package.json +++ b/samples/blog/package.json @@ -1,6 +1,6 @@ { "name": "sample-blog", - "version": "3.0.0-alpha.29", + "version": "3.0.0-alpha.30", "description": "", "main": "index.js", "scripts": { diff --git a/tests/e2e/package.json b/tests/e2e/package.json index 43651930..80e79926 100644 --- a/tests/e2e/package.json +++ b/tests/e2e/package.json @@ -1,6 +1,6 @@ { "name": "e2e", - "version": "3.0.0-alpha.29", + "version": "3.0.0-alpha.30", "private": true, "type": "module", "scripts": { From ab36b05676a3418b9fe5a4463e8a8db6e884bf43 Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Thu, 21 Aug 2025 20:17:15 +0800 Subject: [PATCH 4/7] chore: minor fixes to crud types (#197) * chore: minor fixes to crud types * update --- packages/runtime/src/client/crud-types.ts | 10 +++++----- pnpm-lock.yaml | 4 ++-- pnpm-workspace.yaml | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/runtime/src/client/crud-types.ts b/packages/runtime/src/client/crud-types.ts index 85cf3b56..91011428 100644 --- a/packages/runtime/src/client/crud-types.ts +++ b/packages/runtime/src/client/crud-types.ts @@ -427,7 +427,7 @@ export type WhereUniqueInput; export type OmitInput> = { - [Key in NonRelationFields]?: true; + [Key in NonRelationFields]?: boolean; }; export type SelectIncludeOmit, AllowCount extends boolean> = { @@ -442,7 +442,7 @@ export type SelectInput< AllowCount extends boolean = true, AllowRelation extends boolean = true, > = { - [Key in NonRelationFields]?: true; + [Key in NonRelationFields]?: boolean; } & (AllowRelation extends true ? IncludeInput : {}) & // relation fields // relation count (AllowCount extends true @@ -453,11 +453,11 @@ export type SelectInput< : {}); type SelectCount> = - | true + | boolean | { select: { - [Key in RelationFields as FieldIsArray extends true ? Key : never]: - | true + [Key in RelationFields as FieldIsArray extends true ? Key : never]?: + | boolean | { where: WhereInput, false>; }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7463216..42a9470e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,7 +22,7 @@ catalogs: specifier: 3.5.0 version: 3.5.0 prisma: - specifier: ^6.14.0 + specifier: ^6.10.0 version: 6.14.0 tmp: specifier: ^0.2.3 @@ -31,7 +31,7 @@ catalogs: specifier: ^5.7.1 version: 5.7.1 typescript: - specifier: ^5.0.0 + specifier: ^5.8.0 version: 5.8.3 zod: specifier: ^4.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 4b745954..80826124 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -5,11 +5,11 @@ packages: catalog: kysely: ^0.27.6 zod: ^4.0.0 - prisma: ^6.14.0 + prisma: ^6.10.0 langium: 3.5.0 langium-cli: 3.5.0 ts-pattern: ^5.7.1 - typescript: ^5.0.0 + typescript: ^5.8.0 '@types/node': ^20.17.24 tmp: ^0.2.3 '@types/tmp': ^0.2.6 From 5ff4ad0f3541dd5e03f61393ec4d700cc37459c4 Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Thu, 21 Aug 2025 22:44:32 +0800 Subject: [PATCH 5/7] chore: minor typing and export fixes (#199) --- packages/runtime/src/client/crud-types.ts | 4 ++-- packages/runtime/src/client/index.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/runtime/src/client/crud-types.ts b/packages/runtime/src/client/crud-types.ts index 91011428..c2696069 100644 --- a/packages/runtime/src/client/crud-types.ts +++ b/packages/runtime/src/client/crud-types.ts @@ -693,7 +693,7 @@ type ScalarCreatePayload< | MapModelFieldType | (FieldIsArray extends true ? { - set?: MapModelFieldType[]; + set?: MapModelFieldType; } : never); @@ -1203,7 +1203,7 @@ type NestedUpsertInput< Field extends RelationFields, > = OrArray< { - where: WhereUniqueInput; + where: WhereUniqueInput>; create: CreateInput< Schema, RelationFieldType, diff --git a/packages/runtime/src/client/index.ts b/packages/runtime/src/client/index.ts index bafc558e..aaf50754 100644 --- a/packages/runtime/src/client/index.ts +++ b/packages/runtime/src/client/index.ts @@ -4,4 +4,6 @@ export type * from './crud-types'; export * from './errors'; export type { ClientOptions } from './options'; export { definePlugin } from './plugin'; +export type { ZenStackPromise } from './promise'; export type { ToKysely } from './query-builder'; +export { sql } from 'kysely'; From 378a6b5a18af60eff88667e690730475d6699c11 Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Fri, 22 Aug 2025 09:08:42 +0800 Subject: [PATCH 6/7] fix: enum array filter typing (#200) --- packages/runtime/src/client/crud-types.ts | 16 ++++++++++------ .../runtime/test/schemas/typing/typecheck.ts | 11 +++++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/runtime/src/client/crud-types.ts b/packages/runtime/src/client/crud-types.ts index c2696069..96c57be5 100644 --- a/packages/runtime/src/client/crud-types.ts +++ b/packages/runtime/src/client/crud-types.ts @@ -218,7 +218,7 @@ export type WhereInput< ? // relation RelationFilter : FieldIsArray extends true - ? ArrayFilter> + ? ArrayFilter> : // enum GetModelFieldType extends GetEnums ? EnumFilter, ModelFieldIsOptional> @@ -246,14 +246,18 @@ type EnumFilter, Nullable e not?: EnumFilter; }; -type ArrayFilter = { - equals?: MapBaseType[]; - has?: MapBaseType; - hasEvery?: MapBaseType[]; - hasSome?: MapBaseType[]; +type ArrayFilter = { + equals?: MapScalarType[]; + has?: MapScalarType; + hasEvery?: MapScalarType[]; + hasSome?: MapScalarType[]; isEmpty?: boolean; }; +// map a scalar type (primitive and enum) to TS type +type MapScalarType = + T extends GetEnums ? keyof GetEnum : MapBaseType; + type PrimitiveFilter< Schema extends SchemaDef, T extends string, diff --git a/packages/runtime/test/schemas/typing/typecheck.ts b/packages/runtime/test/schemas/typing/typecheck.ts index 189d1c97..94961400 100644 --- a/packages/runtime/test/schemas/typing/typecheck.ts +++ b/packages/runtime/test/schemas/typing/typecheck.ts @@ -80,6 +80,17 @@ async function find() { where: { name: 'Alex' }, }); + // enum array + await client.user.findFirst({ + where: { status: { equals: [Status.ACTIVE] } }, + }); + await client.user.findFirst({ + where: { status: { has: Status.ACTIVE } }, + }); + await client.user.findFirst({ + where: { status: { hasEvery: [Status.ACTIVE] } }, + }); + await client.user.findMany({ skip: 1, take: 1, From b9bcbf8cb83b8568775f5ee4af56bfd7d6e122a0 Mon Sep 17 00:00:00 2001 From: Yiming Cao Date: Fri, 22 Aug 2025 09:51:44 +0800 Subject: [PATCH 7/7] chore: use zod v3 for dev dependency (#201) * chore: use zod v3 for dev dependency * update --- packages/runtime/package.json | 3 +- packages/runtime/src/client/crud-types.ts | 4 +- packages/runtime/src/client/crud/validator.ts | 50 ++++++++++--------- .../runtime/test/client-api/mixin.test.ts | 2 +- .../test/client-api/typed-json-fields.test.ts | 4 +- packages/zod/package.json | 3 +- packages/zod/src/index.ts | 2 +- pnpm-lock.yaml | 21 ++++---- pnpm-workspace.yaml | 2 +- 9 files changed, 46 insertions(+), 45 deletions(-) diff --git a/packages/runtime/package.json b/packages/runtime/package.json index b00d326a..ba09c9a8 100644 --- a/packages/runtime/package.json +++ b/packages/runtime/package.json @@ -99,6 +99,7 @@ "@zenstackhq/testtools": "workspace:*", "@zenstackhq/typescript-config": "workspace:*", "@zenstackhq/vitest-config": "workspace:*", - "tsx": "^4.19.2" + "tsx": "^4.19.2", + "zod": "~3.25.0" } } diff --git a/packages/runtime/src/client/crud-types.ts b/packages/runtime/src/client/crud-types.ts index 96c57be5..73e5ed31 100644 --- a/packages/runtime/src/client/crud-types.ts +++ b/packages/runtime/src/client/crud-types.ts @@ -247,8 +247,8 @@ type EnumFilter, Nullable e }; type ArrayFilter = { - equals?: MapScalarType[]; - has?: MapScalarType; + equals?: MapScalarType[] | null; + has?: MapScalarType | null; hasEvery?: MapScalarType[]; hasSome?: MapScalarType[]; isEmpty?: boolean; diff --git a/packages/runtime/src/client/crud/validator.ts b/packages/runtime/src/client/crud/validator.ts index eb9ecf21..6d5f7ebf 100644 --- a/packages/runtime/src/client/crud/validator.ts +++ b/packages/runtime/src/client/crud/validator.ts @@ -233,10 +233,10 @@ export class InputValidator { } else { return match(type) .with('String', () => z.string()) - .with('Int', () => z.int()) + .with('Int', () => z.number().int()) .with('Float', () => z.number()) .with('Boolean', () => z.boolean()) - .with('BigInt', () => z.union([z.int(), z.bigint()])) + .with('BigInt', () => z.union([z.number().int(), z.bigint()])) .with('Decimal', () => z.union([z.number(), z.instanceof(Decimal), z.string()])) .with('DateTime', () => z.union([z.date(), z.string().datetime()])) .with('Bytes', () => z.instanceof(Uint8Array)) @@ -252,20 +252,22 @@ export class InputValidator { } const typeDef = this.schema.typeDefs?.[type]; invariant(typeDef, `Type definition "${type}" not found in schema`); - schema = z.looseObject( - Object.fromEntries( - Object.entries(typeDef.fields).map(([field, def]) => { - let fieldSchema = this.makePrimitiveSchema(def.type); - if (def.array) { - fieldSchema = fieldSchema.array(); - } - if (def.optional) { - fieldSchema = fieldSchema.optional(); - } - return [field, fieldSchema]; - }), - ), - ); + schema = z + .object( + Object.fromEntries( + Object.entries(typeDef.fields).map(([field, def]) => { + let fieldSchema = this.makePrimitiveSchema(def.type); + if (def.array) { + fieldSchema = fieldSchema.array(); + } + if (def.optional) { + fieldSchema = fieldSchema.optional(); + } + return [field, fieldSchema]; + }), + ), + ) + .passthrough(); this.schemaCache.set(key, schema); return schema; } @@ -469,7 +471,7 @@ export class InputValidator { private makeDateTimeFilterSchema(optional: boolean, withAggregations: boolean): ZodType { return this.makeCommonPrimitiveFilterSchema( - z.union([z.iso.datetime(), z.date()]), + z.union([z.string().datetime(), z.date()]), optional, () => z.lazy(() => this.makeDateTimeFilterSchema(optional, withAggregations)), withAggregations ? ['_count', '_min', '_max'] : undefined, @@ -519,7 +521,7 @@ export class InputValidator { gte: baseSchema.optional(), not: makeThis().optional(), ...(withAggregations?.includes('_count') - ? { _count: this.makeNumberFilterSchema(z.int(), false, false).optional() } + ? { _count: this.makeNumberFilterSchema(z.number().int(), false, false).optional() } : {}), ...(withAggregations?.includes('_avg') ? { _avg: commonAggSchema() } : {}), ...(withAggregations?.includes('_sum') ? { _sum: commonAggSchema() } : {}), @@ -1020,7 +1022,7 @@ export class InputValidator { return z.strictObject({ where: this.makeWhereSchema(model, false).optional(), data: this.makeUpdateDataSchema(model, [], true), - limit: z.int().nonnegative().optional(), + limit: z.number().int().nonnegative().optional(), }); } @@ -1158,7 +1160,7 @@ export class InputValidator { return z .object({ where: this.makeWhereSchema(model, false).optional(), - limit: z.int().nonnegative().optional(), + limit: z.number().int().nonnegative().optional(), }) .optional(); @@ -1255,10 +1257,10 @@ export class InputValidator { const modelDef = requireModel(this.schema, model); const nonRelationFields = Object.keys(modelDef.fields).filter((field) => !modelDef.fields[field]?.relation); - let schema = z.strictObject({ + let schema: z.ZodSchema = z.strictObject({ where: this.makeWhereSchema(model, false).optional(), orderBy: this.orArray(this.makeOrderBySchema(model, false, true), true).optional(), - by: this.orArray(z.enum(nonRelationFields), true), + by: this.orArray(z.enum(nonRelationFields as [string, ...string[]]), true), having: this.makeHavingSchema(model).optional(), skip: this.makeSkipSchema().optional(), take: this.makeTakeSchema().optional(), @@ -1340,11 +1342,11 @@ export class InputValidator { // #region Helpers private makeSkipSchema() { - return z.int().nonnegative(); + return z.number().int().nonnegative(); } private makeTakeSchema() { - return z.int(); + return z.number().int(); } private refineForSelectIncludeMutuallyExclusive(schema: ZodType) { diff --git a/packages/runtime/test/client-api/mixin.test.ts b/packages/runtime/test/client-api/mixin.test.ts index ffbdbf2f..9f85d30a 100644 --- a/packages/runtime/test/client-api/mixin.test.ts +++ b/packages/runtime/test/client-api/mixin.test.ts @@ -51,7 +51,7 @@ model Bar with CommonFields { description: 'Bar', }, }), - ).rejects.toThrow('Invalid input'); + ).rejects.toThrow(/invalid/i); await expect( client.bar.create({ diff --git a/packages/runtime/test/client-api/typed-json-fields.test.ts b/packages/runtime/test/client-api/typed-json-fields.test.ts index 53ca43e0..4ea57c7e 100644 --- a/packages/runtime/test/client-api/typed-json-fields.test.ts +++ b/packages/runtime/test/client-api/typed-json-fields.test.ts @@ -127,7 +127,7 @@ model User { }, }, }), - ).rejects.toThrow('Invalid input'); + ).rejects.toThrow(/invalid/i); }); it('works with find', async () => { @@ -215,7 +215,7 @@ model User { }, }, }), - ).rejects.toThrow('Invalid input'); + ).rejects.toThrow(/invalid/i); }); }, ); diff --git a/packages/zod/package.json b/packages/zod/package.json index b6ff5943..de0ef2a2 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -30,7 +30,8 @@ }, "devDependencies": { "@zenstackhq/eslint-config": "workspace:*", - "@zenstackhq/typescript-config": "workspace:*" + "@zenstackhq/typescript-config": "workspace:*", + "zod": "~3.25.0" }, "peerDependencies": { "zod": "catalog:" diff --git a/packages/zod/src/index.ts b/packages/zod/src/index.ts index a7fa63e6..a74ad446 100644 --- a/packages/zod/src/index.ts +++ b/packages/zod/src/index.ts @@ -28,6 +28,6 @@ function makeScalarSchema(fieldDef: FieldDef): ZodType { .with('String', () => z.string()) .with(P.union('Int', 'BigInt', 'Float', 'Decimal'), () => z.number()) .with('Boolean', () => z.boolean()) - .with('DateTime', () => z.iso.datetime()) + .with('DateTime', () => z.string().datetime()) .otherwise(() => z.unknown()); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 42a9470e..70b5b1db 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -33,9 +33,6 @@ catalogs: typescript: specifier: ^5.8.0 version: 5.8.3 - zod: - specifier: ^4.0.0 - version: 4.0.5 importers: @@ -290,9 +287,6 @@ importers: uuid: specifier: ^11.0.5 version: 11.0.5 - zod: - specifier: 'catalog:' - version: 4.0.5 devDependencies: '@types/better-sqlite3': specifier: ^7.6.13 @@ -324,6 +318,9 @@ importers: tsx: specifier: ^4.19.2 version: 4.19.2 + zod: + specifier: ~3.25.0 + version: 3.25.76 packages/sdk: dependencies: @@ -427,9 +424,6 @@ importers: ts-pattern: specifier: 'catalog:' version: 5.7.1 - zod: - specifier: 'catalog:' - version: 4.0.5 devDependencies: '@zenstackhq/eslint-config': specifier: workspace:* @@ -437,6 +431,9 @@ importers: '@zenstackhq/typescript-config': specifier: workspace:* version: link:../typescript-config + zod: + specifier: ~3.25.0 + version: 3.25.76 samples/blog: dependencies: @@ -2666,8 +2663,8 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zod@4.0.5: - resolution: {integrity: sha512-/5UuuRPStvHXu7RS+gmvRf4NXrNxpSllGwDnCBcJZtQsKrviYXm54yDGV2KYNLT5kq0lHGcl7lqWJLgSaG+tgA==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} snapshots: @@ -4727,4 +4724,4 @@ snapshots: yocto-queue@0.1.0: {} - zod@4.0.5: {} + zod@3.25.76: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 80826124..11ea3a73 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -4,7 +4,7 @@ packages: - tests/** catalog: kysely: ^0.27.6 - zod: ^4.0.0 + zod: ^3.25.0 || ^4.0.0 prisma: ^6.10.0 langium: 3.5.0 langium-cli: 3.5.0