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
1 change: 1 addition & 0 deletions BREAKINGCHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
1. `check()` ORM api has been removed
1. non-optional to-one relation doesn't automatically filter parent read when evaluating access policies
1. `@omit` and `@password` attributes have been removed
1. SWR plugin is removed
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "zenstack-v3",
"version": "3.0.0-beta.14",
"description": "ZenStack",
"packageManager": "pnpm@10.12.1",
"packageManager": "pnpm@10.20.0",
"scripts": {
"build": "turbo run build",
"watch": "turbo run watch build",
Expand Down
2 changes: 1 addition & 1 deletion packages/orm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,6 @@
"@zenstackhq/typescript-config": "workspace:*",
"@zenstackhq/vitest-config": "workspace:*",
"tsx": "^4.19.2",
"zod": "~3.25.0"
"zod": "^4.1.0"
}
}
16 changes: 8 additions & 8 deletions packages/orm/src/client/crud/validator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { enumerate, invariant } from '@zenstackhq/common-helpers';
import Decimal from 'decimal.js';
import stableStringify from 'json-stable-stringify';
import { match, P } from 'ts-pattern';
import { z, ZodSchema, ZodType } from 'zod';
import { z, ZodType } from 'zod';
import {
type AttributeApplication,
type BuiltinType,
Expand Down Expand Up @@ -830,7 +830,7 @@ export class InputValidator<Schema extends SchemaDef> {

private makeCreateSchema(model: string) {
const dataSchema = this.makeCreateDataSchema(model, false);
let schema: ZodSchema = z.strictObject({
let schema: ZodType = z.strictObject({
data: dataSchema,
select: this.makeSelectSchema(model).optional(),
include: this.makeIncludeSchema(model).optional(),
Expand Down Expand Up @@ -1107,7 +1107,7 @@ export class InputValidator<Schema extends SchemaDef> {
// #region Update

private makeUpdateSchema(model: string) {
let schema: ZodSchema = z.strictObject({
let schema: ZodType = z.strictObject({
where: this.makeWhereSchema(model, true),
data: this.makeUpdateDataSchema(model),
select: this.makeSelectSchema(model).optional(),
Expand All @@ -1129,7 +1129,7 @@ export class InputValidator<Schema extends SchemaDef> {

private makeUpdateManyAndReturnSchema(model: string) {
const base = this.makeUpdateManySchema(model);
let schema: ZodSchema = base.extend({
let schema: ZodType = base.extend({
select: this.makeSelectSchema(model).optional(),
omit: this.makeOmitSchema(model).optional(),
});
Expand All @@ -1138,7 +1138,7 @@ export class InputValidator<Schema extends SchemaDef> {
}

private makeUpsertSchema(model: string) {
let schema: ZodSchema = z.strictObject({
let schema: ZodType = z.strictObject({
where: this.makeWhereSchema(model, true),
create: this.makeCreateDataSchema(model, false),
update: this.makeUpdateDataSchema(model),
Expand Down Expand Up @@ -1257,7 +1257,7 @@ export class InputValidator<Schema extends SchemaDef> {
// #region Delete

private makeDeleteSchema(model: GetModels<Schema>) {
let schema: ZodSchema = z.strictObject({
let schema: ZodType = z.strictObject({
where: this.makeWhereSchema(model, true),
select: this.makeSelectSchema(model).optional(),
include: this.makeIncludeSchema(model).optional(),
Expand Down Expand Up @@ -1387,7 +1387,7 @@ export class InputValidator<Schema extends SchemaDef> {
});

// fields used in `having` must be either in the `by` list, or aggregations
schema = schema.refine((value) => {
schema = schema.refine((value: any) => {
const bys = typeof value.by === 'string' ? [value.by] : value.by;
if (value.having && typeof value.having === 'object') {
for (const [key, val] of Object.entries(value.having)) {
Expand All @@ -1414,7 +1414,7 @@ export class InputValidator<Schema extends SchemaDef> {
}, 'fields in "having" must be in "by"');

// fields used in `orderBy` must be either in the `by` list, or aggregations
schema = schema.refine((value) => {
schema = schema.refine((value: any) => {
const bys = typeof value.by === 'string' ? [value.by] : value.by;
if (
value.orderBy &&
Expand Down
27 changes: 23 additions & 4 deletions packages/orm/src/client/crud/validator/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
import Decimal from 'decimal.js';
import { match, P } from 'ts-pattern';
import { z } from 'zod';
import { ZodIssueCode } from 'zod/v3';
import { ExpressionUtils } from '../../../schema';
import { QueryError } from '../../errors';

Expand Down Expand Up @@ -171,8 +172,26 @@ export function addDecimalValidation(
return schema.superRefine((v, ctx) => {
const base = z.number();
const { error } = base[op](value).safeParse((v as Decimal).toNumber());
error?.errors.forEach((e) => {
ctx.addIssue(e);
error?.issues.forEach((issue) => {
if (op === 'gt' || op === 'gte') {
ctx.addIssue({
code: ZodIssueCode.too_small,
origin: 'number',
minimum: value,
type: 'decimal',
inclusive: op === 'gte',
message: issue.message,
});
} else {
ctx.addIssue({
code: ZodIssueCode.too_big,
origin: 'number',
maximum: value,
type: 'decimal',
inclusive: op === 'lte',
message: issue.message,
});
}
});
});
}
Expand Down Expand Up @@ -258,9 +277,9 @@ function applyValidation(
message: string | undefined,
path: string[] | undefined,
) {
const options: z.CustomErrorParams = {};
const options: Parameters<typeof schema.refine>[1] = {};
if (message) {
options.message = message;
options.error = message;
}
if (path) {
options.path = path;
Expand Down
9 changes: 2 additions & 7 deletions packages/orm/src/utils/zod-utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { ZodError } from 'zod';
import { fromError as fromError3 } from 'zod-validation-error/v3';
import { fromError as fromError4 } from 'zod-validation-error/v4';
import { fromError } from 'zod-validation-error/v4';

/**
* Format ZodError into a readable string
*/
export function formatError(error: ZodError): string {
if ('_zod' in error) {
return fromError4(error).toString();
} else {
return fromError3(error).toString();
}
return fromError(error).toString();
}
2 changes: 1 addition & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@
"next": "^15.0.0",
"nuxt": "^4.2.0",
"supertest": "^7.1.4",
"zod": "~3.25.0"
"zod": "^4.1.0"
},
"peerDependencies": {
"@sveltejs/kit": "^2.0.0",
Expand Down
2 changes: 1 addition & 1 deletion packages/testtools/src/vitest-ext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function expectError(err: any, errorType: any) {

function expectErrorMessages(expectedMessages: string[], message: string) {
for (const m of expectedMessages) {
if (!message.includes(m)) {
if (!message.toLowerCase().includes(m.toLowerCase())) {
return {
message: () => `expected message not found in error: ${m}, got message: ${message}`,
pass: false,
Expand Down
2 changes: 1 addition & 1 deletion packages/zod/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"devDependencies": {
"@zenstackhq/eslint-config": "workspace:*",
"@zenstackhq/typescript-config": "workspace:*",
"zod": "~3.25.0"
"zod": "^4.1.0"
},
"peerDependencies": {
"zod": "catalog:"
Expand Down
26 changes: 13 additions & 13 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ packages:
- tests/**
catalog:
kysely: ^0.27.6
zod: ^3.25.0 || ^4.0.0
zod: ^4.0.0
prisma: ^6.10.0
langium: 3.5.0
langium-cli: 3.5.0
Expand Down
Loading
Loading