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 LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022 ZenStack
Copyright (c) 2022-2025 ZenStack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
2 changes: 1 addition & 1 deletion packages/LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2022 ZenStack
Copyright (c) 2022-2025 ZenStack

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
4 changes: 2 additions & 2 deletions packages/plugins/openapi/src/generator-base.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { getZodErrorMessage } from '@zenstackhq/runtime/local-helpers';
import { PluginError, getDataModels, hasAttribute, type PluginOptions, type PluginResult } from '@zenstackhq/sdk';
import { Model } from '@zenstackhq/sdk/ast';
import type { DMMF } from '@zenstackhq/sdk/prisma';
import type { OpenAPIV3_1 as OAPI } from 'openapi-types';
import semver from 'semver';
import { fromZodError } from 'zod-validation-error/v3';
import { name } from '.';
import { SecuritySchemesSchema } from './schema';

Expand Down Expand Up @@ -94,7 +94,7 @@ export abstract class OpenAPIGeneratorBase {
if (securitySchemes) {
const parsed = SecuritySchemesSchema.safeParse(securitySchemes);
if (!parsed.success) {
throw new PluginError(name, `"securitySchemes" option is invalid: ${fromZodError(parsed.error)}`);
throw new PluginError(name, `"securitySchemes" option is invalid: ${getZodErrorMessage(parsed.error)}`);
}
return parsed.data;
}
Expand Down
7 changes: 3 additions & 4 deletions packages/runtime/src/enhancements/node/policy/handler.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* eslint-disable @typescript-eslint/no-explicit-any */

import deepmerge from 'deepmerge';
import { fromZodError } from 'zod-validation-error/v3';
import { CrudFailureReason } from '../../../constants';
import {
ModelDataVisitor,
Expand All @@ -15,7 +14,7 @@ import {
type FieldInfo,
type ModelMeta,
} from '../../../cross';
import { invariant, lowerCaseFirst, upperCaseFirst } from '../../../local-helpers';
import { getZodErrorMessage, invariant, lowerCaseFirst, upperCaseFirst } from '../../../local-helpers';
import { EnhancementContext, PolicyOperationKind, type CrudContract, type DbClientContract } from '../../../types';
import type { InternalEnhancementOptions } from '../create-enhancement';
import { Logger } from '../logger';
Expand Down Expand Up @@ -420,7 +419,7 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
throw this.policyUtils.deniedByPolicy(
model,
'create',
`input failed validation: ${fromZodError(err)}`,
`input failed validation: ${getZodErrorMessage(err)}`,
CrudFailureReason.DATA_VALIDATION_VIOLATION,
err
);
Expand Down Expand Up @@ -1267,7 +1266,7 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
throw this.policyUtils.deniedByPolicy(
model,
'update',
`input failed validation: ${fromZodError(err)}`,
`input failed validation: ${getZodErrorMessage(err)}`,
CrudFailureReason.DATA_VALIDATION_VIOLATION,
err
);
Expand Down
13 changes: 9 additions & 4 deletions packages/runtime/src/enhancements/node/policy/policy-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import deepmerge from 'deepmerge';
import { z, type ZodError, type ZodObject, type ZodSchema } from 'zod';
import { fromZodError } from 'zod-validation-error/v3';
import { CrudFailureReason, PrismaErrorCode } from '../../../constants';
import {
clone,
Expand All @@ -14,7 +13,13 @@ import {
type FieldInfo,
type ModelMeta,
} from '../../../cross';
import { isPlainObject, lowerCaseFirst, simpleTraverse, upperCaseFirst } from '../../../local-helpers';
import {
getZodErrorMessage,
isPlainObject,
lowerCaseFirst,
simpleTraverse,
upperCaseFirst,
} from '../../../local-helpers';
import {
AuthUser,
CrudContract,
Expand Down Expand Up @@ -935,7 +940,7 @@ export class PolicyUtil extends QueryUtils {
throw this.deniedByPolicy(
model,
operation,
`entity ${formatObject(uniqueFilter, false)} failed validation: [${fromZodError(err)}]`,
`entity ${formatObject(uniqueFilter, false)} failed validation: [${getZodErrorMessage(err)}]`,
CrudFailureReason.DATA_VALIDATION_VIOLATION,
err
);
Expand Down Expand Up @@ -1440,7 +1445,7 @@ export class PolicyUtil extends QueryUtils {
if (!parseResult.success) {
if (this.logger.enabled('info')) {
this.logger.info(
`entity ${model} failed validation for operation ${kind}: ${fromZodError(parseResult.error)}`
`entity ${model} failed validation for operation ${kind}: ${getZodErrorMessage(parseResult.error)}`
);
}
onError(parseResult.error);
Expand Down
7 changes: 4 additions & 3 deletions packages/runtime/src/local-helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
export * from './simple-traverse';
export * from './sleep';
export * from './is-plain-object';
export * from './lower-case-first';
export * from './upper-case-first';
export * from './param-case';
export * from './simple-traverse';
export * from './sleep';
export * from './tiny-invariant';
export * from './upper-case-first';
export * from './zod-utils';
23 changes: 23 additions & 0 deletions packages/runtime/src/local-helpers/zod-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { type ZodError } from 'zod';
import { fromZodError as fromZodErrorV3 } from 'zod-validation-error/v3';
import { fromZodError as fromZodErrorV4 } from 'zod-validation-error/v4';
import { type ZodError as Zod4Error } from 'zod/v4';

/**
* Formats a Zod error message for better readability. Compatible with both Zod v3 and v4.
*/
export function getZodErrorMessage(err: unknown): string {
if (!(err instanceof Error)) {
return 'Unknown error';
}

try {
if ('_zod' in err) {
return fromZodErrorV4(err as Zod4Error).message;
} else {
return fromZodErrorV3(err as ZodError).message;
}
} catch {
return err.message;
}
}
4 changes: 2 additions & 2 deletions packages/runtime/src/validation.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { z } from 'zod';
import { fromZodError } from 'zod-validation-error/v3';
import { getZodErrorMessage } from './local-helpers';

/**
* Error indicating violations of field-level constraints
Expand All @@ -15,7 +15,7 @@ export function validate(validator: z.ZodType, data: unknown) {
try {
validator.parse(data);
} catch (err) {
throw new ValidationError(fromZodError(err as z.ZodError).message);
throw new ValidationError(getZodErrorMessage(err as z.ZodError));
}
}

Expand Down
3 changes: 1 addition & 2 deletions packages/schema/src/cli/config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import fs from 'fs';
import z, { ZodError } from 'zod';
import { fromZodError } from 'zod-validation-error/v3';
import { CliError } from './cli-error';

// TODO: future use
Expand Down Expand Up @@ -28,7 +27,7 @@ export function loadConfig(filename: string) {
throw new CliError(`Config is not a valid JSON file: ${filename}`);
}
if (err instanceof ZodError) {
throw new CliError(`Config file ${filename} is not valid: ${fromZodError(err)}`);
throw new CliError(`Config file ${filename} is not valid: ${err}`);
}
throw new CliError(`Error loading config: ${filename}`);
}
Expand Down
11 changes: 5 additions & 6 deletions packages/server/src/api/rest/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ import {
PrismaErrorCode,
clone,
enumerate,
requireField,
getIdFields,
isPrismaClientKnownRequestError,
requireField,
} from '@zenstackhq/runtime';
import { lowerCaseFirst, upperCaseFirst, paramCase } from '@zenstackhq/runtime/local-helpers';
import { getZodErrorMessage, lowerCaseFirst, paramCase, upperCaseFirst } from '@zenstackhq/runtime/local-helpers';
import SuperJSON from 'superjson';
import { Linker, Paginator, Relator, Serializer, SerializerOptions } from 'ts-japi';
import UrlPattern from 'url-pattern';
import z, { ZodError } from 'zod';
import { fromZodError } from 'zod-validation-error/v3';
import { LoggerConfig, Response } from '../../types';
import { APIHandlerBase, RequestContext } from '../base';
import { logWarning, registerCustomSerializers } from '../utils';
Expand Down Expand Up @@ -821,7 +820,7 @@ class RequestHandler extends APIHandlerBase {
return {
error: this.makeError(
'invalidPayload',
fromZodError(parsed.error).message,
getZodErrorMessage(parsed.error),
422,
CrudFailureReason.DATA_VALIDATION_VIOLATION,
parsed.error
Expand Down Expand Up @@ -1022,7 +1021,7 @@ class RequestHandler extends APIHandlerBase {
if (!parsed.success) {
return this.makeError(
'invalidPayload',
fromZodError(parsed.error).message,
getZodErrorMessage(parsed.error),
undefined,
CrudFailureReason.DATA_VALIDATION_VIOLATION,
parsed.error
Expand Down Expand Up @@ -1053,7 +1052,7 @@ class RequestHandler extends APIHandlerBase {
if (!parsed.success) {
return this.makeError(
'invalidPayload',
fromZodError(parsed.error).message,
getZodErrorMessage(parsed.error),
undefined,
CrudFailureReason.DATA_VALIDATION_VIOLATION,
parsed.error
Expand Down
Loading