Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
9 changes: 5 additions & 4 deletions packages/plugins/openapi/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import z from 'zod';
* Zod schema for OpenAPI security schemes: https://swagger.io/docs/specification/authentication/
*/
export const SecuritySchemesSchema = z.record(
z.string(),
z.union([
z.object({ type: z.literal('http'), scheme: z.literal('basic') }),
z.object({ type: z.literal('http'), scheme: z.literal('bearer'), bearerFormat: z.string().optional() }),
Expand All @@ -20,22 +21,22 @@ export const SecuritySchemesSchema = z.record(
authorizationUrl: z.string(),
tokenUrl: z.string(),
refreshUrl: z.string(),
scopes: z.record(z.string()),
scopes: z.record(z.string(), z.string()),
}),
implicit: z.object({
authorizationUrl: z.string(),
refreshUrl: z.string(),
scopes: z.record(z.string()),
scopes: z.record(z.string(), z.string()),
}),
password: z.object({
tokenUrl: z.string(),
refreshUrl: z.string(),
scopes: z.record(z.string()),
scopes: z.record(z.string(), z.string()),
}),
clientCredentials: z.object({
tokenUrl: z.string(),
refreshUrl: z.string(),
scopes: z.record(z.string()),
scopes: z.record(z.string(), z.string()),
}),
}),
}),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"trpc-nuxt": "^0.10.22",
"vue": "latest",
"vue-router": "latest",
"zod": "^3.25.0"
"zod": "^4.0.0"
},
"devDependencies": {
"esbuild": "^0.24.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"trpc-nuxt": "^0.11.0-beta.1",
"vue": "latest",
"vue-router": "latest",
"zod": "^3.25.0"
"zod": "^4.0.0"
},
"devDependencies": {
"esbuild": "^0.24.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"react-dom": "^18.3.1",
"server-only": "^0.0.1",
"superjson": "^2.2.1",
"zod": "^3.25.0"
"zod": "^4.0.0"
},
"devDependencies": {
"@types/eslint": "^8.56.10",
Expand Down
23 changes: 12 additions & 11 deletions packages/runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,6 @@
"uuid": "^9.0.0",
"zod-validation-error": "catalog:"
},
"peerDependencies": {
"@prisma/client": "5.0.0 - 6.15.x",
"zod": "catalog:"
},
"author": {
"name": "ZenStack Team"
},
"homepage": "https://zenstack.dev",
"license": "MIT",
"devDependencies": {
"@types/bcryptjs": "^2.4.2",
"@types/pluralize": "^0.0.29",
Expand All @@ -129,6 +120,16 @@
"@types/uuid": "^8.3.4",
"decimal.js-light": "^2.5.1",
"superjson": "^1.13.0",
"uuid": "^9.0.0"
}
"uuid": "^9.0.0",
"zod": "^4.0.0"
},
"peerDependencies": {
"@prisma/client": "5.0.0 - 6.15.x",
"zod": "catalog:"
},
"author": {
"name": "ZenStack Team"
},
"homepage": "https://zenstack.dev",
"license": "MIT"
}
2 changes: 1 addition & 1 deletion packages/runtime/src/enhancements/node/policy/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1262,7 +1262,7 @@ export class PolicyProxyHandler<DbClient extends DbClientContract> implements Pr
{}
);

const validatedData = this.policyUtils.validateZodSchema(model, 'update', literalData, false, (err) => {
const validatedData: any = this.policyUtils.validateZodSchema(model, 'update', literalData, false, (err) => {
throw this.policyUtils.deniedByPolicy(
model,
'update',
Expand Down
11 changes: 10 additions & 1 deletion packages/runtime/src/zod-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,14 @@ function countUnrecognizedKeys(issues: Z.ZodIssue[]) {
}

function unwrapLazy<T extends Z.ZodSchema>(z: typeof Z, schema: T | Z.ZodLazy<T>): T {
return schema instanceof z.ZodLazy ? schema.schema : schema;
if (!(schema instanceof z.ZodLazy)) {
return schema;
}
if ('unwrap' in schema && typeof schema.unwrap === 'function') {
return schema.unwrap();
} else if ('schema' in schema) {
return schema.schema as T;
} else {
throw new Error('Unable to determine how to unwrap a lazy schema with this zod version.');
}
}
12 changes: 10 additions & 2 deletions packages/schema/src/plugins/zod/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,15 @@ export class ZodSchemaGenerator {
path.join(output, 'common', 'index.ts'),
`
import { z } from 'zod';
export const DecimalSchema = z.union([z.number(), z.string(), z.object({d: z.number().array(), e: z.number(), s: z.number()}).passthrough()]);
export const DecimalSchema = z.any().refine((val) => {
if (typeof val === 'string' || typeof val === 'number') {
return val;
}
// Decimal.js shape
if (typeof val === 'object' && val && Array.isArray(val.d) && typeof val.e === 'number' && typeof val.s === 'number') {
return val;
}
});
`,
{ overwrite: true }
)
Expand Down Expand Up @@ -553,7 +561,7 @@ export const ${upperCaseFirst(model.name)}PrismaCreateSchema = ${prismaCreateSch
.map((field) => {
let fieldSchema = makeFieldSchema(field, false);
if (field.type.type === 'Int' || field.type.type === 'Float') {
fieldSchema = `z.union([${fieldSchema}, z.record(z.unknown())])`;
fieldSchema = `z.union([${fieldSchema}, z.record(z.string(), z.unknown())])`;
}
return `\t${field.name}: ${fieldSchema}`;
})
Expand Down
2 changes: 1 addition & 1 deletion packages/schema/src/plugins/zod/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -486,7 +486,7 @@ export const ${this.name}ObjectSchema: SchemaType = ${schema} as SchemaType;`;
jsonSchemaImplementation += `\n`;
jsonSchemaImplementation += `const literalSchema = z.union([z.string(), z.number(), z.boolean()]);\n`;
jsonSchemaImplementation += `const jsonSchema: z.ZodType<Prisma.InputJsonValue> = z.lazy(() =>\n`;
jsonSchemaImplementation += ` z.union([literalSchema, z.array(jsonSchema.nullable()), z.record(jsonSchema.nullable())])\n`;
jsonSchemaImplementation += ` z.union([literalSchema, z.array(jsonSchema.nullable()), z.record(z.string(), jsonSchema.nullable())])\n`;
jsonSchemaImplementation += `);\n\n`;
}

Expand Down
4 changes: 3 additions & 1 deletion packages/schema/src/plugins/zod/utils/schema-gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export function makeFieldSchema(field: DataModelField | TypeDefField, addDefault
// array field is always optional
return `z.array(z.unknown()).optional()`;
} else {
return field.type.optional ? `z.record(z.unknown()).optional()` : `z.record(z.unknown())`;
return field.type.optional
? `z.record(z.string(), z.unknown()).optional()`
: `z.record(z.string(), z.unknown())`;
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
"superjson": "^1.13.0",
"ts-japi": "^1.10.1",
"url-pattern": "^1.0.3",
"zod-validation-error": "catalog:",
"decimal.js": "^10.4.2"
},
"peerDependencies": {
Expand All @@ -57,7 +56,8 @@
"next": "14.2.4",
"nuxt": "^3.7.4",
"reflect-metadata": "^0.2.2",
"supertest": "^6.3.3"
"supertest": "^6.3.3",
"zod": "^4.0.0"
},
"exports": {
"./package.json": "./package.json",
Expand Down
3 changes: 2 additions & 1 deletion packages/server/src/api/rest/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ class RequestHandler extends APIHandlerBase {
attributes: z.object({}).passthrough().optional(),
relationships: z
.record(
z.string(),
z.object({
data: z.union([
z.object({ type: z.string(), id: z.union([z.string(), z.number()]) }),
Expand Down Expand Up @@ -1973,7 +1974,7 @@ class RequestHandler extends APIHandlerBase {
detail?: string,
status?: number,
reason?: string,
zodErrors?: ZodError
zodErrors?: ZodError<any>
) {
const error: any = {
status: status ?? this.errors[code].status,
Expand Down
8 changes: 4 additions & 4 deletions packages/server/src/api/rpc/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import {
isPrismaClientUnknownRequestError,
isPrismaClientValidationError,
} from '@zenstackhq/runtime';
import { upperCaseFirst } from '@zenstackhq/runtime/local-helpers';
import { upperCaseFirst, getZodErrorMessage } from '@zenstackhq/runtime/local-helpers';
import SuperJSON from 'superjson';
import { ZodError } from 'zod';
import { fromZodError } from 'zod-validation-error/v3';
import { Response } from '../../types';
import { APIHandlerBase, RequestContext } from '../base';
import { logError, registerCustomSerializers } from '../utils';
Expand Down Expand Up @@ -202,7 +201,8 @@ class RequestHandler extends APIHandlerBase {
}
}

private makeError(message: string, reason?: string, zodErrors?: ZodError) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private makeError(message: string, reason?: string, zodErrors?: ZodError<any>) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const error: any = { message, reason };
if (reason === CrudFailureReason.ACCESS_POLICY_VIOLATION || reason === CrudFailureReason.RESULT_NOT_READABLE) {
Expand Down Expand Up @@ -251,7 +251,7 @@ class RequestHandler extends APIHandlerBase {
} else {
return {
data: undefined,
error: fromZodError(parseResult.error).message,
error: getZodErrorMessage(parseResult.error),
zodErrors: parseResult.error,
};
}
Expand Down
41 changes: 19 additions & 22 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 @@ -7,5 +7,5 @@ packages:
catalog:
ts-morph: ^26.0.0
typescript: ~5.8.0
zod: ^3.25.0
zod: ^4.0.0
zod-validation-error: ^4.0.0
2 changes: 1 addition & 1 deletion script/test-scaffold.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function run(cmd: string) {

run('npm init -y');
run(
'npm i --no-audit --no-fund typescript@~5.8.0 [email protected] @prisma/[email protected] zod@^3.25.0 decimal.js @types/node'
'npm i --no-audit --no-fund typescript@~5.8.0 [email protected] @prisma/[email protected] zod@^4.0.0 decimal.js @types/node'
);

console.log('Test scaffold setup complete.');
2 changes: 1 addition & 1 deletion tests/integration/test-run/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
"react": "^18.2.0",
"swr": "^1.3.0",
"zenstack": "file:../../../packages/schema/dist",
"zod": "^3.25.0"
"zod": "^4.0.0"
}
}
2 changes: 1 addition & 1 deletion tests/integration/tests/cli/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ model Post {
// set up project
fs.writeFileSync('package.json', JSON.stringify({ name: 'my app', version: '1.0.0' }));
createNpmrc();
installPackage('prisma @prisma/client zod@^3.25.0');
installPackage('prisma @prisma/client zod@^4.0.0');
installPackage(path.join(__dirname, '../../../../packages/runtime/dist'));

// set up schema
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/tests/cli/plugins.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe('CLI Plugins Tests', () => {
// deps
const ver = require('../../../../packages/schema/package.json').version;
const depPkgs = [
'zod@^3.25.0',
'zod@^4.0.0',
'react',
'swr',
'@tanstack/[email protected]',
Expand Down
Loading
Loading