Skip to content

Commit b12f116

Browse files
refactor: apply automated zod v3 to v4 migrations
Automated transformations applied via zod-v3-to-v4: - String validators: z.string().url() → z.url() - Number validators: z.number().int() → z.int() - Object methods: .passthrough() → z.looseObject(), .strict() → z.strictObject() - Schema composition: .merge() → .extend() where applicable - Error messages: { message } → { error } - Defaults: .default() → .prefault() - Object stripping: .strip() removed/consolidated
1 parent e59360c commit b12f116

File tree

10 files changed

+206
-300
lines changed

10 files changed

+206
-300
lines changed

src/examples/server/simpleSseServer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ const getServer = () => {
2828
'start-notification-stream',
2929
'Starts sending periodic notifications',
3030
{
31-
interval: z.number().describe('Interval in milliseconds between notifications').default(1000),
32-
count: z.number().describe('Number of notifications to send').default(10)
31+
interval: z.number().describe('Interval in milliseconds between notifications').prefault(1000),
32+
count: z.number().describe('Number of notifications to send').prefault(10)
3333
},
3434
async ({ interval, count }, extra): Promise<CallToolResult> => {
3535
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

src/examples/server/simpleStatelessStreamableHttp.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ const getServer = () => {
4242
'start-notification-stream',
4343
'Starts sending periodic notifications for testing resumability',
4444
{
45-
interval: z.number().describe('Interval in milliseconds between notifications').default(100),
46-
count: z.number().describe('Number of notifications to send (0 for 100)').default(10)
45+
interval: z.number().describe('Interval in milliseconds between notifications').prefault(100),
46+
count: z.number().describe('Number of notifications to send (0 for 100)').prefault(10)
4747
},
4848
async ({ interval, count }, extra): Promise<CallToolResult> => {
4949
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

src/examples/server/simpleStreamableHttp.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,8 @@ const getServer = () => {
291291
'start-notification-stream',
292292
'Starts sending periodic notifications for testing resumability',
293293
{
294-
interval: z.number().describe('Interval in milliseconds between notifications').default(100),
295-
count: z.number().describe('Number of notifications to send (0 for 100)').default(50)
294+
interval: z.number().describe('Interval in milliseconds between notifications').prefault(100),
295+
count: z.number().describe('Number of notifications to send (0 for 100)').prefault(50)
296296
},
297297
async ({ interval, count }, extra): Promise<CallToolResult> => {
298298
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

src/examples/server/sseAndStreamableHttpCompatibleServer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ const getServer = () => {
3333
'start-notification-stream',
3434
'Starts sending periodic notifications for testing resumability',
3535
{
36-
interval: z.number().describe('Interval in milliseconds between notifications').default(100),
37-
count: z.number().describe('Number of notifications to send (0 for 100)').default(50)
36+
interval: z.number().describe('Interval in milliseconds between notifications').prefault(100),
37+
count: z.number().describe('Number of notifications to send (0 for 100)').prefault(50)
3838
},
3939
async ({ interval, count }, extra): Promise<CallToolResult> => {
4040
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));

src/integration-tests/stateManagementStreamableHttp.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ describe('Streamable HTTP Transport Session Management', () => {
5656
'greet',
5757
'A simple greeting tool',
5858
{
59-
name: z.string().describe('Name to greet').default('World')
59+
name: z.string().describe('Name to greet').prefault('World')
6060
},
6161
async ({ name }) => {
6262
return {

src/integration-tests/taskResumability.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('Transport resumability', () => {
2828
'send-notification',
2929
'Sends a single notification',
3030
{
31-
message: z.string().describe('Message to send').default('Test notification')
31+
message: z.string().describe('Message to send').prefault('Test notification')
3232
},
3333
async ({ message }, { sendNotification }) => {
3434
// Send notification immediately
@@ -51,8 +51,8 @@ describe('Transport resumability', () => {
5151
'run-notifications',
5252
'Sends multiple notifications over time',
5353
{
54-
count: z.number().describe('Number of notifications to send').default(10),
55-
interval: z.number().describe('Interval between notifications in ms').default(50)
54+
count: z.number().describe('Number of notifications to send').prefault(10),
55+
interval: z.number().describe('Interval between notifications in ms').prefault(50)
5656
},
5757
async ({ count, interval }, { sendNotification }) => {
5858
// Send notifications at specified intervals

src/server/auth/handlers/authorize.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ const ClientAuthorizationParamsSchema = z.object({
2121
redirect_uri: z
2222
.string()
2323
.optional()
24-
.refine(value => value === undefined || URL.canParse(value), { message: 'redirect_uri must be a valid URL' })
24+
.refine(value => value === undefined || URL.canParse(value), {
25+
error: 'redirect_uri must be a valid URL'
26+
})
2527
});
2628

2729
// Parameters that must be validated for a successful authorization request. Failure can be reported to the redirect URI.
@@ -31,7 +33,7 @@ const RequestAuthorizationParamsSchema = z.object({
3133
code_challenge_method: z.literal('S256'),
3234
scope: z.string().optional(),
3335
state: z.string().optional(),
34-
resource: z.string().url().optional()
36+
resource: z.url().optional()
3537
});
3638

3739
export function authorizationHandler({ provider, rateLimit: rateLimitConfig }: AuthorizationHandlerOptions): RequestHandler {

src/server/auth/handlers/token.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ const AuthorizationCodeGrantSchema = z.object({
3232
code: z.string(),
3333
code_verifier: z.string(),
3434
redirect_uri: z.string().optional(),
35-
resource: z.string().url().optional()
35+
resource: z.url().optional()
3636
});
3737

3838
const RefreshTokenGrantSchema = z.object({
3939
refresh_token: z.string(),
4040
scope: z.string().optional(),
41-
resource: z.string().url().optional()
41+
resource: z.url().optional()
4242
});
4343

4444
export function tokenHandler({ provider, rateLimit: rateLimitConfig }: TokenHandlerOptions): RequestHandler {

src/shared/auth.ts

Lines changed: 25 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,11 @@ import { z } from 'zod';
33
/**
44
* Reusable URL validation that disallows javascript: scheme
55
*/
6-
export const SafeUrlSchema = z
7-
.string()
8-
.url()
6+
export const SafeUrlSchema = z.url()
97
.superRefine((val, ctx) => {
108
if (!URL.canParse(val)) {
119
ctx.addIssue({
12-
code: z.ZodIssueCode.custom,
10+
code: "custom",
1311
message: 'URL must be parseable',
1412
fatal: true
1513
});
@@ -22,36 +20,35 @@ export const SafeUrlSchema = z
2220
const u = new URL(url);
2321
return u.protocol !== 'javascript:' && u.protocol !== 'data:' && u.protocol !== 'vbscript:';
2422
},
25-
{ message: 'URL cannot use javascript:, data:, or vbscript: scheme' }
23+
{
24+
error: 'URL cannot use javascript:, data:, or vbscript: scheme'
25+
}
2626
);
2727

2828
/**
2929
* RFC 9728 OAuth Protected Resource Metadata
3030
*/
31-
export const OAuthProtectedResourceMetadataSchema = z
32-
.object({
33-
resource: z.string().url(),
31+
export const OAuthProtectedResourceMetadataSchema = z.looseObject({
32+
resource: z.url(),
3433
authorization_servers: z.array(SafeUrlSchema).optional(),
35-
jwks_uri: z.string().url().optional(),
34+
jwks_uri: z.url().optional(),
3635
scopes_supported: z.array(z.string()).optional(),
3736
bearer_methods_supported: z.array(z.string()).optional(),
3837
resource_signing_alg_values_supported: z.array(z.string()).optional(),
3938
resource_name: z.string().optional(),
4039
resource_documentation: z.string().optional(),
41-
resource_policy_uri: z.string().url().optional(),
42-
resource_tos_uri: z.string().url().optional(),
40+
resource_policy_uri: z.url().optional(),
41+
resource_tos_uri: z.url().optional(),
4342
tls_client_certificate_bound_access_tokens: z.boolean().optional(),
4443
authorization_details_types_supported: z.array(z.string()).optional(),
4544
dpop_signing_alg_values_supported: z.array(z.string()).optional(),
4645
dpop_bound_access_tokens_required: z.boolean().optional()
47-
})
48-
.passthrough();
46+
});
4947

5048
/**
5149
* RFC 8414 OAuth 2.0 Authorization Server Metadata
5250
*/
53-
export const OAuthMetadataSchema = z
54-
.object({
51+
export const OAuthMetadataSchema = z.looseObject({
5552
issuer: z.string(),
5653
authorization_endpoint: SafeUrlSchema,
5754
token_endpoint: SafeUrlSchema,
@@ -70,15 +67,13 @@ export const OAuthMetadataSchema = z
7067
introspection_endpoint_auth_methods_supported: z.array(z.string()).optional(),
7168
introspection_endpoint_auth_signing_alg_values_supported: z.array(z.string()).optional(),
7269
code_challenge_methods_supported: z.array(z.string()).optional()
73-
})
74-
.passthrough();
70+
});
7571

7672
/**
7773
* OpenID Connect Discovery 1.0 Provider Metadata
7874
* see: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderMetadata
7975
*/
80-
export const OpenIdProviderMetadataSchema = z
81-
.object({
76+
export const OpenIdProviderMetadataSchema = z.looseObject({
8277
issuer: z.string(),
8378
authorization_endpoint: SafeUrlSchema,
8479
token_endpoint: SafeUrlSchema,
@@ -114,8 +109,7 @@ export const OpenIdProviderMetadataSchema = z
114109
require_request_uri_registration: z.boolean().optional(),
115110
op_policy_uri: SafeUrlSchema.optional(),
116111
op_tos_uri: SafeUrlSchema.optional()
117-
})
118-
.passthrough();
112+
});
119113

120114
/**
121115
* OpenID Connect Discovery metadata that may include OAuth 2.0 fields
@@ -131,16 +125,14 @@ export const OpenIdProviderDiscoveryMetadataSchema = OpenIdProviderMetadataSchem
131125
/**
132126
* OAuth 2.1 token response
133127
*/
134-
export const OAuthTokensSchema = z
135-
.object({
128+
export const OAuthTokensSchema = z.object({
136129
access_token: z.string(),
137130
id_token: z.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect
138131
token_type: z.string(),
139132
expires_in: z.number().optional(),
140133
scope: z.string().optional(),
141134
refresh_token: z.string().optional()
142-
})
143-
.strip();
135+
});
144136

145137
/**
146138
* OAuth 2.1 error response
@@ -159,8 +151,7 @@ export const OptionalSafeUrlSchema = SafeUrlSchema.optional().or(z.literal('').t
159151
/**
160152
* RFC 7591 OAuth 2.0 Dynamic Client Registration metadata
161153
*/
162-
export const OAuthClientMetadataSchema = z
163-
.object({
154+
export const OAuthClientMetadataSchema = z.object({
164155
redirect_uris: z.array(SafeUrlSchema),
165156
token_endpoint_auth_method: z.string().optional(),
166157
grant_types: z.array(z.string()).optional(),
@@ -177,20 +168,17 @@ export const OAuthClientMetadataSchema = z
177168
software_id: z.string().optional(),
178169
software_version: z.string().optional(),
179170
software_statement: z.string().optional()
180-
})
181-
.strip();
171+
});
182172

183173
/**
184174
* RFC 7591 OAuth 2.0 Dynamic Client Registration client information
185175
*/
186-
export const OAuthClientInformationSchema = z
187-
.object({
176+
export const OAuthClientInformationSchema = z.object({
188177
client_id: z.string(),
189178
client_secret: z.string().optional(),
190179
client_id_issued_at: z.number().optional(),
191180
client_secret_expires_at: z.number().optional()
192-
})
193-
.strip();
181+
});
194182

195183
/**
196184
* RFC 7591 OAuth 2.0 Dynamic Client Registration full response (client information plus metadata)
@@ -200,22 +188,18 @@ export const OAuthClientInformationFullSchema = OAuthClientMetadataSchema.merge(
200188
/**
201189
* RFC 7591 OAuth 2.0 Dynamic Client Registration error response
202190
*/
203-
export const OAuthClientRegistrationErrorSchema = z
204-
.object({
191+
export const OAuthClientRegistrationErrorSchema = z.object({
205192
error: z.string(),
206193
error_description: z.string().optional()
207-
})
208-
.strip();
194+
});
209195

210196
/**
211197
* RFC 7009 OAuth 2.0 Token Revocation request
212198
*/
213-
export const OAuthTokenRevocationRequestSchema = z
214-
.object({
199+
export const OAuthTokenRevocationRequestSchema = z.object({
215200
token: z.string(),
216201
token_type_hint: z.string().optional()
217-
})
218-
.strip();
202+
});
219203

220204
export type OAuthMetadata = z.infer<typeof OAuthMetadataSchema>;
221205
export type OpenIdProviderMetadata = z.infer<typeof OpenIdProviderMetadataSchema>;

0 commit comments

Comments
 (0)