Skip to content
Open
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
21 changes: 15 additions & 6 deletions packages/orm/src/client/crud-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -968,12 +968,14 @@ export type SelectIncludeOmit<
*/
omit?: (OmitInput<Schema, Model> & ExtResultSelectOmitFields<ExtResult, Model & string>) | null;
} & (AllowRelation extends true
? {
/**
* Specifies relations to be included in the query result. All scalar fields are included.
*/
include?: IncludeInput<Schema, Model, Options, AllowCount, ExtResult> | null;
}
? HasRelations<Schema, Model> extends true
? {
/**
* Specifies relations to be included in the query result. All scalar fields are included.
*/
include?: IncludeInput<Schema, Model, Options, AllowCount, ExtResult> | null;
}
: {}
: {});

export type SelectInput<
Expand Down Expand Up @@ -2390,6 +2392,13 @@ type HasToManyRelations<Schema extends SchemaDef, Model extends GetModels<Schema
? false
: true;

type HasRelations<Schema extends SchemaDef, Model extends GetModels<Schema>> = RelationFields<
Schema,
Model
> extends never
? false
: true;

type EnumValue<Schema extends SchemaDef, Enum extends GetEnums<Schema>> = GetEnum<Schema, Enum>[keyof GetEnum<
Schema,
Enum
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ export abstract class LateralJoinDialectBase<Schema extends SchemaDef> extends B
);
}

if (typeof payload === 'object' && payload.include && typeof payload.include === 'object') {
if (typeof payload === 'object' && 'include' in payload && payload.include && typeof payload.include === 'object') {
// include relation fields

Object.assign(
Expand All @@ -270,7 +270,7 @@ export abstract class LateralJoinDialectBase<Schema extends SchemaDef> extends B
) {
let result = query;
if (typeof payload === 'object') {
const selectInclude = payload.include ?? payload.select;
const selectInclude = ('include' in payload ? payload.include : undefined) ?? payload.select;
if (selectInclude && typeof selectInclude === 'object') {
Object.entries<any>(selectInclude)
.filter(([, value]) => value)
Expand Down
2 changes: 1 addition & 1 deletion packages/orm/src/client/crud/dialects/sqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ export class SqliteCrudDialect<Schema extends SchemaDef> extends BaseCrudDialect
);
}

if (typeof payload === 'object' && payload.include && typeof payload.include === 'object') {
if (typeof payload === 'object' && 'include' in payload && payload.include && typeof payload.include === 'object') {
// include relation fields
objArgs.push(
...Object.entries<any>(payload.include)
Expand Down
5 changes: 3 additions & 2 deletions packages/orm/src/client/executor/zenstack-query-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { TransactionIsolationLevel, type ClientContract } from '../contract';
import { getCrudDialect } from '../crud/dialects';
import type { BaseCrudDialect } from '../crud/dialects/base-dialect';
import { createDBQueryError, createInternalError, ORMError } from '../errors';
import type { AfterEntityMutationCallback, OnKyselyQueryCallback } from '../plugin';
import type { AfterEntityMutationCallback, BeforeEntityMutationCallback, OnKyselyQueryCallback } from '../plugin';
import { requireIdFields, stripAlias } from '../query-utils';
import { QueryNameMapper } from './name-mapper';
import { TempAliasTransformer } from './temp-alias-transformer';
Expand Down Expand Up @@ -355,7 +355,8 @@ export class ZenStackQueryExecutor extends DefaultQueryExecutor {
continue;
}

await onEntityMutation.beforeEntityMutation({
// tsc perf
await (onEntityMutation.beforeEntityMutation as BeforeEntityMutationCallback<SchemaDef>)({
model: mutationInfo.model,
action: mutationInfo.action,
queryNode,
Expand Down
27 changes: 26 additions & 1 deletion tests/e2e/orm/schemas/typing/typecheck.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ZenStackClient } from '@zenstackhq/orm';
import { ZenStackClient, type FindManyArgs } from '@zenstackhq/orm';
import SQLite from 'better-sqlite3';
import { SqliteDialect } from 'kysely';
import { Role, Status, type Identity, type IdentityProvider } from './models';
Expand Down Expand Up @@ -679,3 +679,28 @@ function typeDefs() {
}

main();

// Type test: `include` should not be allowed for models without relations
{
type NoRelationsSchema = {
provider: { type: 'sqlite' };
models: {
Dummy: {
name: 'Dummy';
fields: {
id: { name: 'id'; type: 'Int'; id: true };
name: { name: 'name'; type: 'String' };
};
idFields: ['id'];
uniqueFields: { id: { type: 'Int' } };
};
};
enums: {};
typeDefs: {};
plugins: {};
};
type DummyFindManyArgs = FindManyArgs<NoRelationsSchema, 'Dummy'>;
// @ts-expect-error include should not be allowed for models without relations
const _testIncludeNotAllowed: DummyFindManyArgs = { include: { abcdefg: true } };
void _testIncludeNotAllowed;
}