From 02a958ac39fc78e2f77261db6ebbcece241c263d Mon Sep 17 00:00:00 2001 From: termorey Date: Wed, 2 Jul 2025 20:44:16 +0300 Subject: [PATCH 01/18] chore: updated zod package --- packages/zod/package.json | 8 ++++---- pnpm-lock.yaml | 9 +++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/packages/zod/package.json b/packages/zod/package.json index edbe9725..47a83b5f 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -7,12 +7,12 @@ "license": "MIT", "repository": "https://github.com/igorkamyshev/farfetched", "devDependencies": { - "zod": "^3.19", - "@farfetched/core": "workspace:*" + "@farfetched/core": "workspace:*", + "zod": "^3.25.0" }, "peerDependencies": { - "zod": "^3.19", - "@farfetched/core": "workspace:*" + "@farfetched/core": "workspace:*", + "zod": "^3.25.0" }, "scripts": { "test:run": "vitest run --typecheck", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 641fb684..d581bf0f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -258,10 +258,10 @@ importers: packages/zod: specifiers: '@farfetched/core': workspace:* - zod: ^3.19 + zod: ^3.25.0 devDependencies: '@farfetched/core': link:../core - zod: 3.19.1 + zod: 3.25.0 packages: @@ -7292,3 +7292,8 @@ packages: /zod/3.19.1: resolution: {integrity: sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==} + dev: false + + /zod/3.25.0: + resolution: {integrity: sha512-ficnZKUW0mlNivqeJkosTEkGbJ6NKCtSaOHGx5aXbtfeWMdRyzXLbAIn19my4C/KB7WPY/p9vlGPt+qpOp6c4Q==} + dev: true From f8887656e1ca2845cab194b97f490f3081fd2279 Mon Sep 17 00:00:00 2001 From: termorey Date: Wed, 2 Jul 2025 21:11:28 +0300 Subject: [PATCH 02/18] chore: changed import on version import --- packages/zod/src/zod_contract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zod/src/zod_contract.ts b/packages/zod/src/zod_contract.ts index 7e69a995..829cce8b 100644 --- a/packages/zod/src/zod_contract.ts +++ b/packages/zod/src/zod_contract.ts @@ -1,4 +1,4 @@ -import { type ZodType, type TypeOf } from 'zod'; +import { type ZodType, type TypeOf } from 'zod/v3'; import { type Contract } from '@farfetched/core'; /** From ec5107f5d1263af4a70a404cc7aeb98092b0ee90 Mon Sep 17 00:00:00 2001 From: termorey Date: Wed, 2 Jul 2025 22:09:47 +0300 Subject: [PATCH 03/18] chore: added version alias import --- packages/zod/src/zod_contract.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/zod/src/zod_contract.ts b/packages/zod/src/zod_contract.ts index 829cce8b..28d8762d 100644 --- a/packages/zod/src/zod_contract.ts +++ b/packages/zod/src/zod_contract.ts @@ -1,15 +1,15 @@ -import { type ZodType, type TypeOf } from 'zod/v3'; +import { type ZodType as ZodTypeV3, type TypeOf as TypeOfV3 } from 'zod/v3'; import { type Contract } from '@farfetched/core'; /** * Transforms Zod contracts for `data` to internal Contract. * Any response which does not conform to `data` will be treated as error. * - * @param {ZodType} data Zod Contract for valid data + * @param {ZodTypeV3} data Zod Contract for valid data */ -function zodContract>( +function zodContract>( data: T -): Contract> { +): Contract> { function isData(prepared: unknown): prepared is T { return data.safeParse(prepared).success; } From b04c00d4b6eba2b1290db53f8f5b794f944bd8ce Mon Sep 17 00:00:00 2001 From: termorey Date: Wed, 2 Jul 2025 23:11:16 +0300 Subject: [PATCH 04/18] chore: changed used errors instance --- packages/zod/src/zod_contract.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zod/src/zod_contract.ts b/packages/zod/src/zod_contract.ts index 28d8762d..df0b3ac5 100644 --- a/packages/zod/src/zod_contract.ts +++ b/packages/zod/src/zod_contract.ts @@ -22,7 +22,7 @@ function zodContract>( return []; } - return validation.error.errors.map((e) => { + return validation.error.issues.map((e) => { const path = e.path.join('.'); return path !== '' ? `${e.message}, path: ${path}` : e.message; }); From 32767ba8050a027a5775552faeb91450e73ba00e Mon Sep 17 00:00:00 2001 From: termorey Date: Fri, 4 Jul 2025 23:02:50 +0300 Subject: [PATCH 05/18] chore: zod v4 and v4-mini support --- packages/zod/src/zod_contract.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/zod/src/zod_contract.ts b/packages/zod/src/zod_contract.ts index df0b3ac5..8ebeca35 100644 --- a/packages/zod/src/zod_contract.ts +++ b/packages/zod/src/zod_contract.ts @@ -1,4 +1,5 @@ -import { type ZodType as ZodTypeV3, type TypeOf as TypeOfV3 } from 'zod/v3'; +import { type ZodType as ZodTypeV3 } from 'zod/v3'; +import { type $ZodType as ZodTypeV4, safeParse } from 'zod/v4/core'; import { type Contract } from '@farfetched/core'; /** @@ -7,17 +8,20 @@ import { type Contract } from '@farfetched/core'; * * @param {ZodTypeV3} data Zod Contract for valid data */ -function zodContract>( - data: T -): Contract> { +function zodContract( + data: ZodTypeV3 | ZodTypeV4 +): Contract { function isData(prepared: unknown): prepared is T { + if ("_zod" in data) return safeParse(data, prepared).success; return data.safeParse(prepared).success; } return { isData, getErrorMessages(raw) { - const validation = data.safeParse(raw); + const validation = ("_zod" in data) + ? safeParse(data, raw) + : data.safeParse(raw); if (validation.success) { return []; } From 0585674bcc687dc6b6b58a83c2c2780573e39f7f Mon Sep 17 00:00:00 2001 From: termorey Date: Sat, 5 Jul 2025 17:51:55 +0300 Subject: [PATCH 06/18] chore: versioning zod for test --- packages/zod/src/__tests__/contract.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/zod/src/__tests__/contract.test.ts b/packages/zod/src/__tests__/contract.test.ts index 08c85734..c7a1c502 100644 --- a/packages/zod/src/__tests__/contract.test.ts +++ b/packages/zod/src/__tests__/contract.test.ts @@ -1,9 +1,11 @@ -import { z as zod } from 'zod'; +import { z as zod_v3 } from 'zod/v3'; import { describe, test, expect } from 'vitest'; import { zodContract } from '../zod_contract'; -describe('zod/zodContract short', () => { +describe('zod/zodContract short (zod v3)', () => { + const zod = zod_v3; + test('interprets invalid response as error', () => { const contract = zodContract(zod.string()); From ea195291dc2c37e49c25e777f763d68f7579718c Mon Sep 17 00:00:00 2001 From: termorey Date: Sat, 5 Jul 2025 18:19:04 +0300 Subject: [PATCH 07/18] chore: added zod v4 test --- packages/zod/src/__tests__/contract.test.ts | 120 ++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/packages/zod/src/__tests__/contract.test.ts b/packages/zod/src/__tests__/contract.test.ts index c7a1c502..eb825b54 100644 --- a/packages/zod/src/__tests__/contract.test.ts +++ b/packages/zod/src/__tests__/contract.test.ts @@ -1,4 +1,5 @@ import { z as zod_v3 } from 'zod/v3'; +import { z as zod_v4 } from 'zod/v4'; import { describe, test, expect } from 'vitest'; import { zodContract } from '../zod_contract'; @@ -121,3 +122,122 @@ describe('zod/zodContract short (zod v3)', () => { `); }); }); + +describe('zod/zodContract short (zod v4)', () => { + const zod = zod_v4; + + test('interprets invalid response as error', () => { + const contract = zodContract(zod.string()); + + expect(contract.getErrorMessages(2)).toMatchInlineSnapshot(` + [ + "Invalid input: expected string, received number", + ] + `); + }); + + test('passes valid data', () => { + const contract = zodContract(zod.string()); + + expect(contract.getErrorMessages('foo')).toEqual([]); + }); + + test('isData passes for valid data', () => { + const contract = zodContract( + zod.object({ + x: zod.number(), + y: zod.string(), + }) + ); + + expect( + contract.isData({ + x: 42, + y: 'answer', + }) + ).toEqual(true); + }); + + test('isData does not pass for invalid data', () => { + const contract = zodContract( + zod.object({ + x: zod.number(), + y: zod.string(), + }) + ); + + expect( + contract.isData({ + 42: 'x', + answer: 'y', + }) + ).toEqual(false); + }); + + test('interprets complex invalid response as error', () => { + const contract = zodContract( + zod.tuple([ + zod.object({ + x: zod.number(), + y: zod.literal(true), + k: zod + .set(zod.string()) + .nonempty('Invalid input: expected set of strings'), + }), + zod.literal('Uhm?'), + zod.literal(42), + ]) + ); + + expect( + contract.getErrorMessages([ + { + x: 456, + y: false, + k: new Set(), + }, + 'Answer is:', + '42', + ]) + ).toMatchInlineSnapshot(` + [ + "Invalid input: expected true, path: 0.y", + "Invalid input: expected set of strings, path: 0.k", + "Invalid input: expected "Uhm?", path: 1", + "Invalid input: expected 42, path: 2", + ] + `); + }); + + test('path from original zod error included in final message', () => { + const contract = zodContract( + zod.object({ + x: zod.number(), + y: zod.object({ + z: zod.string(), + k: zod.object({ + j: zod.boolean(), + }), + }), + }) + ); + + expect( + contract.getErrorMessages({ + x: '42', + y: { + z: 123, + k: { + j: new Map(), + }, + }, + }) + ).toMatchInlineSnapshot(` + [ + "Invalid input: expected number, received string, path: x", + "Invalid input: expected string, received number, path: y.z", + "Invalid input: expected boolean, received Map, path: y.k.j", + ] + `); + }); +}); From 40b7cb8f9be1e8d3d189ddb8e6b2dc3371fa6e35 Mon Sep 17 00:00:00 2001 From: termorey Date: Sat, 5 Jul 2025 18:28:42 +0300 Subject: [PATCH 08/18] chore: added zod v4-mini test --- packages/zod/src/__tests__/contract.test.ts | 118 ++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/packages/zod/src/__tests__/contract.test.ts b/packages/zod/src/__tests__/contract.test.ts index eb825b54..1ffcaa20 100644 --- a/packages/zod/src/__tests__/contract.test.ts +++ b/packages/zod/src/__tests__/contract.test.ts @@ -1,5 +1,6 @@ import { z as zod_v3 } from 'zod/v3'; import { z as zod_v4 } from 'zod/v4'; +import { z as zod_v4mini } from 'zod/v4-mini'; import { describe, test, expect } from 'vitest'; import { zodContract } from '../zod_contract'; @@ -241,3 +242,120 @@ describe('zod/zodContract short (zod v4)', () => { `); }); }); + +describe('zod/zodContract short (zod v4-mini)', () => { + const zod = zod_v4mini; + + test('interprets invalid response as error', () => { + const contract = zodContract(zod.string()); + + expect(contract.getErrorMessages(2)).toMatchInlineSnapshot(` + [ + "Invalid input: expected string, received number", + ] + `); + }); + + test('passes valid data', () => { + const contract = zodContract(zod.string()); + + expect(contract.getErrorMessages('foo')).toEqual([]); + }); + + test('isData passes for valid data', () => { + const contract = zodContract( + zod.object({ + x: zod.number(), + y: zod.string(), + }) + ); + + expect( + contract.isData({ + x: 42, + y: 'answer', + }) + ).toEqual(true); + }); + + test('isData does not pass for invalid data', () => { + const contract = zodContract( + zod.object({ + x: zod.number(), + y: zod.string(), + }) + ); + + expect( + contract.isData({ + 42: 'x', + answer: 'y', + }) + ).toEqual(false); + }); + + test('interprets complex invalid response as error', () => { + const contract = zodContract( + zod.tuple([ + zod.object({ + x: zod.number(), + y: zod.literal(true), + k: zod.set(zod.string(), { error: 'Invalid input: expected set of strings' }), + }), + zod.literal('Uhm?'), + zod.literal(42), + ]) + ); + + expect( + contract.getErrorMessages([ + { + x: 456, + y: false, + k: new Map(), + }, + 'Answer is:', + '42', + ]) + ).toMatchInlineSnapshot(` + [ + "Invalid input: expected true, path: 0.y", + "Invalid input: expected set of strings, path: 0.k", + "Invalid input: expected "Uhm?", path: 1", + "Invalid input: expected 42, path: 2", + ] + `); + }); + + test('path from original zod error included in final message', () => { + const contract = zodContract( + zod.object({ + x: zod.number(), + y: zod.object({ + z: zod.string(), + k: zod.object({ + j: zod.boolean(), + }), + }), + }) + ); + + expect( + contract.getErrorMessages({ + x: '42', + y: { + z: 123, + k: { + j: new Map(), + }, + }, + }) + ).toMatchInlineSnapshot(` + [ + "Invalid input: expected number, received string, path: x", + "Invalid input: expected string, received number, path: y.z", + "Invalid input: expected boolean, received Map, path: y.k.j", + ] + `); + }); +}); From 1043f91245925e6ac07fa27e0d7264a8035ac20b Mon Sep 17 00:00:00 2001 From: termorey Date: Sat, 5 Jul 2025 18:32:41 +0300 Subject: [PATCH 09/18] chore: versioning zod for test --- packages/zod/src/__tests__/contract.test-d.ts | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/zod/src/__tests__/contract.test-d.ts b/packages/zod/src/__tests__/contract.test-d.ts index c9d3b854..3f663032 100644 --- a/packages/zod/src/__tests__/contract.test-d.ts +++ b/packages/zod/src/__tests__/contract.test-d.ts @@ -1,11 +1,11 @@ import { describe, test, expectTypeOf } from 'vitest'; -import { z as zod } from 'zod'; +import { z as zodV3 } from 'zod/v3'; import { zodContract } from '../zod_contract'; -describe('zodContract', () => { +describe('zodContract (zod v3)', () => { test('string', () => { - const stringContract = zodContract(zod.string()); + const stringContract = zodContract(zodV3.string()); const smth: unknown = null; @@ -17,14 +17,14 @@ describe('zodContract', () => { test('complex object', () => { const complexContract = zodContract( - zod.tuple([ - zod.object({ - x: zod.number(), - y: zod.literal(false), - k: zod.set(zod.string()), + zodV3.tuple([ + zodV3.object({ + x: zodV3.number(), + y: zodV3.literal(false), + k: zodV3.set(zodV3.string()), }), - zod.literal('literal'), - zod.literal(42), + zodV3.literal('literal'), + zodV3.literal(42), ]) ); @@ -60,15 +60,15 @@ describe('zodContract', () => { }); test('branded type', () => { - const BrandedContainer = zod.object({ - branded: zod.string().brand<'Branded'>(), + const BrandedContainer = zodV3.object({ + branded: zodV3.string().brand<'Branded'>(), }); const brandedContract = zodContract(BrandedContainer); const smth: unknown = { branded: 'branded' }; if (brandedContract.isData(smth)) { - expectTypeOf(smth).toEqualTypeOf>(); + expectTypeOf(smth).toEqualTypeOf>(); } }); }); From f12b6af43571f672f7273e0dee7d5124d321aa50 Mon Sep 17 00:00:00 2001 From: termorey Date: Sat, 5 Jul 2025 19:32:15 +0300 Subject: [PATCH 10/18] chore: types improvements --- packages/zod/src/zod_contract.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/zod/src/zod_contract.ts b/packages/zod/src/zod_contract.ts index 8ebeca35..c8d2305f 100644 --- a/packages/zod/src/zod_contract.ts +++ b/packages/zod/src/zod_contract.ts @@ -1,17 +1,27 @@ -import { type ZodType as ZodTypeV3 } from 'zod/v3'; -import { type $ZodType as ZodTypeV4, safeParse } from 'zod/v4/core'; +import { type ZodType as ZodTypeV3, type TypeOf as TypeOfV3 } from 'zod/v3'; +import { type $ZodType as ZodTypeV4, type output as TypeOfV4, safeParse } from 'zod/v4/core'; import { type Contract } from '@farfetched/core'; +type ZodAnyType = + | ZodTypeV3 + | ZodTypeV4; +type Output = + T extends ZodTypeV4 + ? TypeOfV4 + : T extends ZodTypeV3 + ? TypeOfV3 + : never; + /** * Transforms Zod contracts for `data` to internal Contract. * Any response which does not conform to `data` will be treated as error. * - * @param {ZodTypeV3} data Zod Contract for valid data + * @param {ZodTypeV3 | ZodTypeV4} data Zod Contract for valid data */ -function zodContract( - data: ZodTypeV3 | ZodTypeV4 -): Contract { - function isData(prepared: unknown): prepared is T { +function zodContract( + data: T +): Contract> { + function isData(prepared: unknown): prepared is Output { if ("_zod" in data) return safeParse(data, prepared).success; return data.safeParse(prepared).success; } From 6b64349543459c952b334999608bb843d5f0f0bd Mon Sep 17 00:00:00 2001 From: termorey Date: Sat, 5 Jul 2025 19:46:21 +0300 Subject: [PATCH 11/18] chore: added zod v4 test --- packages/zod/src/__tests__/contract.test-d.ts | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/packages/zod/src/__tests__/contract.test-d.ts b/packages/zod/src/__tests__/contract.test-d.ts index 3f663032..c54548f9 100644 --- a/packages/zod/src/__tests__/contract.test-d.ts +++ b/packages/zod/src/__tests__/contract.test-d.ts @@ -1,5 +1,6 @@ import { describe, test, expectTypeOf } from 'vitest'; import { z as zodV3 } from 'zod/v3'; +import { z as zodV4 } from 'zod/v4'; import { zodContract } from '../zod_contract'; @@ -72,3 +73,73 @@ describe('zodContract (zod v3)', () => { } }); }); + +describe('zodContract (zod v4)', () => { + test('string', () => { + const stringContract = zodContract(zodV4.string()); + + const smth: unknown = null; + + if (stringContract.isData(smth)) { + expectTypeOf(smth).toEqualTypeOf(); + expectTypeOf(smth).not.toEqualTypeOf(); + } + }); + + test('complex object', () => { + const complexContract = zodContract( + zodV4.tuple([ + zodV4.object({ + x: zodV4.number(), + y: zodV4.literal(false), + k: zodV4.set(zodV4.string()), + }), + zodV4.literal('literal'), + zodV4.literal(42), + ]) + ); + + const smth: unknown = null; + + if (complexContract.isData(smth)) { + expectTypeOf(smth).toEqualTypeOf< + [ + { + x: number; + y: false; + k: Set; + }, + 'literal', + 42, + ] + >(); + + expectTypeOf(smth).not.toEqualTypeOf(); + + expectTypeOf(smth).not.toEqualTypeOf< + [ + { + x: string; + y: false; + k: Set; + }, + 'literal', + 42, + ] + >(); + } + }); + + test('branded type', () => { + const BrandedContainer = zodV4.object({ + branded: zodV4.string().brand<'Branded'>(), + }); + const brandedContract = zodContract(BrandedContainer); + + const smth: unknown = { branded: 'branded' }; + + if (brandedContract.isData(smth)) { + expectTypeOf(smth).toEqualTypeOf>(); + } + }); +}); From d242f6f8560f981329cf79bc0e24c0596c7b6c4f Mon Sep 17 00:00:00 2001 From: termorey Date: Sat, 5 Jul 2025 19:47:25 +0300 Subject: [PATCH 12/18] chore: added zod v4-mini test --- packages/zod/src/__tests__/contract.test-d.ts | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/packages/zod/src/__tests__/contract.test-d.ts b/packages/zod/src/__tests__/contract.test-d.ts index c54548f9..d1508c3e 100644 --- a/packages/zod/src/__tests__/contract.test-d.ts +++ b/packages/zod/src/__tests__/contract.test-d.ts @@ -1,6 +1,7 @@ import { describe, test, expectTypeOf } from 'vitest'; import { z as zodV3 } from 'zod/v3'; import { z as zodV4 } from 'zod/v4'; +import { z as zodV4mini } from 'zod/v4-mini'; import { zodContract } from '../zod_contract'; @@ -143,3 +144,73 @@ describe('zodContract (zod v4)', () => { } }); }); + +describe('zodContract (zod v4-mini)', () => { + test('string', () => { + const stringContract = zodContract(zodV4mini.string()); + + const smth: unknown = null; + + if (stringContract.isData(smth)) { + expectTypeOf(smth).toEqualTypeOf(); + expectTypeOf(smth).not.toEqualTypeOf(); + } + }); + + test('complex object', () => { + const complexContract = zodContract( + zodV4mini.tuple([ + zodV4mini.object({ + x: zodV4mini.number(), + y: zodV4mini.literal(false), + k: zodV4mini.set(zodV4mini.string()), + }), + zodV4mini.literal('literal'), + zodV4mini.literal(42), + ]) + ); + + const smth: unknown = null; + + if (complexContract.isData(smth)) { + expectTypeOf(smth).toEqualTypeOf< + [ + { + x: number; + y: false; + k: Set; + }, + 'literal', + 42, + ] + >(); + + expectTypeOf(smth).not.toEqualTypeOf(); + + expectTypeOf(smth).not.toEqualTypeOf< + [ + { + x: string; + y: false; + k: Set; + }, + 'literal', + 42, + ] + >(); + } + }); + + test('branded type', () => { + const BrandedContainer = zodV4mini.object({ + branded: zodV4mini.string().brand<'Branded'>(), + }); + const brandedContract = zodContract(BrandedContainer); + + const smth: unknown = { branded: 'branded' }; + + if (brandedContract.isData(smth)) { + expectTypeOf(smth).toEqualTypeOf>(); + } + }); +}); From 11d77297dac0e99b8901d86f3d0184f8abe2cf2f Mon Sep 17 00:00:00 2001 From: termorey Date: Sun, 6 Jul 2025 00:00:27 +0300 Subject: [PATCH 13/18] chore: updated zod version --- packages/zod/package.json | 2 +- pnpm-lock.yaml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/zod/package.json b/packages/zod/package.json index 47a83b5f..853e0390 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -8,7 +8,7 @@ "repository": "https://github.com/igorkamyshev/farfetched", "devDependencies": { "@farfetched/core": "workspace:*", - "zod": "^3.25.0" + "zod": "^3.25.67" }, "peerDependencies": { "@farfetched/core": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d581bf0f..6a6c7c18 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -258,10 +258,10 @@ importers: packages/zod: specifiers: '@farfetched/core': workspace:* - zod: ^3.25.0 + zod: ^3.25.67 devDependencies: '@farfetched/core': link:../core - zod: 3.25.0 + zod: 3.25.67 packages: @@ -7294,6 +7294,6 @@ packages: resolution: {integrity: sha512-LYjZsEDhCdYET9ikFu6dVPGp2YH9DegXjdJToSzD9rO6fy4qiRYFoyEYwps88OseJlPyl2NOe2iJuhEhL7IpEA==} dev: false - /zod/3.25.0: - resolution: {integrity: sha512-ficnZKUW0mlNivqeJkosTEkGbJ6NKCtSaOHGx5aXbtfeWMdRyzXLbAIn19my4C/KB7WPY/p9vlGPt+qpOp6c4Q==} + /zod/3.25.67: + resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} dev: true From 5a50ea97fb42212c43da31130badbb3d2275e2d8 Mon Sep 17 00:00:00 2001 From: termorey Date: Sun, 6 Jul 2025 13:12:18 +0300 Subject: [PATCH 14/18] chore: changed external imports list --- packages/zod/vite.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zod/vite.config.ts b/packages/zod/vite.config.ts index 01bd9593..e32b7362 100644 --- a/packages/zod/vite.config.ts +++ b/packages/zod/vite.config.ts @@ -20,7 +20,7 @@ export default { formats: ['es', 'cjs'], }, rollupOptions: { - external: ['zod'], + external: ['zod', 'zod/v3', 'zod/v4/core'], }, }, }; From f3b1fb45d83506c30d91af3d85ac7150bed4982d Mon Sep 17 00:00:00 2001 From: termorey Date: Sun, 6 Jul 2025 13:41:13 +0300 Subject: [PATCH 15/18] chore: prettier code --- packages/zod/src/__tests__/contract.test-d.ts | 4 ++- packages/zod/src/__tests__/contract.test.ts | 4 ++- packages/zod/src/zod_contract.ts | 28 +++++++++---------- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/packages/zod/src/__tests__/contract.test-d.ts b/packages/zod/src/__tests__/contract.test-d.ts index d1508c3e..ef8c3781 100644 --- a/packages/zod/src/__tests__/contract.test-d.ts +++ b/packages/zod/src/__tests__/contract.test-d.ts @@ -210,7 +210,9 @@ describe('zodContract (zod v4-mini)', () => { const smth: unknown = { branded: 'branded' }; if (brandedContract.isData(smth)) { - expectTypeOf(smth).toEqualTypeOf>(); + expectTypeOf(smth).toEqualTypeOf< + zodV4mini.infer + >(); } }); }); diff --git a/packages/zod/src/__tests__/contract.test.ts b/packages/zod/src/__tests__/contract.test.ts index 1ffcaa20..025315a7 100644 --- a/packages/zod/src/__tests__/contract.test.ts +++ b/packages/zod/src/__tests__/contract.test.ts @@ -300,7 +300,9 @@ describe('zod/zodContract short (zod v4-mini)', () => { zod.object({ x: zod.number(), y: zod.literal(true), - k: zod.set(zod.string(), { error: 'Invalid input: expected set of strings' }), + k: zod.set(zod.string(), { + error: 'Invalid input: expected set of strings', + }), }), zod.literal('Uhm?'), zod.literal(42), diff --git a/packages/zod/src/zod_contract.ts b/packages/zod/src/zod_contract.ts index c8d2305f..75b1834b 100644 --- a/packages/zod/src/zod_contract.ts +++ b/packages/zod/src/zod_contract.ts @@ -1,16 +1,17 @@ import { type ZodType as ZodTypeV3, type TypeOf as TypeOfV3 } from 'zod/v3'; -import { type $ZodType as ZodTypeV4, type output as TypeOfV4, safeParse } from 'zod/v4/core'; +import { + type $ZodType as ZodTypeV4, + type output as TypeOfV4, + safeParse, +} from 'zod/v4/core'; import { type Contract } from '@farfetched/core'; -type ZodAnyType = - | ZodTypeV3 - | ZodTypeV4; -type Output = - T extends ZodTypeV4 - ? TypeOfV4 - : T extends ZodTypeV3 - ? TypeOfV3 - : never; +type ZodAnyType = ZodTypeV3 | ZodTypeV4; +type Output = T extends ZodTypeV4 + ? TypeOfV4 + : T extends ZodTypeV3 + ? TypeOfV3 + : never; /** * Transforms Zod contracts for `data` to internal Contract. @@ -22,16 +23,15 @@ function zodContract( data: T ): Contract> { function isData(prepared: unknown): prepared is Output { - if ("_zod" in data) return safeParse(data, prepared).success; + if ('_zod' in data) return safeParse(data, prepared).success; return data.safeParse(prepared).success; } return { isData, getErrorMessages(raw) { - const validation = ("_zod" in data) - ? safeParse(data, raw) - : data.safeParse(raw); + const validation = + '_zod' in data ? safeParse(data, raw) : data.safeParse(raw); if (validation.success) { return []; } From 5e7e312ddbe97029ac02b913c33765a9734adae6 Mon Sep 17 00:00:00 2001 From: termorey Date: Wed, 9 Jul 2025 18:30:55 +0300 Subject: [PATCH 16/18] chore: unify version check --- packages/zod/src/zod_contract.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/zod/src/zod_contract.ts b/packages/zod/src/zod_contract.ts index 75b1834b..79bb053c 100644 --- a/packages/zod/src/zod_contract.ts +++ b/packages/zod/src/zod_contract.ts @@ -12,6 +12,9 @@ type Output = T extends ZodTypeV4 : T extends ZodTypeV3 ? TypeOfV3 : never; +function isZodV4(schema: unknown): schema is ZodTypeV4 { + return !!schema && typeof schema === "object" && "_zod" in schema; +} /** * Transforms Zod contracts for `data` to internal Contract. @@ -23,7 +26,7 @@ function zodContract( data: T ): Contract> { function isData(prepared: unknown): prepared is Output { - if ('_zod' in data) return safeParse(data, prepared).success; + if (isZodV4(data)) return safeParse(data, prepared).success; return data.safeParse(prepared).success; } @@ -31,7 +34,7 @@ function zodContract( isData, getErrorMessages(raw) { const validation = - '_zod' in data ? safeParse(data, raw) : data.safeParse(raw); + isZodV4(data) ? safeParse(data, raw) : data.safeParse(raw); if (validation.success) { return []; } From 5ffdf47016b501546d86551fa6e3421b347096f3 Mon Sep 17 00:00:00 2001 From: termorey Date: Wed, 9 Jul 2025 18:46:15 +0300 Subject: [PATCH 17/18] chore: prettier code --- packages/zod/src/zod_contract.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/zod/src/zod_contract.ts b/packages/zod/src/zod_contract.ts index 79bb053c..ea14816b 100644 --- a/packages/zod/src/zod_contract.ts +++ b/packages/zod/src/zod_contract.ts @@ -13,7 +13,7 @@ type Output = T extends ZodTypeV4 ? TypeOfV3 : never; function isZodV4(schema: unknown): schema is ZodTypeV4 { - return !!schema && typeof schema === "object" && "_zod" in schema; + return !!schema && typeof schema === 'object' && '_zod' in schema; } /** @@ -33,8 +33,9 @@ function zodContract( return { isData, getErrorMessages(raw) { - const validation = - isZodV4(data) ? safeParse(data, raw) : data.safeParse(raw); + const validation = isZodV4(data) + ? safeParse(data, raw) + : data.safeParse(raw); if (validation.success) { return []; } From c9d9ac54e9816fe0858aa1329f0c05e7b8f9fefa Mon Sep 17 00:00:00 2001 From: termorey Date: Thu, 17 Jul 2025 12:17:19 +0300 Subject: [PATCH 18/18] chore: changed possible package version --- packages/zod/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/zod/package.json b/packages/zod/package.json index 853e0390..123dbfad 100644 --- a/packages/zod/package.json +++ b/packages/zod/package.json @@ -12,7 +12,7 @@ }, "peerDependencies": { "@farfetched/core": "workspace:*", - "zod": "^3.25.0" + "zod": "^3.25.0 || ^4.0.0" }, "scripts": { "test:run": "vitest run --typecheck",