Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b95170a
Upgrade MCP SDK to 1.2.3
kingston Nov 27, 2025
2f540aa
Upgrade Zod package
kingston Nov 27, 2025
e4bcbd5
Remove unused transform with dynamic schema
kingston Nov 27, 2025
148c1fb
Apply first batch of type fixes
kingston Nov 27, 2025
305b1e2
Add z object to service actions
kingston Nov 27, 2025
dee71b3
Apply remainder of zod fixes
kingston Nov 27, 2025
232cbff
Fix tests
kingston Nov 27, 2025
8bab9ef
Fix lint issues
kingston Nov 27, 2025
fbb9dac
Try to fix parsing of admin crud column
kingston Nov 27, 2025
3c6b6b9
Simplify admin crud column with discriminated unions
kingston Nov 27, 2025
478fcc7
Fix crud input types as well
kingston Nov 27, 2025
f3820dd
Convert actions as well
kingston Nov 27, 2025
767f5e9
Sync updates to examples
kingston Nov 27, 2025
b2c5e86
Upgrade zod in examples + use z.int()
kingston Nov 27, 2025
d57ba46
Clean up defaults
kingston Nov 28, 2025
30b0a82
Apply some fixes to deprecated validators for zod v4
kingston Nov 28, 2025
72a6b48
Upgrade tanstack router
kingston Nov 28, 2025
333ed5f
Fix zod 4 errors
kingston Nov 28, 2025
c585914
Fix remaining typecheck errors
kingston Nov 28, 2025
e554f4d
Fix deprecated z.string().email()
kingston Nov 28, 2025
96bb957
Apply proper fix
kingston Nov 28, 2025
80eb0ba
Fix enum types as well
kingston Nov 28, 2025
3d38da6
Fix pnpm dedupe
kingston Nov 28, 2025
5530630
Fix linting errors
kingston Nov 28, 2025
2760997
Fix knip
kingston Nov 28, 2025
03c2d8a
Add changeset
kingston Nov 28, 2025
f42d27a
Fix up linting error from blog with auth
kingston Nov 28, 2025
4da1c20
Fix rest of linting errors
kingston Nov 28, 2025
f891025
Fix linting errors and e2e tests
kingston Nov 28, 2025
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
Prev Previous commit
Next Next commit
Fix zod 4 errors
  • Loading branch information
kingston committed Nov 28, 2025
commit 333ed5f8b70440897dc0052a21a9e629ef502224
Original file line number Diff line number Diff line change
Expand Up @@ -718,9 +718,9 @@ export function defineUpdateOperation<
}

// Validate data unless skipValidation is true (e.g., when GraphQL already validated)
const validatedData = skipValidation
? inputData
: dataSchema.parse(inputData);
const validatedData = (
skipValidation ? inputData : dataSchema.parse(inputData)
) as Partial<InferInput<TFields>>;

let existingItem: GetPayload<TModelName> | undefined;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { BadRequestError } from './http-errors.js';
*/
export const handleZodRequestValidationError = (error: unknown): never => {
if (error instanceof ZodError) {
const formattedErrors = error.errors.map((err) => ({
const formattedErrors = error.issues.map((err) => ({
path: err.path.join('.'),
message: err.message,
code: err.code,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -718,9 +718,9 @@ export function defineUpdateOperation<
}

// Validate data unless skipValidation is true (e.g., when GraphQL already validated)
const validatedData = skipValidation
? inputData
: dataSchema.parse(inputData);
const validatedData = (
skipValidation ? inputData : dataSchema.parse(inputData)
) as Partial<InferInput<TFields>>;
Comment on lines +721 to +723
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Investigate the need for explicit type casting.

The validation result now requires explicit type casting to Partial<InferInput<TFields>>. This suggests a potential type inference issue with the Zod v4 upgrade. Type assertions can hide type errors and should be avoided when possible.

Please verify:

  1. Why is this type assertion necessary with Zod v4?
  2. Whether dataSchema.parse() no longer returns the correct type
  3. If there's a way to fix the underlying type inference issue instead of using as

Run the following script to check if similar type assertions were needed elsewhere:

#!/bin/bash
# Search for similar type assertion patterns in validation code
rg -nP --type=ts 'dataSchema\.parse\([^)]+\)\s*as\s+(Partial<)?InferInput' -C3
🤖 Prompt for AI Agents
In
examples/blog-with-auth/apps/backend/src/utils/data-operations/define-operations.ts
around lines 721-723, the code casts the result of dataSchema.parse(...) to
Partial<InferInput<TFields>> which hides a type-inference mismatch introduced by
the Zod v4 upgrade; fix by ensuring the Zod schema and input types line up
instead of asserting: declare dataSchema with the correct generic return type
(e.g. ZodType<Partial<InferInput<TFields>>> or
ZodSchema<Partial<InferInput<TFields>>> depending on imports), ensure inputData
is typed as Partial<InferInput<TFields>> so the skipValidation branch matches
the parse branch, or use dataSchema.safeParse and narrow the type from its
success value; update the schema/input types accordingly, remove the cast, and
run the provided ripgrep script to find and fix any other occurrences.


let existingItem: GetPayload<TModelName> | undefined;

Expand Down
2 changes: 1 addition & 1 deletion examples/blog-with-auth/apps/backend/src/utils/zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { BadRequestError } from './http-errors.js';
*/
export const handleZodRequestValidationError = (error: unknown): never => {
if (error instanceof ZodError) {
const formattedErrors = error.errors.map((err) => ({
const formattedErrors = error.issues.map((err) => ({
path: err.path.join('.'),
message: err.message,
code: err.code,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,18 @@ function makeFilenameSafe(filename: string): string {
const fileUploadOptionsSchema = z.object({
/** The file name */
filename: z
.string({ required_error: 'File name is required and must be a string' })
.string('File name is required and must be a string')
.max(MAX_FILENAME_LENGTH, {
message: `File name is too long (max ${MAX_FILENAME_LENGTH} characters)`,
}),
/** The file size in bytes */
size: z
.number({
required_error: 'File size is required and must be a positive number',
})
.positive({
message: 'File size is required and must be a positive number',
}),
.number('File size is required and must be a positive number')
.positive('File size is required and must be a positive number'),
/** The content type of the file */
contentType: z.string({ required_error: 'Content type is required' }).min(1),
contentType: z.string('Content type is required').min(1),
/** The category of the file */
category: z.string({ required_error: 'Category is required' }).min(1),
category: z.string('Category is required').min(1),
});

export type FileUploadOptions = z.infer<typeof fileUploadOptionsSchema>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -718,9 +718,9 @@ export function defineUpdateOperation<
}

// Validate data unless skipValidation is true (e.g., when GraphQL already validated)
const validatedData = skipValidation
? inputData
: dataSchema.parse(inputData);
const validatedData = (
skipValidation ? inputData : dataSchema.parse(inputData)
) as Partial<InferInput<TFields>>;

let existingItem: GetPayload<TModelName> | undefined;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { BadRequestError } from './http-errors.js';
*/
export const handleZodRequestValidationError = (error: unknown): never => {
if (error instanceof ZodError) {
const formattedErrors = error.errors.map((err) => ({
const formattedErrors = error.issues.map((err) => ({
path: err.path.join('.'),
message: err.message,
code: err.code,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,18 @@ function makeFilenameSafe(filename: string): string {
const fileUploadOptionsSchema = z.object({
/** The file name */
filename: z
.string({ required_error: 'File name is required and must be a string' })
.string('File name is required and must be a string')
.max(MAX_FILENAME_LENGTH, {
message: `File name is too long (max ${MAX_FILENAME_LENGTH} characters)`,
}),
/** The file size in bytes */
size: z
.number({
required_error: 'File size is required and must be a positive number',
})
.positive({
message: 'File size is required and must be a positive number',
}),
.number('File size is required and must be a positive number')
.positive('File size is required and must be a positive number'),
/** The content type of the file */
contentType: z.string({ required_error: 'Content type is required' }).min(1),
contentType: z.string('Content type is required').min(1),
/** The category of the file */
category: z.string({ required_error: 'Category is required' }).min(1),
category: z.string('Category is required').min(1),
});

export type FileUploadOptions = z.infer<typeof fileUploadOptionsSchema>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -718,9 +718,9 @@ export function defineUpdateOperation<
}

// Validate data unless skipValidation is true (e.g., when GraphQL already validated)
const validatedData = skipValidation
? inputData
: dataSchema.parse(inputData);
const validatedData = (
skipValidation ? inputData : dataSchema.parse(inputData)
) as Partial<InferInput<TFields>>;

let existingItem: GetPayload<TModelName> | undefined;

Expand Down
2 changes: 1 addition & 1 deletion examples/todo-with-auth0/apps/backend/src/utils/zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { BadRequestError } from './http-errors.js';
*/
export const handleZodRequestValidationError = (error: unknown): never => {
if (error instanceof ZodError) {
const formattedErrors = error.errors.map((err) => ({
const formattedErrors = error.issues.map((err) => ({
path: err.path.join('.'),
message: err.message,
code: err.code,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { ZodError } from 'zod';
*/
export const handleZodRequestValidationError = (error: unknown): never => {
if (error instanceof ZodError) {
const formattedErrors = error.errors.map((err) => ({
const formattedErrors = error.issues.map((err) => ({
path: err.path.join('.'),
message: err.message,
code: err.code,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -716,9 +716,9 @@ export function defineUpdateOperation<
}

// Validate data unless skipValidation is true (e.g., when GraphQL already validated)
const validatedData = skipValidation
? inputData
: dataSchema.parse(inputData);
const validatedData = (
skipValidation ? inputData : dataSchema.parse(inputData)
) as Partial<InferInput<TFields>>;

let existingItem: GetPayload<TModelName> | undefined;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"group": "generated",
"pathRootRelativePath": "{src-root}/generated/prisma/client.ts",
"projectExports": {
"*": {},
"$Enums": {},
"*": {},
"Prisma": {},
"PrismaClient": {}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ const prismaPrismaImportsTask = createGeneratorTask({
prismaGeneratedImports: createTsImportMap(
prismaGeneratedImportsSchema,
{
'*': paths.client,
$Enums: paths.client,
'*': paths.client,
Prisma: paths.client,
PrismaClient: paths.client,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const client = createTsTemplateFile({
fileOptions: { kind: 'singleton' },
group: 'generated',
name: 'client',
projectExports: { '*': {}, $Enums: {}, Prisma: {}, PrismaClient: {} },
projectExports: { $Enums: {}, '*': {}, Prisma: {}, PrismaClient: {} },
projectExportsOnly: true,
source: { contents: '' },
variables: {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,18 @@ function makeFilenameSafe(filename: string): string {
const fileUploadOptionsSchema = z.object({
/** The file name */
filename: z
.string({ required_error: 'File name is required and must be a string' })
.string('File name is required and must be a string')
.max(MAX_FILENAME_LENGTH, {
message: `File name is too long (max ${MAX_FILENAME_LENGTH} characters)`,
}),
/** The file size in bytes */
size: z
.number({
required_error: 'File size is required and must be a positive number',
})
.positive({
message: 'File size is required and must be a positive number',
}),
.number('File size is required and must be a positive number')
.positive('File size is required and must be a positive number'),
/** The content type of the file */
contentType: z.string({ required_error: 'Content type is required' }).min(1),
contentType: z.string('Content type is required').min(1),
/** The category of the file */
category: z.string({ required_error: 'Category is required' }).min(1),
category: z.string('Category is required').min(1),
});

export type FileUploadOptions = z.infer<typeof fileUploadOptionsSchema>;
Expand Down