diff --git a/package.json b/package.json index 047ab631bd..74382166ba 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@arethetypeswrong/cli": "^0.17.4", "@biomejs/biome": "^1.9.4", "@types/benchmark": "^2.1.5", + "@types/node": "^24.3.0", "@types/semver": "^7.7.0", "@web-std/file": "^3.0.3", "arktype": "^2.1.19", diff --git a/packages/docs/content/api.mdx b/packages/docs/content/api.mdx index a0936948af..86decc89bb 100644 --- a/packages/docs/content/api.mdx +++ b/packages/docs/content/api.mdx @@ -262,6 +262,7 @@ z.ipv6(); z.cidrv4(); // ipv4 CIDR block z.cidrv6(); // ipv6 CIDR block z.hash("sha256"); // or "sha1", "sha384", "sha512", "md5" +z.jsonString(); // validates JSON syntax z.iso.date(); z.iso.time(); z.iso.datetime(); @@ -388,6 +389,40 @@ new URL("HTTP://ExAmPle.com:80/./a/../b?X=1#f oo").href // => "http://example.com/b?X=1#f%20oo" ``` +### JSON Strings + +To validate that a string contains valid JSON syntax: + +```ts +const schema = z.jsonString(); + +schema.parse('{"name": "Alice", "age": 30}'); // ✅ +schema.parse('{"name": "Alice", "age": 30}'); // ✅ (returns original string) +schema.parse("42"); // ✅ +schema.parse("[1, 2, 3]"); // ✅ +schema.parse("invalid json"); // ❌ +``` + +When used without an inner schema, `z.jsonString()` validates JSON syntax and returns the original string. This is useful for validating JSON strings without parsing them. + +To parse JSON and validate the parsed value against a schema: + +```ts +const userSchema = z.object({ + name: z.string(), + age: z.number(), +}); + +const schema = z.jsonString(userSchema); + +schema.parse('{"name": "Alice", "age": 30}'); // ✅ { name: "Alice", age: 30 } +schema.parse('{"name": "Alice", "age": "30"}'); // ❌ (age should be number) +schema.parse('{"name": "Alice"}'); // ❌ (missing age) +schema.parse("invalid json"); // ❌ (invalid JSON syntax) +``` + +The `z.jsonString()` method supports all JSON types (objects, arrays, strings, numbers, booleans, null) and provides proper error reporting for both JSON syntax errors and schema validation errors. + ### ISO datetimes As you may have noticed, Zod string includes a few date/time related validations. These validations are regular expression based, so they are not as strict as a full date/time library. However, they are very convenient for validating user input. diff --git a/packages/docs/content/v4/changelog.mdx b/packages/docs/content/v4/changelog.mdx index b54ddc9268..cc17c27da8 100644 --- a/packages/docs/content/v4/changelog.mdx +++ b/packages/docs/content/v4/changelog.mdx @@ -903,3 +903,25 @@ const longString = z.string().refine((val) => val.length > 10, { - ZodEnum and ZodNativeEnum are merged - `.Values` and `.Enum` are removed. Use `.enum` instead. - `.options` is removed */} + +## New features + +### JSON String validation + +Zod 4 introduces `z.jsonString()` for validating and parsing JSON strings: + +```ts +// Validate JSON syntax only +const schema = z.jsonString(); +schema.parse('{"name": "Alice", "age": 30}'); // ✅ returns original string +schema.parse("invalid json"); // ❌ throws error + +// Parse JSON and validate structure +const userSchema = z.jsonString(z.object({ + name: z.string(), + age: z.number(), +})); +userSchema.parse('{"name": "Alice", "age": 30}'); // ✅ returns { name: "Alice", age: 30 } +``` + +This feature is useful for validating JSON strings from external sources (like API responses or user input) and provides proper error reporting for both JSON syntax errors and schema validation errors. diff --git a/packages/zod/src/v4/classic/schemas.ts b/packages/zod/src/v4/classic/schemas.ts index 08147140eb..9fdb73b8eb 100644 --- a/packages/zod/src/v4/classic/schemas.ts +++ b/packages/zod/src/v4/classic/schemas.ts @@ -2203,3 +2203,30 @@ export function preprocess( ): ZodPipe, U> { return pipe(transform(fn as any), schema as any) as any; } + +// ZodJSONString +export interface ZodJSONString extends ZodStringFormat<"json_string"> { + _zod: core.$ZodJSONStringInternals; +} +export const ZodJSONString: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodJSONString", + (inst, def) => { + core.$ZodJSONString.init(inst, def); + ZodStringFormat.init(inst, def); + } +); + +export function jsonString(params?: string | core.$ZodJSONStringParams): ZodJSONString; +export function jsonString( + inner: T, + params?: string | core.$ZodJSONStringParams +): ZodPipe, T>; +export function jsonString(innerOrParams?: any, maybeParams?: any): any { + if (innerOrParams && typeof innerOrParams === "object" && "_zod" in innerOrParams) { + // inner schema provided: create a pipe String -> JSON.parse -> inner + const inner = innerOrParams as core.$ZodType; + const params = maybeParams; + return core._jsonStringPipe({ Pipe: ZodPipe, Transform: ZodTransform, String: ZodString }, inner, params); + } + return core._jsonString(ZodJSONString, innerOrParams); +} diff --git a/packages/zod/src/v4/classic/tests/jsonString.test.ts b/packages/zod/src/v4/classic/tests/jsonString.test.ts new file mode 100644 index 0000000000..596d01aaef --- /dev/null +++ b/packages/zod/src/v4/classic/tests/jsonString.test.ts @@ -0,0 +1,105 @@ +import { expect, test } from "vitest"; +import * as z from "zod/v4"; + +test("jsonString() validates JSON syntax", () => { + const schema = z.jsonString(); + + // Valid JSON + expect(schema.parse("{}")).toBe("{}"); + expect(schema.parse("42")).toBe("42"); + expect(schema.parse('"hello"')).toBe('"hello"'); + expect(schema.parse("true")).toBe("true"); + expect(schema.parse("null")).toBe("null"); + expect(schema.parse("[1,2,3]")).toBe("[1,2,3]"); + + // Invalid JSON + expect(() => schema.parse("")).toThrow(); + expect(() => schema.parse("invalid")).toThrow(); + expect(() => schema.parse("{")).toThrow(); + expect(() => schema.parse('{"key":}')).toThrow(); + expect(() => schema.parse('{"key": "value",}')).toThrow(); +}); + +test("jsonString() preserves whitespace and formatting", () => { + const schema = z.jsonString(); + expect(schema.parse(' {"key": "value"} ')).toBe(' {"key": "value"} '); + expect(schema.parse('\n\t{"key": "value"}\n')).toBe('\n\t{"key": "value"}\n'); +}); + +test("jsonString() handles unicode and special characters", () => { + const schema = z.jsonString(); + expect(schema.parse('"Hello 世界"')).toBe('"Hello 世界"'); + expect(schema.parse('"🎉🎊🎈"')).toBe('"🎉🎊🎈"'); + expect(schema.parse('"\\"quoted\\""')).toBe('"\\"quoted\\""'); + expect(schema.parse('{"key世界": "value"}')).toBe('{"key世界": "value"}'); +}); + +test("jsonString() rejects malformed nested structures", () => { + const schema = z.jsonString(); + expect(() => schema.parse('{"a": {"b": {"c":}')).toThrow(); + expect(() => schema.parse('{"a": [1, 2, 3}')).toThrow(); + expect(() => schema.parse('{"a": [1, 2, 3]]')).toThrow(); +}); + +test("jsonString() with inner schema parses and validates", () => { + const userSchema = z.object({ + id: z.number(), + name: z.string(), + email: z.string().email(), + }); + const schema = z.jsonString(userSchema); + + // Valid + const valid = schema.parse('{"id": 123, "name": "John", "email": "john@example.com"}'); + expect(valid).toEqual({ id: 123, name: "John", email: "john@example.com" }); + + // Missing fields + const missing = schema.safeParse('{"id": 123}'); + expect(missing.success).toBe(false); + if (!missing.success) { + expect(missing.error.issues.some((issue) => issue.path.includes("name"))).toBe(true); + } + + // Invalid JSON syntax + const invalidSyntax = schema.safeParse('{"id": 123, "name": "John", "email":'); + expect(invalidSyntax.success).toBe(false); + if (!invalidSyntax.success) { + expect(invalidSyntax.error.issues[0].code).toBe("invalid_format"); + expect((invalidSyntax.error.issues[0] as any).format).toBe("json_string"); + } +}); + +test("jsonString() with primitive schemas", () => { + expect(z.jsonString(z.number()).parse("42")).toBe(42); + expect(z.jsonString(z.string()).parse('"hello"')).toBe("hello"); + expect(z.jsonString(z.boolean()).parse("true")).toBe(true); + expect(z.jsonString(z.array(z.number())).parse("[1,2,3]")).toEqual([1, 2, 3]); +}); + +test("jsonString() with union schemas", () => { + const unionSchema = z.union([z.string(), z.number()]); + const schema = z.jsonString(unionSchema); + expect(schema.parse('"hello"')).toBe("hello"); + expect(schema.parse("42")).toBe(42); + expect(() => schema.parse("true")).toThrow(); +}); + +test("jsonString() handles large payloads", () => { + const schema = z.jsonString(); + const largeObject = { + users: Array.from({ length: 100 }, (_, i) => ({ id: i, name: `User ${i}` })), + }; + const largeJson = JSON.stringify(largeObject); + expect(schema.parse(largeJson)).toBe(largeJson); +}); + +test("jsonString() works with async parsing", async () => { + const schema = z.jsonString(z.object({ name: z.string() })); + const result = await schema.parseAsync('{"name": "Alice"}'); + expect(result).toEqual({ name: "Alice" }); +}); + +test("jsonString() works with transforms", () => { + const schema = z.jsonString(z.object({ count: z.number() })).transform((data) => data.count * 2); + expect(schema.parse('{"count": 5}')).toBe(10); +}); diff --git a/packages/zod/src/v4/core/api.ts b/packages/zod/src/v4/core/api.ts index 1206f2b9d7..0262f90840 100644 --- a/packages/zod/src/v4/core/api.ts +++ b/packages/zod/src/v4/core/api.ts @@ -1619,3 +1619,71 @@ export function _stringFormat( const inst = new Class(def); return inst as any; } + +// JSON String +export type $ZodJSONStringParams = StringFormatParams; +export type $ZodCheckJSONStringParams = CheckStringFormatParams; +export function _jsonString( + Class: util.SchemaClass, + params?: string | $ZodJSONStringParams | $ZodCheckJSONStringParams +): T { + return new Class({ + type: "string", + format: "json_string", + check: "string_format", + abort: false, + ...util.normalizeParams(params), + }); +} + +export function _jsonStringPipe( + Classes: { + Pipe?: typeof schemas.$ZodPipe; + Transform?: typeof schemas.$ZodTransform; + String?: typeof schemas.$ZodString; + }, + inner: schemas.$ZodType, + _params?: string | $ZodJSONStringParams | $ZodCheckJSONStringParams +): schemas.$ZodPipe>, typeof inner> { + const params = util.normalizeParams(_params); + + const _Pipe = Classes.Pipe ?? schemas.$ZodPipe; + const _String = Classes.String ?? schemas.$ZodString; + const _Transform = Classes.Transform ?? schemas.$ZodTransform; + + const tx = new _Transform({ + type: "transform", + transform: (input, payload: schemas.ParsePayload) => { + try { + return JSON.parse(input as string); + } catch (_) { + payload.issues.push({ + origin: "string", + code: "invalid_format", + format: "json_string", + input: payload.value as string, + inst: tx, + continue: false, + }); + return {} as never; + } + }, + error: params.error, + }); + + const innerPipe = new _Pipe({ + type: "pipe", + in: new _String({ type: "string", error: params.error }), + out: tx, + error: params.error, + }); + + const outerPipe = new _Pipe({ + type: "pipe", + in: innerPipe, + out: inner, + error: params.error, + }); + + return outerPipe as any; +} diff --git a/packages/zod/src/v4/core/checks.ts b/packages/zod/src/v4/core/checks.ts index 8c5383b589..eec563fbe5 100644 --- a/packages/zod/src/v4/core/checks.ts +++ b/packages/zod/src/v4/core/checks.ts @@ -861,38 +861,34 @@ export const $ZodCheckRegex: core.$constructor<$ZodCheckRegex> = /*@__PURE__*/ c /////////////////////////////////// ///// $ZodCheckJSONString ///// /////////////////////////////////// -// interface $ZodCheckJSONStringDef extends $ZodCheckStringFormatDef<"json_string"> { -// // check: "string_format"; -// // format: "json_string"; -// // error?: errors.$ZodErrorMap | undefined; -// } +interface $ZodCheckJSONStringDef extends $ZodCheckStringFormatDef<"json_string"> {} -// export interface $ZodCheckJSONString extends $ZodCheckStringFormat { -// _def: $ZodCheckJSONStringDef; -// } +export interface $ZodCheckJSONString extends $ZodCheckStringFormat { + _zod: $ZodCheckStringFormatInternals & { def: $ZodCheckJSONStringDef }; +} -// export const $ZodCheckJSONString: core.$constructor<$ZodCheckJSONString> = /*@__PURE__*/ core.$constructor( -// "$ZodCheckJSONString", -// (inst, def) => { -// $ZodCheck.init(inst, def); +export const $ZodCheckJSONString: core.$constructor<$ZodCheckJSONString> = /*@__PURE__*/ core.$constructor( + "$ZodCheckJSONString", + (inst, def) => { + $ZodCheckStringFormat.init(inst, def); -// inst._zod.check = (payload) => { -// try { -// JSON.parse(payload.value); -// return; -// } catch (_) { -// payload.issues.push({ -// origin: "string", -// code: "invalid_format", -// format: def.format, -// input: payload.value, -// inst, -// continue: !def.abort, -// }); -// } -// }; -// } -// ); + inst._zod.check = (payload) => { + try { + JSON.parse(payload.value); + return; + } catch (_) { + payload.issues.push({ + origin: "string", + code: "invalid_format", + format: def.format, + input: payload.value, + inst, + continue: !def.abort, + }); + } + }; + } +); ////////////////////////////////////// ///// $ZodCheckLowerCase ///// diff --git a/packages/zod/src/v4/core/schemas.ts b/packages/zod/src/v4/core/schemas.ts index a32883b30f..359bd4698d 100644 --- a/packages/zod/src/v4/core/schemas.ts +++ b/packages/zod/src/v4/core/schemas.ts @@ -4295,4 +4295,35 @@ export type $ZodStringFormatTypes = | $ZodJWT | $ZodCustomStringFormat<"hex"> | $ZodCustomStringFormat - | $ZodCustomStringFormat<"hostname">; + | $ZodCustomStringFormat<"hostname"> + | $ZodJSONString; + +////////////////////////////// ZodJSONString ////////////////////////////// + +export interface $ZodJSONStringDef extends $ZodStringFormatDef<"json_string"> {} +export interface $ZodJSONStringInternals extends $ZodStringFormatInternals<"json_string"> {} + +export interface $ZodJSONString extends $ZodType { + _zod: $ZodJSONStringInternals; +} + +export const $ZodJSONString: core.$constructor<$ZodJSONString> = /*@__PURE__*/ core.$constructor( + "$ZodJSONString", + (inst, def) => { + $ZodStringFormat.init(inst, def); + inst._zod.check = (payload) => { + try { + JSON.parse(payload.value); + return; + } catch (_) { + payload.issues.push({ + code: "invalid_format", + format: "json_string", + input: payload.value, + inst, + continue: !def.abort, + }); + } + }; + } +); diff --git a/packages/zod/src/v4/mini/schemas.ts b/packages/zod/src/v4/mini/schemas.ts index 1c05c358cc..fdb6e0220e 100644 --- a/packages/zod/src/v4/mini/schemas.ts +++ b/packages/zod/src/v4/mini/schemas.ts @@ -1737,3 +1737,31 @@ export function _function(params?: { } export { _function as function }; + +// ZodMiniJSONString +export interface ZodMiniJSONString extends _ZodMiniString {} +export const ZodMiniJSONString: core.$constructor = /*@__PURE__*/ core.$constructor( + "ZodMiniJSONString", + (inst, def) => { + core.$ZodJSONString.init(inst, def); + ZodMiniStringFormat.init(inst, def); + } +); + +export function jsonString(params?: string | core.$ZodJSONStringParams): ZodMiniJSONString; +export function jsonString( + inner: T, + params?: string | core.$ZodJSONStringParams +): ZodMiniPipe, T>; +export function jsonString(innerOrParams?: any, maybeParams?: any): any { + if (innerOrParams && typeof innerOrParams === "object" && "_zod" in innerOrParams) { + const inner = innerOrParams as core.$ZodType; + const params = maybeParams; + return core._jsonStringPipe( + { Pipe: ZodMiniPipe, Transform: ZodMiniTransform, String: ZodMiniString }, + inner, + params + ); + } + return core._jsonString(ZodMiniJSONString, innerOrParams); +} diff --git a/play.ts b/play.ts index dc872eced9..18a9403518 100644 --- a/play.ts +++ b/play.ts @@ -1,4 +1,4 @@ -import * as z from "zod"; +import { z } from "zod"; z; @@ -13,3 +13,88 @@ const stringToDate = z.codec( console.log(stringToDate.decode("2024-01-15T10:30:00.000Z")); // Date console.log(stringToDate.encode(new Date("2024-01-15"))); // "2024-01-15T00:00:00.000Z" + +// --- Runtime playground below --- +function section(title: string) { + console.log(`\n=== ${title} ===`); +} + +// Simple jsonString examples +section("jsonString basics"); +const jsonSchema = z.jsonString(); +console.log("Valid JSON:", jsonSchema.parse('{"name": "Alice", "age": 30}')); +console.log("Number JSON:", jsonSchema.parse("42")); +console.log("Array JSON:", jsonSchema.parse("[1, 2, 3]")); + +try { + jsonSchema.parse("invalid json"); +} catch (err) { + console.log("Invalid JSON error:", err instanceof z.ZodError ? err.issues[0].code : "unknown error"); +} + +// Basic object with coercion and validations +const UserSchema = z.object({ + id: z.string().uuid(), + name: z.string().min(1), + age: z.coerce.number().int().min(0).optional(), + email: z.string().email().optional(), +}); + +const goodUserInput = { + id: "550e8400-e29b-41d4-a716-446655440000", + name: "Ada", + age: "33", + email: "ada@example.com", +}; +const badUserInput = { + id: "123", // not a uuid + name: "", // too short + age: -5, // below min + email: "not-an-email", +}; + +section("parse (ok)"); +console.log(UserSchema.parse(goodUserInput)); + +section("parse (throws on error)"); +try { + UserSchema.parse(badUserInput); +} catch (err: unknown) { + if (err instanceof z.ZodError) { + console.log(err.issues); + } else { + console.error(err); + } +} + +section("safeParse (success/failure object)"); +console.log(UserSchema.safeParse(badUserInput)); + +// Transform example +const PercentString = z + .string() + .regex(/^\d+%$/) + .transform((value: string) => Number(value.slice(0, -1))); +section("transform"); +console.log(PercentString.parse("42%")); // 42 + +// Discriminated union example +const Shape = z.discriminatedUnion("kind", [ + z.object({ kind: z.literal("circle"), radius: z.number().positive() }), + z.object({ kind: z.literal("square"), size: z.number().positive() }), +]); +section("discriminated union"); +console.log(Shape.parse({ kind: "circle", radius: 2 })); + +// Async refinement example +const EvenNumberAsync = z.number().refine( + async (n: number) => { + await new Promise((r) => setTimeout(r, 10)); + return n % 2 === 0; + }, + { message: "Must be even" } +); +section("async refinement"); +EvenNumberAsync.parseAsync(4) + .then((v: number) => console.log("async ok:", v)) + .catch((e: unknown) => console.log("async error:", e instanceof z.ZodError ? e.issues : e)); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 645297d369..a640641ff5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ importers: '@types/benchmark': specifier: ^2.1.5 version: 2.1.5 + '@types/node': + specifier: ^24.3.0 + version: 24.3.0 '@types/semver': specifier: ^7.7.0 version: 7.7.0 @@ -94,7 +97,7 @@ importers: version: 5.5.4 vitest: specifier: ^2.1.9 - version: 2.1.9(@types/node@22.13.13)(lightningcss@1.29.3)(terser@5.39.0) + version: 2.1.9(@types/node@24.3.0)(lightningcss@1.29.3)(terser@5.39.0) zod: specifier: workspace:* version: link:packages/zod @@ -2609,6 +2612,9 @@ packages: '@types/node@22.13.13': resolution: {integrity: sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==} + '@types/node@24.3.0': + resolution: {integrity: sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==} + '@types/prismjs@1.26.5': resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==} @@ -5383,6 +5389,9 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + undici-types@7.10.0: + resolution: {integrity: sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==} + unicode-emoji-modifier-base@1.0.0: resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} engines: {node: '>=4'} @@ -7712,6 +7721,10 @@ snapshots: dependencies: undici-types: 6.20.0 + '@types/node@24.3.0': + dependencies: + undici-types: 7.10.0 + '@types/prismjs@1.26.5': {} '@types/prop-types@15.7.14': {} @@ -7763,13 +7776,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.9(vite@5.4.18(@types/node@22.13.13)(lightningcss@1.29.3)(terser@5.39.0))': + '@vitest/mocker@2.1.9(vite@5.4.18(@types/node@24.3.0)(lightningcss@1.29.3)(terser@5.39.0))': dependencies: '@vitest/spy': 2.1.9 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 5.4.18(@types/node@22.13.13)(lightningcss@1.29.3)(terser@5.39.0) + vite: 5.4.18(@types/node@24.3.0)(lightningcss@1.29.3)(terser@5.39.0) '@vitest/pretty-format@2.1.9': dependencies: @@ -11097,6 +11110,8 @@ snapshots: undici-types@6.20.0: {} + undici-types@7.10.0: {} + unicode-emoji-modifier-base@1.0.0: {} unicode-trie@2.0.0: @@ -11233,13 +11248,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-node@2.1.9(@types/node@22.13.13)(lightningcss@1.29.3)(terser@5.39.0): + vite-node@2.1.9(@types/node@24.3.0)(lightningcss@1.29.3)(terser@5.39.0): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.6.0 pathe: 1.1.2 - vite: 5.4.18(@types/node@22.13.13)(lightningcss@1.29.3)(terser@5.39.0) + vite: 5.4.18(@types/node@24.3.0)(lightningcss@1.29.3)(terser@5.39.0) transitivePeerDependencies: - '@types/node' - less @@ -11251,21 +11266,21 @@ snapshots: - supports-color - terser - vite@5.4.18(@types/node@22.13.13)(lightningcss@1.29.3)(terser@5.39.0): + vite@5.4.18(@types/node@24.3.0)(lightningcss@1.29.3)(terser@5.39.0): dependencies: esbuild: 0.21.5 postcss: 8.5.3 rollup: 4.39.0 optionalDependencies: - '@types/node': 22.13.13 + '@types/node': 24.3.0 fsevents: 2.3.3 lightningcss: 1.29.3 terser: 5.39.0 - vitest@2.1.9(@types/node@22.13.13)(lightningcss@1.29.3)(terser@5.39.0): + vitest@2.1.9(@types/node@24.3.0)(lightningcss@1.29.3)(terser@5.39.0): dependencies: '@vitest/expect': 2.1.9 - '@vitest/mocker': 2.1.9(vite@5.4.18(@types/node@22.13.13)(lightningcss@1.29.3)(terser@5.39.0)) + '@vitest/mocker': 2.1.9(vite@5.4.18(@types/node@24.3.0)(lightningcss@1.29.3)(terser@5.39.0)) '@vitest/pretty-format': 2.1.9 '@vitest/runner': 2.1.9 '@vitest/snapshot': 2.1.9 @@ -11281,11 +11296,11 @@ snapshots: tinyexec: 0.3.2 tinypool: 1.0.2 tinyrainbow: 1.2.0 - vite: 5.4.18(@types/node@22.13.13)(lightningcss@1.29.3)(terser@5.39.0) - vite-node: 2.1.9(@types/node@22.13.13)(lightningcss@1.29.3)(terser@5.39.0) + vite: 5.4.18(@types/node@24.3.0)(lightningcss@1.29.3)(terser@5.39.0) + vite-node: 2.1.9(@types/node@24.3.0)(lightningcss@1.29.3)(terser@5.39.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.13.13 + '@types/node': 24.3.0 transitivePeerDependencies: - less - lightningcss