Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/cli/src/actions/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export async function run(options: Options) {

\`\`\`ts
import { ZenStackClient } from '@zenstackhq/orm';
import { schema } from '${outputPath}/schema';
import { schema } from '${path.relative('.', outputPath)}/schema';

const client = new ZenStackClient(schema, {
dialect: { ... }
Expand Down
99 changes: 36 additions & 63 deletions packages/orm/src/client/crud-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,50 +139,34 @@ type ModelSelectResult<
Options extends QueryOptions<Schema>,
> = {
[Key in keyof Select as Select[Key] extends false | undefined
? never
: Key extends keyof Omit
? Omit[Key] extends true
? never
: Key
: Key extends '_count'
? Select[Key] extends SelectCount<Schema, Model>
? Key
: never
? // not selected
never
: Key extends '_count'
? // select "_count"
Select[Key] extends SelectCount<Schema, Model>
? Key
: never
: Key extends keyof Omit
? Omit[Key] extends true
? // omit
never
: Key
: Key]: Key extends '_count'
? SelectCountResult<Schema, Model, Select[Key]>
? // select "_count" result
SelectCountResult<Schema, Model, Select[Key]>
: Key extends NonRelationFields<Schema, Model>
? MapModelFieldType<Schema, Model, Key>
? // scalar field result
MapModelFieldType<Schema, Model, Key>
: Key extends RelationFields<Schema, Model>
? Select[Key] extends FindArgs<
? // relation field result (recurse)
ModelResult<
Schema,
RelationFieldType<Schema, Model, Key>,
Select[Key],
Options,
ModelFieldIsOptional<Schema, Model, Key>,
FieldIsArray<Schema, Model, Key>
>
? 'select' extends keyof Select[Key]
? ModelResult<
Schema,
RelationFieldType<Schema, Model, Key>,
Pick<Select[Key], 'select'>,
Options,
ModelFieldIsOptional<Schema, Model, Key>,
FieldIsArray<Schema, Model, Key>
>
: ModelResult<
Schema,
RelationFieldType<Schema, Model, Key>,
Pick<Select[Key], 'include' | 'omit'>,
Options,
ModelFieldIsOptional<Schema, Model, Key>,
FieldIsArray<Schema, Model, Key>
>
: DefaultModelResult<
Schema,
RelationFieldType<Schema, Model, Key>,
Omit,
Options,
ModelFieldIsOptional<Schema, Model, Key>,
FieldIsArray<Schema, Model, Key>
>
: never;
};

Expand All @@ -204,40 +188,29 @@ export type ModelResult<
Array = false,
> = WrapType<
Args extends {
select: infer S;
omit?: infer O;
}
select: infer S extends object;
omit?: infer O extends object;
} & Record<string, unknown>
? ModelSelectResult<Schema, Model, S, O, Options>
: Args extends {
include: infer I;
omit?: infer O;
}
? DefaultModelResult<Schema, Model, O, Options, false, false> & {
include: infer I extends object;
omit?: infer O extends object;
} & Record<string, unknown>
? // select all non-omitted scalar fields
DefaultModelResult<Schema, Model, O, Options, false, false> & {
// recurse for "include" relations
[Key in keyof I & RelationFields<Schema, Model> as I[Key] extends false | undefined
? never
: Key]: I[Key] extends FindArgs<
: Key]: ModelResult<
Schema,
RelationFieldType<Schema, Model, Key>,
I[Key],
Options,
ModelFieldIsOptional<Schema, Model, Key>,
FieldIsArray<Schema, Model, Key>
>
? ModelResult<
Schema,
RelationFieldType<Schema, Model, Key>,
I[Key],
Options,
ModelFieldIsOptional<Schema, Model, Key>,
FieldIsArray<Schema, Model, Key>
>
: DefaultModelResult<
Schema,
RelationFieldType<Schema, Model, Key>,
undefined,
Options,
ModelFieldIsOptional<Schema, Model, Key>,
FieldIsArray<Schema, Model, Key>
>;
>;
}
: Args extends { omit: infer O }
: Args extends { omit: infer O } & Record<string, unknown>
? DefaultModelResult<Schema, Model, O, Options, false, false>
: DefaultModelResult<Schema, Model, undefined, Options, false, false>,
Optional,
Expand Down
1 change: 1 addition & 0 deletions tests/e2e/orm/schemas/typing/typecheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ async function find() {
select: {
posts: {
where: { title: 'Foo' },
take: 1,
select: {
author: {
select: {
Expand Down
6 changes: 3 additions & 3 deletions tests/regression/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
"private": true,
"type": "module",
"scripts": {
"build": "pnpm run generate",
"generate": "tsx ../../scripts/test-generate.ts ./test",
"test": "pnpm generate && tsc && vitest run"
"build": "pnpm run test:generate",
"test:generate": "tsx ../../scripts/test-generate.ts ./test",
"test": "pnpm test:generate && tsc && vitest run"
},
"dependencies": {
"@zenstackhq/testtools": "workspace:*",
Expand Down
70 changes: 70 additions & 0 deletions tests/regression/test/issue-503/input.ts
Original file line number Diff line number Diff line change
@@ -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, QueryOptions as $QueryOptions } from "@zenstackhq/orm";
import type { SimplifiedPlainResult as $Result, SelectIncludeOmit as $SelectIncludeOmit } from "@zenstackhq/orm";
export type InternalChatFindManyArgs = $FindManyArgs<$Schema, "InternalChat">;
export type InternalChatFindUniqueArgs = $FindUniqueArgs<$Schema, "InternalChat">;
export type InternalChatFindFirstArgs = $FindFirstArgs<$Schema, "InternalChat">;
export type InternalChatCreateArgs = $CreateArgs<$Schema, "InternalChat">;
export type InternalChatCreateManyArgs = $CreateManyArgs<$Schema, "InternalChat">;
export type InternalChatCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "InternalChat">;
export type InternalChatUpdateArgs = $UpdateArgs<$Schema, "InternalChat">;
export type InternalChatUpdateManyArgs = $UpdateManyArgs<$Schema, "InternalChat">;
export type InternalChatUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "InternalChat">;
export type InternalChatUpsertArgs = $UpsertArgs<$Schema, "InternalChat">;
export type InternalChatDeleteArgs = $DeleteArgs<$Schema, "InternalChat">;
export type InternalChatDeleteManyArgs = $DeleteManyArgs<$Schema, "InternalChat">;
export type InternalChatCountArgs = $CountArgs<$Schema, "InternalChat">;
export type InternalChatAggregateArgs = $AggregateArgs<$Schema, "InternalChat">;
export type InternalChatGroupByArgs = $GroupByArgs<$Schema, "InternalChat">;
export type InternalChatWhereInput = $WhereInput<$Schema, "InternalChat">;
export type InternalChatSelect = $SelectInput<$Schema, "InternalChat">;
export type InternalChatInclude = $IncludeInput<$Schema, "InternalChat">;
export type InternalChatOmit = $OmitInput<$Schema, "InternalChat">;
export type InternalChatGetPayload<Args extends $SelectIncludeOmit<$Schema, "InternalChat", true>, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "InternalChat", Args, Options>;
export type MessageFindManyArgs = $FindManyArgs<$Schema, "Message">;
export type MessageFindUniqueArgs = $FindUniqueArgs<$Schema, "Message">;
export type MessageFindFirstArgs = $FindFirstArgs<$Schema, "Message">;
export type MessageCreateArgs = $CreateArgs<$Schema, "Message">;
export type MessageCreateManyArgs = $CreateManyArgs<$Schema, "Message">;
export type MessageCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Message">;
export type MessageUpdateArgs = $UpdateArgs<$Schema, "Message">;
export type MessageUpdateManyArgs = $UpdateManyArgs<$Schema, "Message">;
export type MessageUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Message">;
export type MessageUpsertArgs = $UpsertArgs<$Schema, "Message">;
export type MessageDeleteArgs = $DeleteArgs<$Schema, "Message">;
export type MessageDeleteManyArgs = $DeleteManyArgs<$Schema, "Message">;
export type MessageCountArgs = $CountArgs<$Schema, "Message">;
export type MessageAggregateArgs = $AggregateArgs<$Schema, "Message">;
export type MessageGroupByArgs = $GroupByArgs<$Schema, "Message">;
export type MessageWhereInput = $WhereInput<$Schema, "Message">;
export type MessageSelect = $SelectInput<$Schema, "Message">;
export type MessageInclude = $IncludeInput<$Schema, "Message">;
export type MessageOmit = $OmitInput<$Schema, "Message">;
export type MessageGetPayload<Args extends $SelectIncludeOmit<$Schema, "Message", true>, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Message", Args, Options>;
export type MediaFindManyArgs = $FindManyArgs<$Schema, "Media">;
export type MediaFindUniqueArgs = $FindUniqueArgs<$Schema, "Media">;
export type MediaFindFirstArgs = $FindFirstArgs<$Schema, "Media">;
export type MediaCreateArgs = $CreateArgs<$Schema, "Media">;
export type MediaCreateManyArgs = $CreateManyArgs<$Schema, "Media">;
export type MediaCreateManyAndReturnArgs = $CreateManyAndReturnArgs<$Schema, "Media">;
export type MediaUpdateArgs = $UpdateArgs<$Schema, "Media">;
export type MediaUpdateManyArgs = $UpdateManyArgs<$Schema, "Media">;
export type MediaUpdateManyAndReturnArgs = $UpdateManyAndReturnArgs<$Schema, "Media">;
export type MediaUpsertArgs = $UpsertArgs<$Schema, "Media">;
export type MediaDeleteArgs = $DeleteArgs<$Schema, "Media">;
export type MediaDeleteManyArgs = $DeleteManyArgs<$Schema, "Media">;
export type MediaCountArgs = $CountArgs<$Schema, "Media">;
export type MediaAggregateArgs = $AggregateArgs<$Schema, "Media">;
export type MediaGroupByArgs = $GroupByArgs<$Schema, "Media">;
export type MediaWhereInput = $WhereInput<$Schema, "Media">;
export type MediaSelect = $SelectInput<$Schema, "Media">;
export type MediaInclude = $IncludeInput<$Schema, "Media">;
export type MediaOmit = $OmitInput<$Schema, "Media">;
export type MediaGetPayload<Args extends $SelectIncludeOmit<$Schema, "Media", true>, Options extends $QueryOptions<$Schema> = $QueryOptions<$Schema>> = $Result<$Schema, "Media", Args, Options>;
12 changes: 12 additions & 0 deletions tests/regression/test/issue-503/models.ts
Original file line number Diff line number Diff line change
@@ -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 InternalChat = $ModelResult<$Schema, "InternalChat">;
export type Message = $ModelResult<$Schema, "Message">;
export type Media = $ModelResult<$Schema, "Media">;
31 changes: 31 additions & 0 deletions tests/regression/test/issue-503/regression.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { createTestClient } from '@zenstackhq/testtools';
import { describe, expect, it } from 'vitest';
import { schema } from './schema';

describe('Regression tests for issues #503', () => {
it('verifies the issue', async () => {
const db = await createTestClient(schema);
const r = await db.internalChat.create({
data: {
messages: {
create: {
media: {
create: {
type: 'Image',
},
},
},
},
},
select: {
messages: {
take: 1,
include: {
media: true,
},
},
},
});
expect(r.messages[0]?.media).toMatchObject({ type: 'Image' });
});
});
109 changes: 109 additions & 0 deletions tests/regression/test/issue-503/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//////////////////////////////////////////////////////////////////////////////////////////////
// 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";
export class SchemaType implements SchemaDef {
provider = {
type: "sqlite"
} as const;
models = {
InternalChat: {
name: "InternalChat",
fields: {
id: {
name: "id",
type: "Int",
id: true,
attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }],
default: ExpressionUtils.call("autoincrement")
},
messages: {
name: "messages",
type: "Message",
array: true,
relation: { opposite: "chat" }
}
},
idFields: ["id"],
uniqueFields: {
id: { type: "Int" }
}
},
Message: {
name: "Message",
fields: {
id: {
name: "id",
type: "Int",
id: true,
attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }],
default: ExpressionUtils.call("autoincrement")
},
chat: {
name: "chat",
type: "InternalChat",
attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array([ExpressionUtils.field("chatId")]) }, { name: "references", value: ExpressionUtils.array([ExpressionUtils.field("id")]) }] }],
relation: { opposite: "messages", fields: ["chatId"], references: ["id"] }
},
chatId: {
name: "chatId",
type: "Int",
foreignKeyFor: [
"chat"
]
},
media: {
name: "media",
type: "Media",
optional: true,
attributes: [{ name: "@relation", args: [{ name: "fields", value: ExpressionUtils.array([ExpressionUtils.field("mediaId")]) }, { name: "references", value: ExpressionUtils.array([ExpressionUtils.field("id")]) }] }],
relation: { opposite: "messages", fields: ["mediaId"], references: ["id"] }
},
mediaId: {
name: "mediaId",
type: "Int",
optional: true,
foreignKeyFor: [
"media"
]
}
},
idFields: ["id"],
uniqueFields: {
id: { type: "Int" }
}
},
Media: {
name: "Media",
fields: {
id: {
name: "id",
type: "Int",
id: true,
attributes: [{ name: "@id" }, { name: "@default", args: [{ name: "value", value: ExpressionUtils.call("autoincrement") }] }],
default: ExpressionUtils.call("autoincrement")
},
type: {
name: "type",
type: "String"
},
messages: {
name: "messages",
type: "Message",
array: true,
relation: { opposite: "media" }
}
},
idFields: ["id"],
uniqueFields: {
id: { type: "Int" }
}
}
} as const;
plugins = {};
}
export const schema = new SchemaType();
Loading
Loading