zod 4.1.0-canary.20250821T014902 → 4.1.0-canary.20250823T064644

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/package.json +1 -1
  2. package/src/v3/tests/object.test.ts +2 -2
  3. package/src/v4/classic/external.ts +0 -1
  4. package/src/v4/classic/parse.ts +49 -0
  5. package/src/v4/classic/schemas.ts +145 -7
  6. package/src/v4/classic/tests/array.test.ts +6 -6
  7. package/src/v4/classic/tests/catch.test.ts +25 -0
  8. package/src/v4/classic/tests/codec-examples.test.ts +538 -0
  9. package/src/v4/classic/tests/codec.test.ts +532 -0
  10. package/src/v4/classic/tests/continuability.test.ts +1 -1
  11. package/src/v4/classic/tests/default.test.ts +32 -0
  12. package/src/v4/classic/tests/firstparty.test.ts +4 -0
  13. package/src/v4/classic/tests/function.test.ts +31 -31
  14. package/src/v4/classic/tests/hash.test.ts +68 -0
  15. package/src/v4/classic/tests/nonoptional.test.ts +15 -0
  16. package/src/v4/classic/tests/object.test.ts +33 -2
  17. package/src/v4/classic/tests/pipe.test.ts +25 -5
  18. package/src/v4/classic/tests/prefault.test.ts +25 -0
  19. package/src/v4/classic/tests/preprocess.test.ts +1 -6
  20. package/src/v4/classic/tests/refine.test.ts +78 -5
  21. package/src/v4/classic/tests/set.test.ts +1 -1
  22. package/src/v4/classic/tests/string-formats.test.ts +16 -0
  23. package/src/v4/classic/tests/string.test.ts +82 -1
  24. package/src/v4/classic/tests/stringbool.test.ts +40 -0
  25. package/src/v4/classic/tests/template-literal.test.ts +1 -1
  26. package/src/v4/classic/tests/to-json-schema.test.ts +21 -2
  27. package/src/v4/classic/tests/transform.test.ts +7 -0
  28. package/src/v4/classic/tests/union.test.ts +1 -1
  29. package/src/v4/core/api.ts +25 -35
  30. package/src/v4/core/core.ts +7 -26
  31. package/src/v4/core/index.ts +0 -1
  32. package/src/v4/core/json-schema.ts +1 -0
  33. package/src/v4/core/parse.ts +101 -0
  34. package/src/v4/core/regexes.ts +40 -1
  35. package/src/v4/core/schemas.ts +521 -129
  36. package/src/v4/core/to-json-schema.ts +43 -8
  37. package/src/v4/core/util.ts +73 -0
  38. package/src/v4/mini/external.ts +0 -1
  39. package/src/v4/mini/parse.ts +14 -1
  40. package/src/v4/mini/schemas.ts +153 -12
  41. package/src/v4/mini/tests/codec.test.ts +499 -0
  42. package/src/v4/mini/tests/object.test.ts +9 -0
  43. package/src/v4/mini/tests/string.test.ts +16 -0
  44. package/v4/classic/external.cjs +1 -2
  45. package/v4/classic/external.d.cts +1 -1
  46. package/v4/classic/external.d.ts +1 -1
  47. package/v4/classic/external.js +1 -1
  48. package/v4/classic/parse.cjs +10 -1
  49. package/v4/classic/parse.d.cts +8 -0
  50. package/v4/classic/parse.d.ts +8 -0
  51. package/v4/classic/parse.js +9 -0
  52. package/v4/classic/schemas.cjs +59 -4
  53. package/v4/classic/schemas.d.cts +48 -2
  54. package/v4/classic/schemas.d.ts +48 -2
  55. package/v4/classic/schemas.js +51 -3
  56. package/v4/core/api.cjs +19 -24
  57. package/v4/core/api.d.cts +3 -4
  58. package/v4/core/api.d.ts +3 -4
  59. package/v4/core/api.js +19 -24
  60. package/v4/core/core.cjs +8 -1
  61. package/v4/core/core.d.cts +3 -0
  62. package/v4/core/core.d.ts +3 -0
  63. package/v4/core/core.js +6 -0
  64. package/v4/core/index.cjs +0 -1
  65. package/v4/core/index.d.cts +0 -1
  66. package/v4/core/index.d.ts +0 -1
  67. package/v4/core/index.js +0 -1
  68. package/v4/core/json-schema.d.cts +1 -0
  69. package/v4/core/json-schema.d.ts +1 -0
  70. package/v4/core/parse.cjs +45 -1
  71. package/v4/core/parse.d.cts +24 -0
  72. package/v4/core/parse.d.ts +24 -0
  73. package/v4/core/parse.js +36 -0
  74. package/v4/core/regexes.cjs +34 -2
  75. package/v4/core/regexes.d.cts +16 -0
  76. package/v4/core/regexes.d.ts +16 -0
  77. package/v4/core/regexes.js +32 -1
  78. package/v4/core/schemas.cjs +309 -77
  79. package/v4/core/schemas.d.cts +61 -3
  80. package/v4/core/schemas.d.ts +61 -3
  81. package/v4/core/schemas.js +308 -76
  82. package/v4/core/to-json-schema.cjs +42 -5
  83. package/v4/core/to-json-schema.d.cts +4 -3
  84. package/v4/core/to-json-schema.d.ts +4 -3
  85. package/v4/core/to-json-schema.js +42 -5
  86. package/v4/core/util.cjs +69 -0
  87. package/v4/core/util.d.cts +10 -0
  88. package/v4/core/util.d.ts +10 -0
  89. package/v4/core/util.js +62 -0
  90. package/v4/mini/external.cjs +1 -2
  91. package/v4/mini/external.d.cts +1 -1
  92. package/v4/mini/external.d.ts +1 -1
  93. package/v4/mini/external.js +1 -1
  94. package/v4/mini/parse.cjs +9 -1
  95. package/v4/mini/parse.d.cts +1 -1
  96. package/v4/mini/parse.d.ts +1 -1
  97. package/v4/mini/parse.js +1 -1
  98. package/v4/mini/schemas.cjs +58 -3
  99. package/v4/mini/schemas.d.cts +49 -1
  100. package/v4/mini/schemas.d.ts +49 -1
  101. package/v4/mini/schemas.js +49 -2
  102. package/src/v4/core/function.ts +0 -176
  103. package/v4/core/function.cjs +0 -102
  104. package/v4/core/function.d.cts +0 -52
  105. package/v4/core/function.d.ts +0 -52
  106. package/v4/core/function.js +0 -75
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod",
3
- "version": "4.1.0-canary.20250821T014902",
3
+ "version": "4.1.0-canary.20250823T064644",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Colin McDonnell <zod@colinhacks.com>",
@@ -152,13 +152,13 @@ test("catchall overrides strict", () => {
152
152
  });
153
153
  });
154
154
 
155
- test("test that optional keys are unset", async () => {
155
+ test("test that optional keys are unset", () => {
156
156
  const SNamedEntity = z.object({
157
157
  id: z.string(),
158
158
  set: z.string().optional(),
159
159
  unset: z.string().optional(),
160
160
  });
161
- const result = await SNamedEntity.parse({
161
+ const result = SNamedEntity.parse({
162
162
  id: "asdf",
163
163
  set: undefined,
164
164
  });
@@ -16,7 +16,6 @@ export {
16
16
  type GlobalMeta,
17
17
  registry,
18
18
  config,
19
- function,
20
19
  $output,
21
20
  $input,
22
21
  $brand,
@@ -31,3 +31,52 @@ export const safeParseAsync: <T extends core.$ZodType>(
31
31
  value: unknown,
32
32
  _ctx?: core.ParseContext<core.$ZodIssue>
33
33
  ) => Promise<ZodSafeParseResult<core.output<T>>> = /* @__PURE__ */ core._safeParseAsync(ZodRealError) as any;
34
+
35
+ // Codec functions
36
+ export const encode: <T extends core.$ZodType>(
37
+ schema: T,
38
+ value: core.output<T>,
39
+ _ctx?: core.ParseContext<core.$ZodIssue>
40
+ ) => core.input<T> = /* @__PURE__ */ core._encode(ZodRealError) as any;
41
+
42
+ export const decode: <T extends core.$ZodType>(
43
+ schema: T,
44
+ value: core.input<T>,
45
+ _ctx?: core.ParseContext<core.$ZodIssue>
46
+ ) => core.output<T> = /* @__PURE__ */ core._decode(ZodRealError) as any;
47
+
48
+ export const encodeAsync: <T extends core.$ZodType>(
49
+ schema: T,
50
+ value: core.output<T>,
51
+ _ctx?: core.ParseContext<core.$ZodIssue>
52
+ ) => Promise<core.input<T>> = /* @__PURE__ */ core._encodeAsync(ZodRealError) as any;
53
+
54
+ export const decodeAsync: <T extends core.$ZodType>(
55
+ schema: T,
56
+ value: core.input<T>,
57
+ _ctx?: core.ParseContext<core.$ZodIssue>
58
+ ) => Promise<core.output<T>> = /* @__PURE__ */ core._decodeAsync(ZodRealError) as any;
59
+
60
+ export const safeEncode: <T extends core.$ZodType>(
61
+ schema: T,
62
+ value: core.output<T>,
63
+ _ctx?: core.ParseContext<core.$ZodIssue>
64
+ ) => ZodSafeParseResult<core.input<T>> = /* @__PURE__ */ core._safeEncode(ZodRealError) as any;
65
+
66
+ export const safeDecode: <T extends core.$ZodType>(
67
+ schema: T,
68
+ value: core.input<T>,
69
+ _ctx?: core.ParseContext<core.$ZodIssue>
70
+ ) => ZodSafeParseResult<core.output<T>> = /* @__PURE__ */ core._safeDecode(ZodRealError) as any;
71
+
72
+ export const safeEncodeAsync: <T extends core.$ZodType>(
73
+ schema: T,
74
+ value: core.output<T>,
75
+ _ctx?: core.ParseContext<core.$ZodIssue>
76
+ ) => Promise<ZodSafeParseResult<core.input<T>>> = /* @__PURE__ */ core._safeEncodeAsync(ZodRealError) as any;
77
+
78
+ export const safeDecodeAsync: <T extends core.$ZodType>(
79
+ schema: T,
80
+ value: core.input<T>,
81
+ _ctx?: core.ParseContext<core.$ZodIssue>
82
+ ) => Promise<ZodSafeParseResult<core.output<T>>> = /* @__PURE__ */ core._safeDecodeAsync(ZodRealError) as any;
@@ -450,6 +450,14 @@ export function url(params?: string | core.$ZodURLParams): ZodURL {
450
450
  return core._url(ZodURL, params);
451
451
  }
452
452
 
453
+ export function httpUrl(params?: string | Omit<core.$ZodURLParams, "protocol" | "hostname">): ZodURL {
454
+ return core._url(ZodURL, {
455
+ protocol: /^https?$/,
456
+ hostname: core.regexes.domain,
457
+ ...util.normalizeParams(params),
458
+ });
459
+ }
460
+
453
461
  // ZodEmoji
454
462
  export interface ZodEmoji extends ZodStringFormat<"emoji"> {
455
463
  _zod: core.$ZodEmojiInternals;
@@ -698,6 +706,23 @@ export function hostname(_params?: string | core.$ZodStringFormatParams): ZodCus
698
706
  return core._stringFormat(ZodCustomStringFormat, "hostname", core.regexes.hostname, _params) as any;
699
707
  }
700
708
 
709
+ export function hex(_params?: string | core.$ZodStringFormatParams): ZodCustomStringFormat<"hex"> {
710
+ return core._stringFormat(ZodCustomStringFormat, "hex", core.regexes.hex, _params) as any;
711
+ }
712
+
713
+ export function hash<Alg extends util.HashAlgorithm, Enc extends util.HashEncoding = "hex">(
714
+ alg: Alg,
715
+ params?: {
716
+ enc?: Enc;
717
+ } & core.$ZodStringFormatParams
718
+ ): ZodCustomStringFormat<`${Alg}_${Enc}`> {
719
+ const enc = params?.enc ?? "hex";
720
+ const format = `${alg}_${enc}` as const;
721
+ const regex = core.regexes[format as keyof typeof core.regexes] as RegExp;
722
+ if (!regex) throw new Error(`Unrecognized hash format: ${format}`);
723
+ return core._stringFormat(ZodCustomStringFormat, format, regex, params) as any;
724
+ }
725
+
701
726
  // ZodNumber
702
727
  export interface _ZodNumber<Internals extends core.$ZodNumberInternals = core.$ZodNumberInternals>
703
728
  extends _ZodType<Internals> {
@@ -1046,6 +1071,16 @@ export function keyof<T extends ZodObject>(schema: T): ZodEnum<util.KeysEnum<T["
1046
1071
 
1047
1072
  // ZodObject
1048
1073
 
1074
+ export type SafeExtendShape<Base extends core.$ZodShape, Ext extends core.$ZodLooseShape> = {
1075
+ [K in keyof Ext]: K extends keyof Base
1076
+ ? core.output<Ext[K]> extends core.output<Base[K]>
1077
+ ? core.input<Ext[K]> extends core.input<Base[K]>
1078
+ ? Ext[K]
1079
+ : never
1080
+ : never
1081
+ : Ext[K];
1082
+ };
1083
+
1049
1084
  export interface ZodObject<
1050
1085
  /** @ts-ignore Cast variance */
1051
1086
  out Shape extends core.$ZodShape = core.$ZodLooseShape,
@@ -1071,6 +1106,10 @@ export interface ZodObject<
1071
1106
 
1072
1107
  extend<U extends core.$ZodLooseShape>(shape: U): ZodObject<util.Extend<Shape, U>, Config>;
1073
1108
 
1109
+ safeExtend<U extends core.$ZodLooseShape>(
1110
+ shape: SafeExtendShape<Shape, U> & Partial<Record<keyof Shape, core.SomeType>>
1111
+ ): ZodObject<util.Extend<Shape, U>, Config>;
1112
+
1074
1113
  /**
1075
1114
  * @deprecated Use [`A.extend(B.shape)`](https://zod.dev/api?id=extend) instead.
1076
1115
  */
@@ -1122,7 +1161,7 @@ export interface ZodObject<
1122
1161
  }
1123
1162
 
1124
1163
  export const ZodObject: core.$constructor<ZodObject> = /*@__PURE__*/ core.$constructor("ZodObject", (inst, def) => {
1125
- core.$ZodObject.init(inst, def);
1164
+ core.$ZodObjectJIT.init(inst, def);
1126
1165
  ZodType.init(inst, def);
1127
1166
 
1128
1167
  util.defineLazy(inst, "shape", () => def.shape);
@@ -1136,6 +1175,9 @@ export const ZodObject: core.$constructor<ZodObject> = /*@__PURE__*/ core.$const
1136
1175
  inst.extend = (incoming: any) => {
1137
1176
  return util.extend(inst, incoming);
1138
1177
  };
1178
+ inst.safeExtend = (incoming: any) => {
1179
+ return util.safeExtend(inst, incoming);
1180
+ };
1139
1181
  inst.merge = (other) => util.merge(inst, other);
1140
1182
  inst.pick = (mask) => util.pick(inst, mask);
1141
1183
  inst.omit = (mask) => util.omit(inst, mask);
@@ -1393,7 +1435,6 @@ export interface ZodSet<T extends core.SomeType = core.$ZodType>
1393
1435
  extends _ZodType<core.$ZodSetInternals<T>>,
1394
1436
  core.$ZodSet<T> {
1395
1437
  min(minSize: number, params?: string | core.$ZodCheckMinSizeParams): this;
1396
- /** */
1397
1438
  nonempty(params?: string | core.$ZodCheckMinSizeParams): this;
1398
1439
  max(maxSize: number, params?: string | core.$ZodCheckMaxSizeParams): this;
1399
1440
  size(size: number, params?: string | core.$ZodCheckSizeEqualsParams): this;
@@ -1576,6 +1617,10 @@ export const ZodTransform: core.$constructor<ZodTransform> = /*@__PURE__*/ core.
1576
1617
  ZodType.init(inst, def);
1577
1618
 
1578
1619
  inst._zod.parse = (payload, _ctx) => {
1620
+ if (_ctx.direction === "backward") {
1621
+ throw new core.$ZodEncodeError(inst.constructor.name);
1622
+ }
1623
+
1579
1624
  (payload as core.$RefinementCtx).addIssue = (issue) => {
1580
1625
  if (typeof issue === "string") {
1581
1626
  payload.issues.push(util.issue(issue, payload.value, def));
@@ -1838,6 +1883,35 @@ export function pipe(in_: core.SomeType, out: core.SomeType) {
1838
1883
  });
1839
1884
  }
1840
1885
 
1886
+ // ZodCodec
1887
+ export interface ZodCodec<A extends core.SomeType = core.$ZodType, B extends core.SomeType = core.$ZodType>
1888
+ extends ZodPipe<A, B>,
1889
+ core.$ZodCodec<A, B> {
1890
+ _zod: core.$ZodCodecInternals<A, B>;
1891
+ def: core.$ZodCodecDef<A, B>;
1892
+ }
1893
+ export const ZodCodec: core.$constructor<ZodCodec> = /*@__PURE__*/ core.$constructor("ZodCodec", (inst, def) => {
1894
+ ZodPipe.init(inst, def);
1895
+ core.$ZodCodec.init(inst, def);
1896
+ });
1897
+
1898
+ export function codec<const A extends core.SomeType, B extends core.SomeType = core.$ZodType>(
1899
+ in_: A,
1900
+ out: B,
1901
+ params: {
1902
+ decode: (value: core.output<A>, payload: core.ParsePayload<core.output<A>>) => core.input<B>;
1903
+ encode: (value: core.input<B>, payload: core.ParsePayload<core.input<B>>) => core.output<A>;
1904
+ }
1905
+ ): ZodCodec<A, B> {
1906
+ return new ZodCodec({
1907
+ type: "pipe",
1908
+ in: in_ as any as core.$ZodType,
1909
+ out: out as any as core.$ZodType,
1910
+ transform: params.decode as any,
1911
+ reverseTransform: params.encode as any,
1912
+ }) as any;
1913
+ }
1914
+
1841
1915
  // ZodReadonly
1842
1916
  export interface ZodReadonly<T extends core.SomeType = core.$ZodType>
1843
1917
  extends _ZodType<core.$ZodReadonlyInternals<T>>,
@@ -1924,6 +1998,71 @@ export function promise<T extends core.SomeType>(innerType: T): ZodPromise<T> {
1924
1998
  }) as any;
1925
1999
  }
1926
2000
 
2001
+ // ZodFunction
2002
+ export interface ZodFunction<
2003
+ Args extends core.$ZodFunctionIn = core.$ZodFunctionIn,
2004
+ Returns extends core.$ZodFunctionOut = core.$ZodFunctionOut,
2005
+ > extends _ZodType<core.$ZodFunctionInternals<Args, Returns>>,
2006
+ core.$ZodFunction<Args, Returns> {
2007
+ _def: core.$ZodFunctionDef<Args, Returns>;
2008
+ _input: core.$InferInnerFunctionType<Args, Returns>;
2009
+ _output: core.$InferOuterFunctionType<Args, Returns>;
2010
+
2011
+ input<const Items extends util.TupleItems, const Rest extends core.$ZodFunctionOut = core.$ZodFunctionOut>(
2012
+ args: Items,
2013
+ rest?: Rest
2014
+ ): ZodFunction<core.$ZodTuple<Items, Rest>, Returns>;
2015
+ input<NewArgs extends core.$ZodFunctionIn>(args: NewArgs): ZodFunction<NewArgs, Returns>;
2016
+ input(...args: any[]): ZodFunction<any, Returns>;
2017
+
2018
+ output<NewReturns extends core.$ZodType>(output: NewReturns): ZodFunction<Args, NewReturns>;
2019
+ }
2020
+
2021
+ export const ZodFunction: core.$constructor<ZodFunction> = /*@__PURE__*/ core.$constructor(
2022
+ "ZodFunction",
2023
+ (inst, def) => {
2024
+ core.$ZodFunction.init(inst, def);
2025
+ ZodType.init(inst, def);
2026
+ }
2027
+ );
2028
+
2029
+ export function _function(): ZodFunction;
2030
+ export function _function<const In extends Array<core.$ZodType> = Array<core.$ZodType>>(params: {
2031
+ input: In;
2032
+ }): ZodFunction<ZodTuple<In, null>, core.$ZodFunctionOut>;
2033
+ export function _function<
2034
+ const In extends Array<core.$ZodType> = Array<core.$ZodType>,
2035
+ const Out extends core.$ZodFunctionOut = core.$ZodFunctionOut,
2036
+ >(params: {
2037
+ input: In;
2038
+ output: Out;
2039
+ }): ZodFunction<ZodTuple<In, null>, Out>;
2040
+ export function _function<const In extends core.$ZodFunctionIn = core.$ZodFunctionIn>(params: {
2041
+ input: In;
2042
+ }): ZodFunction<In, core.$ZodFunctionOut>;
2043
+ export function _function<const Out extends core.$ZodFunctionOut = core.$ZodFunctionOut>(params: {
2044
+ output: Out;
2045
+ }): ZodFunction<core.$ZodFunctionIn, Out>;
2046
+ export function _function<
2047
+ In extends core.$ZodFunctionIn = core.$ZodFunctionIn,
2048
+ Out extends core.$ZodType = core.$ZodType,
2049
+ >(params?: {
2050
+ input: In;
2051
+ output: Out;
2052
+ }): ZodFunction<In, Out>;
2053
+ export function _function(params?: {
2054
+ output?: core.$ZodType;
2055
+ input?: core.$ZodFunctionArgs | Array<core.$ZodType>;
2056
+ }): ZodFunction {
2057
+ return new ZodFunction({
2058
+ type: "function",
2059
+ input: Array.isArray(params?.input) ? tuple(params?.input as any) : (params?.input ?? array(unknown())),
2060
+ output: params?.output ?? unknown(),
2061
+ });
2062
+ }
2063
+
2064
+ export { _function as function };
2065
+
1927
2066
  // ZodCustom
1928
2067
  export interface ZodCustom<O = unknown, I = unknown>
1929
2068
  extends _ZodType<core.$ZodCustomInternals<O, I>>,
@@ -1989,15 +2128,14 @@ function _instanceof<T extends typeof util.Class>(
1989
2128
  export { _instanceof as instanceof };
1990
2129
 
1991
2130
  // stringbool
1992
- export const stringbool: (
1993
- _params?: string | core.$ZodStringBoolParams
1994
- ) => ZodPipe<ZodPipe<ZodString, ZodTransform<boolean, string>>, ZodBoolean> = (...args) =>
2131
+ export const stringbool: (_params?: string | core.$ZodStringBoolParams) => ZodCodec<ZodString, ZodBoolean> = (
2132
+ ...args
2133
+ ) =>
1995
2134
  core._stringbool(
1996
2135
  {
1997
- Pipe: ZodPipe,
2136
+ Codec: ZodCodec,
1998
2137
  Boolean: ZodBoolean,
1999
2138
  String: ZodString,
2000
- Transform: ZodTransform,
2001
2139
  },
2002
2140
  ...args
2003
2141
  ) as any;
@@ -6,9 +6,9 @@ test("type inference", () => {
6
6
  expectTypeOf<z.infer<typeof schema>>().toEqualTypeOf<string[]>();
7
7
  });
8
8
 
9
- test("array min/max", async () => {
9
+ test("array min/max", () => {
10
10
  const schema = z.array(z.string()).min(2).max(2);
11
- const r1 = await schema.safeParse(["asdf"]);
11
+ const r1 = schema.safeParse(["asdf"]);
12
12
  expect(r1.success).toEqual(false);
13
13
  expect(r1.error!.issues).toMatchInlineSnapshot(`
14
14
  [
@@ -23,7 +23,7 @@ test("array min/max", async () => {
23
23
  ]
24
24
  `);
25
25
 
26
- const r2 = await schema.safeParse(["asdf", "asdf", "asdf"]);
26
+ const r2 = schema.safeParse(["asdf", "asdf", "asdf"]);
27
27
  expect(r2.success).toEqual(false);
28
28
  expect(r2.error!.issues).toMatchInlineSnapshot(`
29
29
  [
@@ -39,11 +39,11 @@ test("array min/max", async () => {
39
39
  `);
40
40
  });
41
41
 
42
- test("array length", async () => {
42
+ test("array length", () => {
43
43
  const schema = z.array(z.string()).length(2);
44
44
  schema.parse(["asdf", "asdf"]);
45
45
 
46
- const r1 = await schema.safeParse(["asdf"]);
46
+ const r1 = schema.safeParse(["asdf"]);
47
47
  expect(r1.success).toEqual(false);
48
48
  expect(r1.error!.issues).toMatchInlineSnapshot(`
49
49
  [
@@ -59,7 +59,7 @@ test("array length", async () => {
59
59
  ]
60
60
  `);
61
61
 
62
- const r2 = await schema.safeParse(["asdf", "asdf", "asdf"]);
62
+ const r2 = schema.safeParse(["asdf", "asdf", "asdf"]);
63
63
  expect(r2.success).toEqual(false);
64
64
  expect(r2.error!.issues).toMatchInlineSnapshot(`
65
65
  [
@@ -249,3 +249,28 @@ test("ctx.input", () => {
249
249
 
250
250
  expect(schema.parse(123)).toEqual("123");
251
251
  });
252
+
253
+ test("direction-aware catch", () => {
254
+ const schema = z.string().catch("fallback");
255
+
256
+ // Forward direction (regular parse): catch should be applied
257
+ expect(schema.parse(123)).toBe("fallback");
258
+
259
+ // Reverse direction (encode): catch should NOT be applied, invalid value should fail validation
260
+ expect(z.safeEncode(schema, 123 as any)).toMatchInlineSnapshot(`
261
+ {
262
+ "error": [ZodError: [
263
+ {
264
+ "expected": "string",
265
+ "code": "invalid_type",
266
+ "path": [],
267
+ "message": "Invalid input: expected string, received number"
268
+ }
269
+ ]],
270
+ "success": false,
271
+ }
272
+ `);
273
+
274
+ // But valid values should still work in reverse
275
+ expect(z.encode(schema, "world")).toBe("world");
276
+ });