diff --git a/package-lock.json b/package-lock.json index c58adfd..1ef4883 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "license": "PROPRIETARY", "dependencies": { "openai": "^6.48.0", - "zod": "^3.23.0" + "zod": "^4.4.3" }, "devDependencies": { "@eslint/js": "^9.39.5", @@ -2923,9 +2923,9 @@ } }, "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 69abea9..0091d17 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ }, "dependencies": { "openai": "^6.48.0", - "zod": "^3.23.0" + "zod": "^4.4.3" }, "devDependencies": { "@eslint/js": "^9.39.5", diff --git a/tests/zod4-migration.test.ts b/tests/zod4-migration.test.ts new file mode 100644 index 0000000..4e41627 --- /dev/null +++ b/tests/zod4-migration.test.ts @@ -0,0 +1,24 @@ +import { describe, expect, it } from 'vitest'; +import { parseRouterConfig, parseTaskDescriptor, RouterConfigSchema, TaskDescriptorSchema } from '../src/schemas.js'; +import { TaskComplexity, TaskType } from '../src/types.js'; + +describe('Zod 4 migration compatibility', () => { + it('preserves legacy unknown-field stripping', () => { + const parsed = parseTaskDescriptor({ type: TaskType.CLASSIFICATION, complexity: TaskComplexity.LOW, futureField: true }); + expect(parsed).not.toHaveProperty('futureField'); + }); + + it('keeps optional legacy fields optional', () => { + expect(TaskDescriptorSchema.safeParse({ type: TaskType.SCORING, complexity: TaskComplexity.TRIVIAL }).success).toBe(true); + expect(RouterConfigSchema.safeParse({ perplexityApiKey: 'p', openrouterApiKey: 'o' }).success).toBe(true); + }); + + it('does not widen enum validation', () => { + expect(TaskDescriptorSchema.safeParse({ type: 'classification-ish', complexity: TaskComplexity.LOW }).success).toBe(false); + }); + + it('keeps validation errors JSON-safe', () => { + try { parseRouterConfig({ perplexityApiKey: '', openrouterApiKey: '' }); } + catch (error) { expect(() => JSON.stringify(error)).not.toThrow(); } + }); +});