Skip to content

Commit 14252b2

Browse files
fix(deps): update dependency zod to v4 (#15373)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Florian Lefebvre <[email protected]>
1 parent bf0b4bf commit 14252b2

20 files changed

Lines changed: 160 additions & 124 deletions

File tree

.changeset/strict-regions-drop.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@astrojs/sitemap': patch
3+
'@astrojs/rss': patch
4+
'@astrojs/db': patch
5+
---
6+
7+
Updates zod to v4

packages/astro-rss/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { z } from 'zod';
1+
import * as z from 'zod/v4';
22
import { XMLBuilder, XMLParser } from 'fast-xml-parser';
33
import colors from 'piccolore';
44
import { rssSchema } from './schema.js';

packages/astro-rss/src/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { z } from 'zod';
1+
import * as z from 'zod/v4';
22

33
export const rssSchema = z.object({
44
title: z.string().optional(),

packages/astro-rss/test/rss.test.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import assert from 'node:assert/strict';
22
import { describe, it } from 'node:test';
3-
4-
import { z } from 'zod';
3+
import * as z from 'zod/v4';
54
import rss, { getRssString } from '../dist/index.js';
65
import { rssSchema } from '../dist/schema.js';
76
import {

packages/astro/src/core/session/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import z from 'zod/v4';
1+
import * as z from 'zod/v4';
22

33
export const SessionDriverConfigSchema = z.object({
44
config: z.record(z.string(), z.any()).optional(),

packages/astro/test/types/schemas.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { describe, it } from 'node:test';
22
import { expectTypeOf } from 'expect-type';
3-
import type z from 'zod';
3+
import type * as z from 'zod/v4';
44
import { type FontProviderSchema, FontFamilySchema } from '../../src/assets/fonts/config.js';
55
import type { FontProvider, FontFamily } from '../../src/assets/fonts/types.js';
66
import type { SessionDriverConfigSchema } from '../../dist/core/session/config.js';

packages/db/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
"piccolore": "^0.1.3",
8080
"prompts": "^2.4.2",
8181
"yargs-parser": "^22.0.0",
82-
"zod": "^3.25.76"
82+
"zod": "^4.3.6"
8383
},
8484
"devDependencies": {
8585
"@types/deep-diff": "^1.0.5",

packages/db/src/core/db-client/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { Config as LibSQLConfig } from '@libsql/client';
2-
import z from 'zod/v3';
2+
import * as z from 'zod/v4';
33

4-
const rawLibSQLOptions = z.record(z.string());
4+
const rawLibSQLOptions = z.record(z.string(), z.string());
55

66
const parseNumber = (value: string) => z.coerce.number().parse(value);
77
const parseBoolean = (value: string) => z.coerce.boolean().parse(value);
@@ -50,7 +50,7 @@ export const parseLibSQLConfig = (config: Record<string, string>): Partial<LibSQ
5050
return libSQLConfigTransformed.parse(config);
5151
} catch (error) {
5252
if (error instanceof z.ZodError) {
53-
throw new Error(`Invalid LibSQL config: ${error.errors.map((e) => e.message).join(', ')}`);
53+
throw new Error(`Invalid LibSQL config: ${error.issues.map((e) => e.message).join(', ')}`);
5454
}
5555
throw error;
5656
}
Lines changed: 86 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,132 @@
1-
/**
2-
* This is a modified version of Astro's error map. source:
3-
* https://github.com/withastro/astro/blob/main/packages/astro/src/content/error-map.ts
4-
*/
5-
import type { z } from 'zod/v3';
1+
import type { $ZodErrorMap } from 'zod/v4/core';
62

7-
interface TypeOrLiteralErrByPathEntry {
3+
type TypeOrLiteralErrByPathEntry = {
84
code: 'invalid_type' | 'invalid_literal';
95
received: unknown;
106
expected: unknown[];
11-
}
7+
message: string | undefined;
8+
};
129

13-
export const errorMap: z.ZodErrorMap = (baseError, ctx) => {
14-
const baseErrorPath = flattenErrorPath(baseError.path);
15-
if (baseError.code === 'invalid_union') {
10+
export const errorMap: $ZodErrorMap = (issue) => {
11+
const baseErrorPath = flattenErrorPath(issue.path ?? []);
12+
if (issue.code === 'invalid_union') {
1613
// Optimization: Combine type and literal errors for keys that are common across ALL union types
1714
// Ex. a union between `{ key: z.literal('tutorial') }` and `{ key: z.literal('blog') }` will
1815
// raise a single error when `key` does not match:
1916
// > Did not match union.
2017
// > key: Expected `'tutorial' | 'blog'`, received 'foo'
21-
const typeOrLiteralErrByPath = new Map<string, TypeOrLiteralErrByPathEntry>();
22-
for (const unionError of baseError.unionErrors.flatMap((e) => e.errors)) {
23-
if (unionError.code === 'invalid_type' || unionError.code === 'invalid_literal') {
18+
let typeOrLiteralErrByPath = new Map<string, TypeOrLiteralErrByPathEntry>();
19+
for (const unionError of issue.errors.flat()) {
20+
if (unionError.code === 'invalid_type') {
2421
const flattenedErrorPath = flattenErrorPath(unionError.path);
25-
const typeOrLiteralErr = typeOrLiteralErrByPath.get(flattenedErrorPath);
26-
if (typeOrLiteralErr) {
27-
typeOrLiteralErr.expected.push(unionError.expected);
22+
if (typeOrLiteralErrByPath.has(flattenedErrorPath)) {
23+
typeOrLiteralErrByPath.get(flattenedErrorPath)!.expected.push(unionError.expected);
2824
} else {
2925
typeOrLiteralErrByPath.set(flattenedErrorPath, {
3026
code: unionError.code,
3127
received: (unionError as any).received,
3228
expected: [unionError.expected],
29+
message: unionError.message,
3330
});
3431
}
3532
}
3633
}
37-
const messages: string[] = [
38-
prefix(
39-
baseErrorPath,
40-
typeOrLiteralErrByPath.size ? 'Did not match union:' : 'Did not match union.',
41-
),
42-
];
34+
const messages: string[] = [prefix(baseErrorPath, 'Did not match union.')];
35+
const details: string[] = [...typeOrLiteralErrByPath.entries()]
36+
// If type or literal error isn't common to ALL union types,
37+
// filter it out. Can lead to confusing noise.
38+
.filter(([, error]) => error.expected.length === issue.errors.flat().length)
39+
.map(([key, error]) =>
40+
key === baseErrorPath
41+
? // Avoid printing the key again if it's a base error
42+
`> ${getTypeOrLiteralMsg(error)}`
43+
: `> ${prefix(key, getTypeOrLiteralMsg(error))}`,
44+
);
45+
46+
if (details.length === 0) {
47+
const expectedShapes: string[] = [];
48+
for (const unionErrors of issue.errors) {
49+
const expectedShape: string[] = [];
50+
for (const _issue of unionErrors) {
51+
// If the issue is a nested union error, show the associated error message instead of the
52+
// base error message.
53+
if (_issue.code === 'invalid_union') {
54+
return errorMap(_issue as any);
55+
}
56+
const relativePath = flattenErrorPath(_issue.path)
57+
.replace(baseErrorPath, '')
58+
.replace(leadingPeriod, '');
59+
if ('expected' in _issue && typeof _issue.expected === 'string') {
60+
expectedShape.push(
61+
relativePath ? `${relativePath}: ${_issue.expected}` : _issue.expected,
62+
);
63+
} else if ('values' in _issue) {
64+
expectedShape.push(
65+
..._issue.values.filter((v) => typeof v === 'string').map((v) => `"${v}"`),
66+
);
67+
} else if (relativePath) {
68+
expectedShape.push(relativePath);
69+
}
70+
}
71+
if (expectedShape.length === 1 && !expectedShape[0]?.includes(':')) {
72+
// In this case the expected shape is not an object, but probably a literal type, e.g. `['string']`.
73+
expectedShapes.push(expectedShape.join(''));
74+
} else if (expectedShape.length > 0) {
75+
expectedShapes.push(`{ ${expectedShape.join('; ')} }`);
76+
}
77+
}
78+
if (expectedShapes.length) {
79+
details.push('> Expected type `' + expectedShapes.join(' | ') + '`');
80+
details.push('> Received `' + stringify(issue.input) + '`');
81+
}
82+
}
83+
4384
return {
44-
message: messages
45-
.concat(
46-
[...typeOrLiteralErrByPath.entries()]
47-
// If type or literal error isn't common to ALL union types,
48-
// filter it out. Can lead to confusing noise.
49-
.filter(([, error]) => error.expected.length === baseError.unionErrors.length)
50-
.map(([key, error]) =>
51-
// Avoid printing the key again if it's a base error
52-
key === baseErrorPath
53-
? `> ${getTypeOrLiteralMsg(error)}`
54-
: `> ${prefix(key, getTypeOrLiteralMsg(error))}`,
55-
),
56-
)
57-
.join('\n'),
85+
message: messages.concat(details).join('\n'),
5886
};
59-
}
60-
if (baseError.code === 'invalid_literal' || baseError.code === 'invalid_type') {
87+
} else if (issue.code === 'invalid_type') {
6188
return {
6289
message: prefix(
6390
baseErrorPath,
6491
getTypeOrLiteralMsg({
65-
code: baseError.code,
66-
received: (baseError as any).received,
67-
expected: [baseError.expected],
92+
code: issue.code,
93+
received: typeof issue.input,
94+
expected: [issue.expected],
95+
message: issue.message,
6896
}),
6997
),
7098
};
71-
} else if (baseError.message) {
72-
return { message: prefix(baseErrorPath, baseError.message) };
73-
} else {
74-
return { message: prefix(baseErrorPath, ctx.defaultError) };
99+
} else if (issue.message) {
100+
return { message: prefix(baseErrorPath, issue.message) };
75101
}
76102
};
77103

78104
const getTypeOrLiteralMsg = (error: TypeOrLiteralErrByPathEntry): string => {
79-
if (error.received === 'undefined') return 'Required';
105+
// received could be `undefined` or the string `'undefined'`
106+
if (typeof error.received === 'undefined' || error.received === 'undefined')
107+
return error.message ?? 'Required';
80108
const expectedDeduped = new Set(error.expected);
81109
switch (error.code) {
82110
case 'invalid_type':
83-
return `Expected type \`${unionExpectedVals(expectedDeduped)}\`, received ${JSON.stringify(
111+
return `Expected type \`${unionExpectedVals(expectedDeduped)}\`, received \`${stringify(
84112
error.received,
85-
)}`;
113+
)}\``;
86114
case 'invalid_literal':
87-
return `Expected \`${unionExpectedVals(expectedDeduped)}\`, received ${JSON.stringify(
115+
return `Expected \`${unionExpectedVals(expectedDeduped)}\`, received \`${stringify(
88116
error.received,
89-
)}`;
117+
)}\``;
90118
}
91119
};
92120

93121
const prefix = (key: string, msg: string) => (key.length ? `**${key}**: ${msg}` : msg);
94122

95123
const unionExpectedVals = (expectedVals: Set<unknown>) =>
96-
[...expectedVals]
97-
.map((expectedVal, idx) => {
98-
if (idx === 0) return JSON.stringify(expectedVal);
99-
const sep = ' | ';
100-
return `${sep}${JSON.stringify(expectedVal)}`;
101-
})
102-
.join('');
124+
[...expectedVals].map((expectedVal) => stringify(expectedVal)).join(' | ');
125+
126+
const flattenErrorPath = (errorPath: (string | number | symbol)[]) => errorPath.join('.');
103127

104-
const flattenErrorPath = (errorPath: Array<string | number>) => errorPath.join('.');
128+
/** `JSON.stringify()` a value with spaces around object/array entries. */
129+
const stringify = (val: unknown) =>
130+
JSON.stringify(val, null, 1).split(newlinePlusWhitespace).join(' ');
131+
const newlinePlusWhitespace = /\n\s*/;
132+
const leadingPeriod = /^\./;

packages/db/src/core/integration/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
type ViteDevServer,
1515
} from 'vite';
1616
import parseArgs from 'yargs-parser';
17-
import { z } from 'zod/v3';
17+
import * as z from 'zod/v4';
1818
import { AstroDbError, isDbError } from '../../runtime/utils.js';
1919
import { CONFIG_FILE_NAMES, DB_PATH, VIRTUAL_MODULE_ID } from '../consts.js';
2020
import { EXEC_DEFAULT_EXPORT_ERROR, EXEC_ERROR } from '../errors.js';
@@ -46,7 +46,7 @@ const astroDBConfigSchema = z
4646
.default('node'),
4747
})
4848
.optional()
49-
.default({});
49+
.prefault({});
5050

5151
export type AstroDBConfig = z.infer<typeof astroDBConfigSchema>;
5252

0 commit comments

Comments
 (0)