zod 4.3.0-canary.20251216T172808 → 4.3.0-canary.20251222T195342

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod",
3
- "version": "4.3.0-canary.20251216T172808",
3
+ "version": "4.3.0-canary.20251222T195342",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Colin McDonnell <zod@colinhacks.com>",
@@ -48,7 +48,9 @@ export interface ZodType<
48
48
  : ["Incompatible schema"]
49
49
  ): this;
50
50
 
51
- brand<T extends PropertyKey = PropertyKey>(value?: T): PropertyKey extends T ? this : core.$ZodBranded<this, T>;
51
+ brand<T extends PropertyKey = PropertyKey, Dir extends "in" | "out" | "inout" = "out">(
52
+ value?: T
53
+ ): PropertyKey extends T ? this : core.$ZodBranded<this, T, Dir>;
52
54
 
53
55
  // parsing
54
56
  parse(data: unknown, params?: core.ParseContext<core.$ZodIssue>): core.output<this>;
@@ -253,7 +255,7 @@ export interface _ZodString<T extends core.$ZodStringInternals<unknown> = core.$
253
255
 
254
256
  // miscellaneous checks
255
257
  regex(regex: RegExp, params?: string | core.$ZodCheckRegexParams): this;
256
- includes(value: string, params?: core.$ZodCheckIncludesParams): this;
258
+ includes(value: string, params?: string | core.$ZodCheckIncludesParams): this;
257
259
  startsWith(value: string, params?: string | core.$ZodCheckStartsWithParams): this;
258
260
  endsWith(value: string, params?: string | core.$ZodCheckEndsWithParams): this;
259
261
  min(minLength: number, params?: string | core.$ZodCheckMinLengthParams): this;
@@ -61,3 +61,46 @@ test("branded record", () => {
61
61
  type recordWithBrandedNumberKeys = z.infer<typeof recordWithBrandedNumberKeys>;
62
62
  expectTypeOf<recordWithBrandedNumberKeys>().toEqualTypeOf<Record<string & z.core.$brand<"SomeBrand">, number>>();
63
63
  });
64
+
65
+ test("brand direction: out (default)", () => {
66
+ const schema = z.string().brand<"A">();
67
+ type Input = z.input<typeof schema>;
68
+ type Output = z.output<typeof schema>;
69
+
70
+ // output is branded
71
+ expectTypeOf<Output>().toEqualTypeOf<string & z.$brand<"A">>();
72
+ // input is NOT branded (default behavior)
73
+ expectTypeOf<Input>().toEqualTypeOf<string>();
74
+ });
75
+
76
+ test("brand direction: out (explicit)", () => {
77
+ const schema = z.string().brand<"A", "out">();
78
+ type Input = z.input<typeof schema>;
79
+ type Output = z.output<typeof schema>;
80
+
81
+ // output is branded
82
+ expectTypeOf<Output>().toEqualTypeOf<string & z.$brand<"A">>();
83
+ // input is NOT branded
84
+ expectTypeOf<Input>().toEqualTypeOf<string>();
85
+ });
86
+
87
+ test("brand direction: in", () => {
88
+ const schema = z.string().brand<"A", "in">();
89
+ type Input = z.input<typeof schema>;
90
+ type Output = z.output<typeof schema>;
91
+
92
+ // input is branded
93
+ expectTypeOf<Input>().toEqualTypeOf<string & z.$brand<"A">>();
94
+ // output is NOT branded
95
+ expectTypeOf<Output>().toEqualTypeOf<string>();
96
+ });
97
+
98
+ test("brand direction: inout", () => {
99
+ const schema = z.string().brand<"A", "inout">();
100
+ type Input = z.input<typeof schema>;
101
+ type Output = z.output<typeof schema>;
102
+
103
+ // both are branded
104
+ expectTypeOf<Input>().toEqualTypeOf<string & z.$brand<"A">>();
105
+ expectTypeOf<Output>().toEqualTypeOf<string & z.$brand<"A">>();
106
+ });
@@ -35,6 +35,27 @@ test("includes", () => {
35
35
  expect(() => includesFromIndex2.parse("XincludesXX")).toThrow();
36
36
  });
37
37
 
38
+ test("includes with string error message", () => {
39
+ const schema = z.string().includes("test", "must contain test");
40
+ schema.parse("this is a test");
41
+
42
+ expect(schema.safeParse("this is invalid")).toMatchInlineSnapshot(`
43
+ {
44
+ "error": [ZodError: [
45
+ {
46
+ "origin": "string",
47
+ "code": "invalid_format",
48
+ "format": "includes",
49
+ "includes": "test",
50
+ "path": [],
51
+ "message": "must contain test"
52
+ }
53
+ ]],
54
+ "success": false,
55
+ }
56
+ `);
57
+ });
58
+
38
59
  test("startswith/endswith", () => {
39
60
  startsWith.parse("startsWithX");
40
61
  endsWith.parse("XendsWith");
@@ -82,8 +82,16 @@ export type $brand<T extends string | number | symbol = string | number | symbol
82
82
  [$brand]: { [k in T]: true };
83
83
  };
84
84
 
85
- export type $ZodBranded<T extends schemas.SomeType, Brand extends string | number | symbol> = T &
86
- Record<"_zod", Record<"output", output<T> & $brand<Brand>>>;
85
+ export type $ZodBranded<
86
+ T extends schemas.SomeType,
87
+ Brand extends string | number | symbol,
88
+ Dir extends "in" | "out" | "inout" = "out",
89
+ > = T &
90
+ (Dir extends "inout"
91
+ ? { _zod: { input: input<T> & $brand<Brand>; output: output<T> & $brand<Brand> } }
92
+ : Dir extends "in"
93
+ ? { _zod: { input: input<T> & $brand<Brand> } }
94
+ : { _zod: { output: output<T> & $brand<Brand> } });
87
95
 
88
96
  export class $ZodAsyncError extends Error {
89
97
  constructor() {
@@ -20,9 +20,9 @@ export interface ZodMiniType<
20
20
  : [core.$replace<R["_meta"], this>]
21
21
  : ["Incompatible schema"]
22
22
  ): this;
23
- brand<T extends PropertyKey = PropertyKey>(
23
+ brand<T extends PropertyKey = PropertyKey, Dir extends "in" | "out" | "inout" = "out">(
24
24
  value?: T
25
- ): PropertyKey extends T ? this : this & Record<"_zod", Record<"output", core.output<this> & core.$brand<T>>>;
25
+ ): PropertyKey extends T ? this : core.$ZodBranded<this, T, Dir>;
26
26
 
27
27
  def: Internals["def"];
28
28
 
@@ -49,3 +49,46 @@ test("branded types", () => {
49
49
  // @ts-expect-error
50
50
  doStuff({ name: "hello there!" });
51
51
  });
52
+
53
+ test("brand direction: out (default)", () => {
54
+ const schema = z.string().brand<"A">();
55
+ type Input = z.input<typeof schema>;
56
+ type Output = z.output<typeof schema>;
57
+
58
+ // output is branded
59
+ expectTypeOf<Output>().toEqualTypeOf<string & z.$brand<"A">>();
60
+ // input is NOT branded (default behavior)
61
+ expectTypeOf<Input>().toEqualTypeOf<string>();
62
+ });
63
+
64
+ test("brand direction: out (explicit)", () => {
65
+ const schema = z.string().brand<"A", "out">();
66
+ type Input = z.input<typeof schema>;
67
+ type Output = z.output<typeof schema>;
68
+
69
+ // output is branded
70
+ expectTypeOf<Output>().toEqualTypeOf<string & z.$brand<"A">>();
71
+ // input is NOT branded
72
+ expectTypeOf<Input>().toEqualTypeOf<string>();
73
+ });
74
+
75
+ test("brand direction: in", () => {
76
+ const schema = z.string().brand<"A", "in">();
77
+ type Input = z.input<typeof schema>;
78
+ type Output = z.output<typeof schema>;
79
+
80
+ // input is branded
81
+ expectTypeOf<Input>().toEqualTypeOf<string & z.$brand<"A">>();
82
+ // output is NOT branded
83
+ expectTypeOf<Output>().toEqualTypeOf<string>();
84
+ });
85
+
86
+ test("brand direction: inout", () => {
87
+ const schema = z.string().brand<"A", "inout">();
88
+ type Input = z.input<typeof schema>;
89
+ type Output = z.output<typeof schema>;
90
+
91
+ // both are branded
92
+ expectTypeOf<Input>().toEqualTypeOf<string & z.$brand<"A">>();
93
+ expectTypeOf<Output>().toEqualTypeOf<string & z.$brand<"A">>();
94
+ });
@@ -20,7 +20,7 @@ export interface ZodType<out Output = unknown, out Input = unknown, out Internal
20
20
  parent: boolean;
21
21
  }): this;
22
22
  register<R extends core.$ZodRegistry>(registry: R, ...meta: this extends R["_schema"] ? undefined extends R["_meta"] ? [core.$replace<R["_meta"], this>?] : [core.$replace<R["_meta"], this>] : ["Incompatible schema"]): this;
23
- brand<T extends PropertyKey = PropertyKey>(value?: T): PropertyKey extends T ? this : core.$ZodBranded<this, T>;
23
+ brand<T extends PropertyKey = PropertyKey, Dir extends "in" | "out" | "inout" = "out">(value?: T): PropertyKey extends T ? this : core.$ZodBranded<this, T, Dir>;
24
24
  parse(data: unknown, params?: core.ParseContext<core.$ZodIssue>): core.output<this>;
25
25
  safeParse(data: unknown, params?: core.ParseContext<core.$ZodIssue>): parse.ZodSafeParseResult<core.output<this>>;
26
26
  parseAsync(data: unknown, params?: core.ParseContext<core.$ZodIssue>): Promise<core.output<this>>;
@@ -86,7 +86,7 @@ export interface _ZodString<T extends core.$ZodStringInternals<unknown> = core.$
86
86
  minLength: number | null;
87
87
  maxLength: number | null;
88
88
  regex(regex: RegExp, params?: string | core.$ZodCheckRegexParams): this;
89
- includes(value: string, params?: core.$ZodCheckIncludesParams): this;
89
+ includes(value: string, params?: string | core.$ZodCheckIncludesParams): this;
90
90
  startsWith(value: string, params?: string | core.$ZodCheckStartsWithParams): this;
91
91
  endsWith(value: string, params?: string | core.$ZodCheckEndsWithParams): this;
92
92
  min(minLength: number, params?: string | core.$ZodCheckMinLengthParams): this;
@@ -20,7 +20,7 @@ export interface ZodType<out Output = unknown, out Input = unknown, out Internal
20
20
  parent: boolean;
21
21
  }): this;
22
22
  register<R extends core.$ZodRegistry>(registry: R, ...meta: this extends R["_schema"] ? undefined extends R["_meta"] ? [core.$replace<R["_meta"], this>?] : [core.$replace<R["_meta"], this>] : ["Incompatible schema"]): this;
23
- brand<T extends PropertyKey = PropertyKey>(value?: T): PropertyKey extends T ? this : core.$ZodBranded<this, T>;
23
+ brand<T extends PropertyKey = PropertyKey, Dir extends "in" | "out" | "inout" = "out">(value?: T): PropertyKey extends T ? this : core.$ZodBranded<this, T, Dir>;
24
24
  parse(data: unknown, params?: core.ParseContext<core.$ZodIssue>): core.output<this>;
25
25
  safeParse(data: unknown, params?: core.ParseContext<core.$ZodIssue>): parse.ZodSafeParseResult<core.output<this>>;
26
26
  parseAsync(data: unknown, params?: core.ParseContext<core.$ZodIssue>): Promise<core.output<this>>;
@@ -86,7 +86,7 @@ export interface _ZodString<T extends core.$ZodStringInternals<unknown> = core.$
86
86
  minLength: number | null;
87
87
  maxLength: number | null;
88
88
  regex(regex: RegExp, params?: string | core.$ZodCheckRegexParams): this;
89
- includes(value: string, params?: core.$ZodCheckIncludesParams): this;
89
+ includes(value: string, params?: string | core.$ZodCheckIncludesParams): this;
90
90
  startsWith(value: string, params?: string | core.$ZodCheckStartsWithParams): this;
91
91
  endsWith(value: string, params?: string | core.$ZodCheckEndsWithParams): this;
92
92
  min(minLength: number, params?: string | core.$ZodCheckMinLengthParams): this;
@@ -22,7 +22,20 @@ export type $brand<T extends string | number | symbol = string | number | symbol
22
22
  [k in T]: true;
23
23
  };
24
24
  };
25
- export type $ZodBranded<T extends schemas.SomeType, Brand extends string | number | symbol> = T & Record<"_zod", Record<"output", output<T> & $brand<Brand>>>;
25
+ export type $ZodBranded<T extends schemas.SomeType, Brand extends string | number | symbol, Dir extends "in" | "out" | "inout" = "out"> = T & (Dir extends "inout" ? {
26
+ _zod: {
27
+ input: input<T> & $brand<Brand>;
28
+ output: output<T> & $brand<Brand>;
29
+ };
30
+ } : Dir extends "in" ? {
31
+ _zod: {
32
+ input: input<T> & $brand<Brand>;
33
+ };
34
+ } : {
35
+ _zod: {
36
+ output: output<T> & $brand<Brand>;
37
+ };
38
+ });
26
39
  export declare class $ZodAsyncError extends Error {
27
40
  constructor();
28
41
  }
package/v4/core/core.d.ts CHANGED
@@ -22,7 +22,20 @@ export type $brand<T extends string | number | symbol = string | number | symbol
22
22
  [k in T]: true;
23
23
  };
24
24
  };
25
- export type $ZodBranded<T extends schemas.SomeType, Brand extends string | number | symbol> = T & Record<"_zod", Record<"output", output<T> & $brand<Brand>>>;
25
+ export type $ZodBranded<T extends schemas.SomeType, Brand extends string | number | symbol, Dir extends "in" | "out" | "inout" = "out"> = T & (Dir extends "inout" ? {
26
+ _zod: {
27
+ input: input<T> & $brand<Brand>;
28
+ output: output<T> & $brand<Brand>;
29
+ };
30
+ } : Dir extends "in" ? {
31
+ _zod: {
32
+ input: input<T> & $brand<Brand>;
33
+ };
34
+ } : {
35
+ _zod: {
36
+ output: output<T> & $brand<Brand>;
37
+ };
38
+ });
26
39
  export declare class $ZodAsyncError extends Error {
27
40
  constructor();
28
41
  }
@@ -8,7 +8,7 @@ export interface ZodMiniType<out Output = unknown, out Input = unknown, out Inte
8
8
  parent: boolean;
9
9
  }): this;
10
10
  register<R extends core.$ZodRegistry>(registry: R, ...meta: this extends R["_schema"] ? undefined extends R["_meta"] ? [core.$replace<R["_meta"], this>?] : [core.$replace<R["_meta"], this>] : ["Incompatible schema"]): this;
11
- brand<T extends PropertyKey = PropertyKey>(value?: T): PropertyKey extends T ? this : this & Record<"_zod", Record<"output", core.output<this> & core.$brand<T>>>;
11
+ brand<T extends PropertyKey = PropertyKey, Dir extends "in" | "out" | "inout" = "out">(value?: T): PropertyKey extends T ? this : core.$ZodBranded<this, T, Dir>;
12
12
  def: Internals["def"];
13
13
  parse(data: unknown, params?: core.ParseContext<core.$ZodIssue>): core.output<this>;
14
14
  safeParse(data: unknown, params?: core.ParseContext<core.$ZodIssue>): util.SafeParseResult<core.output<this>>;
@@ -8,7 +8,7 @@ export interface ZodMiniType<out Output = unknown, out Input = unknown, out Inte
8
8
  parent: boolean;
9
9
  }): this;
10
10
  register<R extends core.$ZodRegistry>(registry: R, ...meta: this extends R["_schema"] ? undefined extends R["_meta"] ? [core.$replace<R["_meta"], this>?] : [core.$replace<R["_meta"], this>] : ["Incompatible schema"]): this;
11
- brand<T extends PropertyKey = PropertyKey>(value?: T): PropertyKey extends T ? this : this & Record<"_zod", Record<"output", core.output<this> & core.$brand<T>>>;
11
+ brand<T extends PropertyKey = PropertyKey, Dir extends "in" | "out" | "inout" = "out">(value?: T): PropertyKey extends T ? this : core.$ZodBranded<this, T, Dir>;
12
12
  def: Internals["def"];
13
13
  parse(data: unknown, params?: core.ParseContext<core.$ZodIssue>): core.output<this>;
14
14
  safeParse(data: unknown, params?: core.ParseContext<core.$ZodIssue>): util.SafeParseResult<core.output<this>>;