Skip to content

Commit dccd6ae

Browse files
ymc9claude
andcommitted
fix(orm): enforce at most one key per orderBy array element
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent f137525 commit dccd6ae

2 files changed

Lines changed: 22 additions & 4 deletions

File tree

packages/orm/src/client/zod/factory.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,10 @@ export class ZodSchemaFactory<
11261126
) {
11271127
const fields: Record<string, ZodType> = {};
11281128
const sort = z.union([z.literal('asc'), z.literal('desc')]);
1129+
const refineAtMostOneKey = (s: ZodObject) =>
1130+
s.refine((v: object) => Object.keys(v).length <= 1, {
1131+
message: 'Each orderBy element must have at most one key',
1132+
});
11291133
const nextOpts = this.nextOptions(options);
11301134
for (const [field, fieldDef] of this.getModelFields(model)) {
11311135
if (fieldDef.relation) {
@@ -1139,9 +1143,8 @@ export class ZodSchemaFactory<
11391143
nextOpts,
11401144
);
11411145
if (fieldDef.array) {
1142-
relationOrderBy = relationOrderBy.extend({
1143-
_count: sort,
1144-
});
1146+
// safeExtend drops existing refinements, so re-apply after extending
1147+
relationOrderBy = refineAtMostOneKey(relationOrderBy.safeExtend({ _count: sort }));
11451148
}
11461149
return relationOrderBy.optional();
11471150
});
@@ -1172,7 +1175,7 @@ export class ZodSchemaFactory<
11721175
}
11731176
}
11741177

1175-
return z.strictObject(fields);
1178+
return refineAtMostOneKey(z.strictObject(fields));
11761179
}
11771180

11781181
@cache()

tests/e2e/orm/client-api/find.test.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,6 +1189,21 @@ describe('Client find tests ', () => {
11891189
expect(result3?._count.posts).toBe(1);
11901190
});
11911191

1192+
it('rejects orderBy array elements with multiple keys', async () => {
1193+
await createUser(client, 'u1@test.com');
1194+
1195+
// zero keys is valid
1196+
await expect(client.user.findMany({ orderBy: [{}] })).resolves.toBeDefined();
1197+
1198+
// single key is valid
1199+
await expect(client.user.findMany({ orderBy: [{ email: 'asc' }] })).resolves.toBeDefined();
1200+
1201+
// multiple keys in one element is rejected
1202+
await expect(
1203+
client.user.findMany({ orderBy: [{ email: 'asc', role: 'desc' }] } as any),
1204+
).toBeRejectedByValidation();
1205+
});
1206+
11921207
it('supports $expr', async () => {
11931208
await createUser(client, 'yiming@gmail.com');
11941209
await createUser(client, 'yiming@zenstack.dev');

0 commit comments

Comments
 (0)