From df08badec5b030c4da615159fd50e5114d7f30d5 Mon Sep 17 00:00:00 2001 From: Eugen Istoc Date: Wed, 19 Nov 2025 11:27:15 -0500 Subject: [PATCH 1/3] chore: enhance auth type to include relations --- packages/orm/src/client/contract.ts | 26 ++++++++++++++++++++++++-- packages/orm/src/client/crud-types.ts | 2 +- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/packages/orm/src/client/contract.ts b/packages/orm/src/client/contract.ts index 6db19953..f78f3197 100644 --- a/packages/orm/src/client/contract.ts +++ b/packages/orm/src/client/contract.ts @@ -1,5 +1,14 @@ import type Decimal from 'decimal.js'; -import { type GetModels, type IsDelegateModel, type ProcedureDef, type SchemaDef } from '../schema'; +import { + type FieldIsArray, + type GetModels, + type IsDelegateModel, + type NonRelationFields, + type ProcedureDef, + type RelationFields, + type RelationFieldType, + type SchemaDef, +} from '../schema'; import type { AnyKysely } from '../utils/kysely-utils'; import type { OrUndefinedIf, Simplify, UnwrapTuplePromises } from '../utils/type-utils'; import type { TRANSACTION_UNSUPPORTED_METHODS } from './constants'; @@ -19,6 +28,7 @@ import type { FindUniqueArgs, GroupByArgs, GroupByResult, + MapModelFieldType, ModelResult, SelectSubset, SimplifiedModelResult, @@ -810,11 +820,23 @@ export type ModelOperations> = { + [Key in NonRelationFields]?: MapModelFieldType; +} & { + [Key in RelationFields]?: FieldIsArray extends true + ? AuthModelType>[] + : AuthModelType>; +}; + export type AuthType = string extends GetModels ? Record : Schema['authType'] extends GetModels - ? Partial> + ? AuthModelType : never; //#endregion diff --git a/packages/orm/src/client/crud-types.ts b/packages/orm/src/client/crud-types.ts index 934c0139..a9b0d438 100644 --- a/packages/orm/src/client/crud-types.ts +++ b/packages/orm/src/client/crud-types.ts @@ -551,7 +551,7 @@ type RelationFilter< //#region Field utils -type MapModelFieldType< +export type MapModelFieldType< Schema extends SchemaDef, Model extends GetModels, Field extends GetModelFields, From d3c9e2108c6744156c1bcdf71b66e37b03f2ed15 Mon Sep 17 00:00:00 2001 From: Eugen Istoc Date: Wed, 19 Nov 2025 20:24:17 -0500 Subject: [PATCH 2/3] chore: update --- packages/orm/src/client/contract.ts | 9 ++++----- packages/orm/src/client/crud-types.ts | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/orm/src/client/contract.ts b/packages/orm/src/client/contract.ts index f78f3197..4ef52dd5 100644 --- a/packages/orm/src/client/contract.ts +++ b/packages/orm/src/client/contract.ts @@ -3,7 +3,6 @@ import { type FieldIsArray, type GetModels, type IsDelegateModel, - type NonRelationFields, type ProcedureDef, type RelationFields, type RelationFieldType, @@ -21,6 +20,7 @@ import type { CreateArgs, CreateManyAndReturnArgs, CreateManyArgs, + DefaultModelResult, DeleteArgs, DeleteManyArgs, FindFirstArgs, @@ -28,7 +28,6 @@ import type { FindUniqueArgs, GroupByArgs, GroupByResult, - MapModelFieldType, ModelResult, SelectSubset, SimplifiedModelResult, @@ -824,9 +823,9 @@ export type ModelOperations> = { - [Key in NonRelationFields]?: MapModelFieldType; -} & { +type AuthModelType> = Partial< + DefaultModelResult +> & { [Key in RelationFields]?: FieldIsArray extends true ? AuthModelType>[] : AuthModelType>; diff --git a/packages/orm/src/client/crud-types.ts b/packages/orm/src/client/crud-types.ts index a9b0d438..6fe3f076 100644 --- a/packages/orm/src/client/crud-types.ts +++ b/packages/orm/src/client/crud-types.ts @@ -47,7 +47,7 @@ import type { ToKyselySchema } from './query-builder'; //#region Query results -type DefaultModelResult< +export type DefaultModelResult< Schema extends SchemaDef, Model extends GetModels, Omit = undefined, @@ -551,7 +551,7 @@ type RelationFilter< //#region Field utils -export type MapModelFieldType< +type MapModelFieldType< Schema extends SchemaDef, Model extends GetModels, Field extends GetModelFields, From 6e56e984fdaad6c382c308031480288a8d70e8ca Mon Sep 17 00:00:00 2001 From: ymc9 <104139426+ymc9@users.noreply.github.com> Date: Wed, 19 Nov 2025 18:52:09 -0800 Subject: [PATCH 3/3] add a regression test --- tests/regression/test/issue-422/input.ts | 70 ++++++++++ tests/regression/test/issue-422/models.ts | 12 ++ .../test/issue-422/regression.test.ts | 18 +++ tests/regression/test/issue-422/schema.ts | 122 ++++++++++++++++++ tests/regression/test/issue-422/schema.zmodel | 25 ++++ 5 files changed, 247 insertions(+) create mode 100644 tests/regression/test/issue-422/input.ts create mode 100644 tests/regression/test/issue-422/models.ts create mode 100644 tests/regression/test/issue-422/regression.test.ts create mode 100644 tests/regression/test/issue-422/schema.ts create mode 100644 tests/regression/test/issue-422/schema.zmodel diff --git a/tests/regression/test/issue-422/input.ts b/tests/regression/test/issue-422/input.ts new file mode 100644 index 00000000..73b7ed9a --- /dev/null +++ b/tests/regression/test/issue-422/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/orm"; +import type { SimplifiedModelResult as $SimplifiedModelResult, SelectIncludeOmit as $SelectIncludeOmit } from "@zenstackhq/orm"; +export type SessionFindManyArgs = $FindManyArgs<$Schema, "Session">; +export type SessionFindUniqueArgs = $FindUniqueArgs<$Schema, "Session">; +export type SessionFindFirstArgs = $FindFirstArgs<$Schema, "Session">; +export type SessionCreateArgs = $CreateArgs<$Schema, "Session">; +export type SessionCreateManyArgs = $CreateManyArgs<$Schema, "Session">; +export type SessionCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Session">; +export type SessionUpdateArgs = $UpdateArgs<$Schema, "Session">; +export type SessionUpdateManyArgs = $UpdateManyArgs<$Schema, "Session">; +export type SessionUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Session">; +export type SessionUpsertArgs = $UpsertArgs<$Schema, "Session">; +export type SessionDeleteArgs = $DeleteArgs<$Schema, "Session">; +export type SessionDeleteManyArgs = $DeleteManyArgs<$Schema, "Session">; +export type SessionCountArgs = $CountArgs<$Schema, "Session">; +export type SessionAggregateArgs = $AggregateArgs<$Schema, "Session">; +export type SessionGroupByArgs = $GroupByArgs<$Schema, "Session">; +export type SessionWhereInput = $WhereInput<$Schema, "Session">; +export type SessionSelect = $SelectInput<$Schema, "Session">; +export type SessionInclude = $IncludeInput<$Schema, "Session">; +export type SessionOmit = $OmitInput<$Schema, "Session">; +export type SessionGetPayload> = $SimplifiedModelResult<$Schema, "Session", Args>; +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>; diff --git a/tests/regression/test/issue-422/models.ts b/tests/regression/test/issue-422/models.ts new file mode 100644 index 00000000..787d1c77 --- /dev/null +++ b/tests/regression/test/issue-422/models.ts @@ -0,0 +1,12 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// 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 ModelResult as $ModelResult } from "@zenstackhq/orm"; +export type Session = $ModelResult<$Schema, "Session">; +export type User = $ModelResult<$Schema, "User">; +export type Profile = $ModelResult<$Schema, "Profile">; diff --git a/tests/regression/test/issue-422/regression.test.ts b/tests/regression/test/issue-422/regression.test.ts new file mode 100644 index 00000000..5d572391 --- /dev/null +++ b/tests/regression/test/issue-422/regression.test.ts @@ -0,0 +1,18 @@ +import { createTestClient } from '@zenstackhq/testtools'; +import { describe, it } from 'vitest'; +import { schema } from './schema'; + +describe('Issue 422 regression tests', () => { + it('should infer correct auth type', async () => { + const db = await createTestClient(schema); + + // all fields optional + db.$setAuth({ id: 'session1' }); + + // relations are allowed + db.$setAuth({ id: 'user1', user: { id: 'user1' } }); + + // nested relations are allowed + db.$setAuth({ id: 'user1', user: { id: 'user1', profile: { name: 'User1' } } }); + }); +}); diff --git a/tests/regression/test/issue-422/schema.ts b/tests/regression/test/issue-422/schema.ts new file mode 100644 index 00000000..587f70aa --- /dev/null +++ b/tests/regression/test/issue-422/schema.ts @@ -0,0 +1,122 @@ +////////////////////////////////////////////////////////////////////////////////////////////// +// DO NOT MODIFY THIS FILE // +// This file is automatically generated by ZenStack CLI and should not be manually updated. // +////////////////////////////////////////////////////////////////////////////////////////////// + +/* eslint-disable */ + +import { type SchemaDef, ExpressionUtils } from "@zenstackhq/orm/schema"; +const _schema = { + provider: { + type: "sqlite" + }, + models: { + Session: { + name: "Session", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }] + }, + token: { + name: "token", + type: "String" + }, + userId: { + name: "userId", + type: "String", + foreignKeyFor: [ + "user" + ] + }, + user: { + name: "user", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array([ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array([ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }], + relation: { opposite: "sessions", fields: ["userId"], references: ["id"], onDelete: "Cascade" } + } + }, + attributes: [ + { name: "@@auth" } + ], + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + User: { + name: "User", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("uuid") }] }], + default: ExpressionUtils.call("uuid") + }, + sessions: { + name: "sessions", + type: "Session", + array: true, + relation: { opposite: "user" } + }, + profile: { + name: "profile", + type: "Profile", + optional: true, + relation: { opposite: "user" } + } + }, + idFields: ["id"], + uniqueFields: { + id: { type: "String" } + } + }, + Profile: { + name: "Profile", + fields: { + id: { + name: "id", + type: "String", + id: true, + attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("uuid") }] }], + default: ExpressionUtils.call("uuid") + }, + name: { + name: "name", + type: "String", + optional: true + }, + userId: { + name: "userId", + type: "String", + unique: true, + attributes: [{ name: "@unique" }], + foreignKeyFor: [ + "user" + ] + }, + user: { + name: "user", + type: "User", + attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array([ExpressionUtils.field("userId")]) }, { name: "references", value: ExpressionUtils.array([ExpressionUtils.field("id")]) }, { name: "onDelete", value: ExpressionUtils.literal("Cascade") }] }], + relation: { opposite: "profile", fields: ["userId"], references: ["id"], onDelete: "Cascade" } + } + }, + idFields: ["id"], + uniqueFields: { + id: { type: "String" }, + userId: { type: "String" } + } + } + }, + authType: "Session", + plugins: {} +} as const satisfies SchemaDef; +type Schema = typeof _schema & { + __brand?: "schema"; +}; +export const schema: Schema = _schema; +export type SchemaType = Schema; diff --git a/tests/regression/test/issue-422/schema.zmodel b/tests/regression/test/issue-422/schema.zmodel new file mode 100644 index 00000000..c37ed929 --- /dev/null +++ b/tests/regression/test/issue-422/schema.zmodel @@ -0,0 +1,25 @@ +datasource db { + provider = "sqlite" + url = "file:./dev.db" +} + +model Session { + id String @id + token String + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + @@auth +} + +model User { + id String @id @default(uuid()) + sessions Session[] + profile Profile? +} + +model Profile { + id String @id @default(uuid()) + name String? + userId String @unique + user User @relation(fields: [userId], references: [id], onDelete: Cascade) +}