zod 3.17.2 → 3.17.5

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/README.md CHANGED
@@ -22,7 +22,7 @@
22
22
  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
23
23
  <a href="https://discord.gg/RcG33DQJdf">Discord</a>
24
24
  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
25
- <a href="https://www.npmjs.com/package/zod">NPM</a>
25
+ <a href="https://www.npmjs.com/package/zod">npm</a>
26
26
  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
27
27
  <a href="https://github.com/colinhacks/zod/issues/new">Issues</a>
28
28
  <span>&nbsp;&nbsp;•&nbsp;&nbsp;</span>
@@ -47,7 +47,7 @@
47
47
  - [Sponsors](#sponsors)
48
48
  - [Ecosystem](#ecosystem)
49
49
  - [Installation](#installation)
50
- - [Node](#node)
50
+ - [Node/npm](#Node/npm)
51
51
  - [Deno](#deno)
52
52
  - [Basic usage](#basic-usage)
53
53
  - [Primitives](#primitives)
@@ -215,6 +215,26 @@ Sponsorship at any level is appreciated and encouraged. For individual developer
215
215
  <b>Trip</b>
216
216
  </td>
217
217
  </tr>
218
+ <tr>
219
+ <td align="center">
220
+ <a href="https://seasoned.cc">
221
+ <img src="https://avatars.githubusercontent.com/u/33913103?s=200&v=4" width="150px;" alt="" />
222
+ </a>
223
+ <br />
224
+ <b>Seasoned Software</b>
225
+ <br />
226
+ <a href="https://seasoned.cc">seasoned.cc</a>
227
+ </td>
228
+ <td align="center">
229
+ <a href="https://seasoned.cc">
230
+ <img src="https://avatars.githubusercontent.com/u/67802063?s=200&v=4" width="150px;" alt="" />
231
+ </a>
232
+ <br />
233
+ <b>Interval</b>
234
+ <br />
235
+ <a href="https://interval.com">interval.com</a>
236
+ </td>
237
+ </tr>
218
238
  </table>
219
239
 
220
240
  #### Bronze
@@ -252,6 +272,18 @@ Sponsorship at any level is appreciated and encouraged. For individual developer
252
272
  <a href="https://twitter.com/alexdotjs">@alexdotjs</a>
253
273
  </td>
254
274
  </tr>
275
+ <tr>
276
+ <td align="center">
277
+ <a href="https://adaptable.io/">
278
+ <img src="https://avatars.githubusercontent.com/u/60378268?s=200&v=4" width="100px;" alt=""/>
279
+ </a>
280
+ <br />
281
+ <b>Adaptable</b>
282
+ <br/>
283
+ <a href="https://adaptable.io/">adaptable.io</a>
284
+ <br />
285
+ </td>
286
+ </tr>
255
287
  </table>
256
288
 
257
289
  ### Ecosystem
@@ -281,6 +313,7 @@ There are a growing number of tools that are built atop or support Zod natively!
281
313
  - [`prisma-zod-generator`](https://github.com/omar-dulaimi/prisma-zod-generator): Emit Zod schemas from your Prisma schema.
282
314
  - [`prisma-trpc-generator`](https://github.com/omar-dulaimi/prisma-trpc-generator): Emit fully implemented tRPC routers and their validation schemas using Zod.
283
315
  - [`nestjs-graphql-zod`](https://github.com/incetarik/nestjs-graphql-zod): Generates NestJS GraphQL model classes from Zod schemas dynamically and provides GraphQL method decorators working with Zod schemas.
316
+ - [`remix-domains`](https://github.com/SeasonedSoftware/remix-domains/): Improves end-to-end type safety in [Remix](https://remix.run/) by leveraging Zod to parse the framework's inputs such as FormData, URLSearchParams, etc.
284
317
 
285
318
  #### Form integrations
286
319
 
@@ -306,7 +339,7 @@ There are a growing number of tools that are built atop or support Zod natively!
306
339
  }
307
340
  ```
308
341
 
309
- ### Node/NPM
342
+ ### Node/npm
310
343
 
311
344
  To install Zod v3:
312
345
 
@@ -318,7 +351,7 @@ pnpm add zod # pnpm
318
351
 
319
352
  ### Deno
320
353
 
321
- Unlike Node, Deno relies on direct URL imports instead of a package manager like NPM. Zod is available on [deno.land/x](deno.land/x). The latest version can be imported like so:
354
+ Unlike Node, Deno relies on direct URL imports instead of a package manager like npm. Zod is available on [deno.land/x](deno.land/x). The latest version can be imported like so:
322
355
 
323
356
  ```ts
324
357
  import { z } from "https://deno.land/x/zod/mod.ts";
@@ -330,7 +363,7 @@ You can also specify a particular version:
330
363
  import { z } from from "https://deno.land/x/zod@v3.16.1/mod.ts"
331
364
  ```
332
365
 
333
- > The rest of this README assumes you are using NPM and importing directly from the `"zod"` package.
366
+ > The rest of this README assumes you are using npm and importing directly from the `"zod"` package.
334
367
 
335
368
  ## Basic usage
336
369
 
@@ -420,6 +453,8 @@ z.string().url();
420
453
  z.string().uuid();
421
454
  z.string().cuid();
422
455
  z.string().regex(regex);
456
+ z.string().startsWith(string);
457
+ z.string().endsWith(string);
423
458
 
424
459
  // trim whitespace
425
460
  z.string().trim();
@@ -433,7 +468,7 @@ z.string().nonempty({ message: "Can't be empty" });
433
468
 
434
469
  > Check out [validator.js](https://github.com/validatorjs/validator.js) for a bunch of other useful string validation functions.
435
470
 
436
- You can customize some common errors messages when creating a string schema.
471
+ You can customize some common error messages when creating a string schema.
437
472
 
438
473
  ```ts
439
474
  const name = z.string({
@@ -451,6 +486,8 @@ z.string().length(5, { message: "Must be exactly 5 characters long" });
451
486
  z.string().email({ message: "Invalid email address" });
452
487
  z.string().url({ message: "Invalid url" });
453
488
  z.string().uuid({ message: "Invalid UUID" });
489
+ z.string().startsWith("https://", { message: "Must provide secure URL" });
490
+ z.string().endsWith(".com", { message: "Only .com domains allowed" });
454
491
  ```
455
492
 
456
493
  ## Numbers
@@ -547,7 +584,7 @@ const VALUES = ["Salmon", "Tuna", "Trout"] as const;
547
584
  const FishEnum = z.enum(VALUES);
548
585
  ```
549
586
 
550
- This is not allowed, since Zod isn't able to infer the exact values of each elements.
587
+ This is not allowed, since Zod isn't able to infer the exact values of each element.
551
588
 
552
589
  ```ts
553
590
  const fish = ["Salmon", "Tuna", "Trout"];
@@ -728,7 +765,7 @@ Dog.shape.age; // => number schema
728
765
 
729
766
  ### `.extend`
730
767
 
731
- You can add additional fields an object schema with the `.extend` method.
768
+ You can add additional fields to an object schema with the `.extend` method.
732
769
 
733
770
  ```ts
734
771
  const DogWithBreed = Dog.extend({
@@ -848,7 +885,7 @@ const deepPartialUser = user.deepPartial();
848
885
 
849
886
  ### `.passthrough`
850
887
 
851
- By default Zod objects schemas strip out unrecognized keys during parsing.
888
+ By default Zod object schemas strip out unrecognized keys during parsing.
852
889
 
853
890
  ```ts
854
891
  const person = z.object({
@@ -875,7 +912,7 @@ person.passthrough().parse({
875
912
 
876
913
  ### `.strict`
877
914
 
878
- By default Zod objects schemas strip out unrecognized keys during parsing. You can _disallow_ unknown keys with `.strict()` . If there are any unknown keys in the input, Zod will throw an error.
915
+ By default Zod object schemas strip out unrecognized keys during parsing. You can _disallow_ unknown keys with `.strict()` . If there are any unknown keys in the input, Zod will throw an error.
879
916
 
880
917
  ```ts
881
918
  const person = z
@@ -1035,7 +1072,7 @@ const item = z
1035
1072
 
1036
1073
  Record schemas are used to validate types such as `{ [k: string]: number }`.
1037
1074
 
1038
- If you want to validate the _values_ of an object against some schema but don't care about the keys, use `Record`.
1075
+ If you want to validate the _values_ of an object against some schema but don't care about the keys, use `z.record(valueType)`:
1039
1076
 
1040
1077
  ```ts
1041
1078
  const NumberCache = z.record(z.number());
@@ -1058,9 +1095,22 @@ userStore["77d2586b-9e8e-4ecf-8b21-ea7e0530eadd"] = {
1058
1095
  }; // TypeError
1059
1096
  ```
1060
1097
 
1098
+ ### Record key type
1099
+
1100
+ If you want to validate both the keys and the values, use
1101
+ `z.record(keyType, valueType)`:
1102
+
1103
+ ```ts
1104
+ const NoEmptyKeysSchema = z.record(z.string().min(1), z.number());
1105
+ NoEmptyKeysSchema.parse({ count: 1 }); // => { 'count': 1 }
1106
+ NoEmptyKeysSchema.parse({ "": 1 }); // fails
1107
+ ```
1108
+
1109
+ _(Notice how when passing two arguments, `valueType` is the second argument)_
1110
+
1061
1111
  **A note on numerical keys**
1062
1112
 
1063
- You may have expected `z.record()` to accept two arguments, one for the keys and one for the values. After all, TypeScript's built-in Record type does: `Record<KeyType, ValueType>` . Otherwise, how do you represent the TypeScript type `Record<number, any>` in Zod?
1113
+ While `z.record(keyType, valueType)` is able to accept numerical key types and TypeScript's built-in Record type is `Record<KeyType, ValueType>`, it's hard to represent the TypeScript type `Record<number, any>` in Zod.
1064
1114
 
1065
1115
  As it turns out, TypeScript's behavior surrounding `[k: number]` is a little unintuitive:
1066
1116
 
@@ -1310,7 +1360,7 @@ type myFunction = z.infer<typeof myFunction>;
1310
1360
  // => (arg0: string)=>number
1311
1361
  ``` -->
1312
1362
 
1313
- Function schemas have an `.implement()` method which accepts a function and returns a new function that automatically validates it's inputs and outputs.
1363
+ Function schemas have an `.implement()` method which accepts a function and returns a new function that automatically validates its inputs and outputs.
1314
1364
 
1315
1365
  ```ts
1316
1366
  const trimmedLength = z
@@ -1528,7 +1578,7 @@ const userId = z.string().refine(async (id) => {
1528
1578
  });
1529
1579
  ```
1530
1580
 
1531
- > ⚠️If you use async refinements, you must use the `.parseAsync` method to parse data! Otherwise Zod will throw an error.
1581
+ > ⚠️ If you use async refinements, you must use the `.parseAsync` method to parse data! Otherwise Zod will throw an error.
1532
1582
 
1533
1583
  #### Relationship to transforms
1534
1584
 
@@ -1584,7 +1634,7 @@ const Strings = z.array(z.string()).superRefine((val, ctx) => {
1584
1634
  if (val.length !== new Set(val).size) {
1585
1635
  ctx.addIssue({
1586
1636
  code: z.ZodIssueCode.custom,
1587
- message: `No duplicated allowed.`,
1637
+ message: `No duplicates allowed.`,
1588
1638
  });
1589
1639
  }
1590
1640
  });
@@ -1977,13 +2027,13 @@ Branded -->
1977
2027
  * Missing support for parsing cyclical data (maybe)
1978
2028
  * Missing error customization -->
1979
2029
 
1980
- **Joi**
2030
+ ### Joi
1981
2031
 
1982
2032
  [https://github.com/hapijs/joi](https://github.com/hapijs/joi)
1983
2033
 
1984
2034
  Doesn't support static type inference 😕
1985
2035
 
1986
- **Yup**
2036
+ ### Yup
1987
2037
 
1988
2038
  [https://github.com/jquense/yup](https://github.com/jquense/yup)
1989
2039
 
@@ -1999,7 +2049,7 @@ Yup is a full-featured library that was implemented first in vanilla JS, and lat
1999
2049
 
2000
2050
  <!-- ¹Yup has a strange interpretation of the word `required`. Instead of meaning "not undefined", Yup uses it to mean "not empty". So `yup.string().required()` will not accept an empty string, and `yup.array(yup.string()).required()` will not accept an empty array. Instead, Yup us Zod arrays there is a dedicated `.nonempty()` method to indicate this, or you can implement it with a custom refinement. -->
2001
2051
 
2002
- **io-ts**
2052
+ ### io-ts
2003
2053
 
2004
2054
  [https://github.com/gcanti/io-ts](https://github.com/gcanti/io-ts)
2005
2055
 
@@ -2050,7 +2100,7 @@ This more declarative API makes schema definitions vastly more concise.
2050
2100
  - Missing promise schemas
2051
2101
  - Missing function schemas
2052
2102
 
2053
- **Runtypes**
2103
+ ### Runtypes
2054
2104
 
2055
2105
  [https://github.com/pelotom/runtypes](https://github.com/pelotom/runtypes)
2056
2106
 
@@ -2063,7 +2113,7 @@ Good type inference support, but limited options for object type masking (no `.p
2063
2113
  - Missing promise schemas
2064
2114
  - Missing error customization
2065
2115
 
2066
- **Ow**
2116
+ ### Ow
2067
2117
 
2068
2118
  [https://github.com/sindresorhus/ow](https://github.com/sindresorhus/ow)
2069
2119
 
package/lib/ZodError.d.ts CHANGED
@@ -68,7 +68,11 @@ export interface ZodInvalidReturnTypeIssue extends ZodIssueBase {
68
68
  export interface ZodInvalidDateIssue extends ZodIssueBase {
69
69
  code: typeof ZodIssueCode.invalid_date;
70
70
  }
71
- export declare type StringValidation = "email" | "url" | "uuid" | "regex" | "cuid";
71
+ export declare type StringValidation = "email" | "url" | "uuid" | "regex" | "cuid" | {
72
+ startsWith: string;
73
+ } | {
74
+ endsWith: string;
75
+ };
72
76
  export interface ZodInvalidStringIssue extends ZodIssueBase {
73
77
  code: typeof ZodIssueCode.invalid_string;
74
78
  validation: StringValidation;
package/lib/ZodError.js CHANGED
@@ -165,10 +165,23 @@ const defaultErrorMap = (issue, _ctx) => {
165
165
  message = `Invalid date`;
166
166
  break;
167
167
  case exports.ZodIssueCode.invalid_string:
168
- if (issue.validation !== "regex")
168
+ if (typeof issue.validation === "object") {
169
+ if ("startsWith" in issue.validation) {
170
+ message = `Invalid input: must start with "${issue.validation.startsWith}"`;
171
+ }
172
+ else if ("endsWith" in issue.validation) {
173
+ message = `Invalid input: must start with "${issue.validation.endsWith}"`;
174
+ }
175
+ else {
176
+ util_1.util.assertNever(issue.validation);
177
+ }
178
+ }
179
+ else if (issue.validation !== "regex") {
169
180
  message = `Invalid ${issue.validation}`;
170
- else
181
+ }
182
+ else {
171
183
  message = "Invalid";
184
+ }
172
185
  break;
173
186
  case exports.ZodIssueCode.too_small:
174
187
  if (issue.type === "array")
package/lib/external.js CHANGED
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
@@ -2,8 +2,12 @@ export declare namespace errorUtil {
2
2
  type ErrMessage = string | {
3
3
  message?: string;
4
4
  };
5
- const errToObj: (message?: ErrMessage | undefined) => {
5
+ const errToObj: (message?: string | {
6
+ message?: string | undefined;
7
+ } | undefined) => {
6
8
  message?: string | undefined;
7
9
  };
8
- const toString: (message?: ErrMessage | undefined) => string | undefined;
10
+ const toString: (message?: string | {
11
+ message?: string | undefined;
12
+ } | undefined) => string | undefined;
9
13
  }
@@ -1,5 +1,5 @@
1
- import { type IssueData, type ZodErrorMap, type ZodIssue } from "../ZodError";
2
- import { type ZodParsedType } from "./util";
1
+ import type { IssueData, ZodErrorMap, ZodIssue } from "../ZodError";
2
+ import type { ZodParsedType } from "./util";
3
3
  export declare const makeIssue: (params: {
4
4
  data: any;
5
5
  path: (string | number)[];
@@ -26,7 +26,7 @@ const makeIssue = (params) => {
26
26
  exports.makeIssue = makeIssue;
27
27
  exports.EMPTY_PATH = [];
28
28
  function addIssueToContext(ctx, issueData) {
29
- const issue = (0, exports.makeIssue)({
29
+ const issue = exports.makeIssue({
30
30
  issueData: issueData,
31
31
  data: ctx.data,
32
32
  path: ctx.path,
@@ -34,7 +34,7 @@ function addIssueToContext(ctx, issueData) {
34
34
  ctx.common.contextualErrorMap,
35
35
  ctx.schemaErrorMap,
36
36
  ZodError_1.overrideErrorMap,
37
- ZodError_1.defaultErrorMap, // then global default map
37
+ ZodError_1.defaultErrorMap,
38
38
  ].filter((x) => !!x),
39
39
  });
40
40
  ctx.common.issues.push(issue);
package/lib/index.js CHANGED
@@ -1,11 +1,7 @@
1
1
  "use strict";
2
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
3
  if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
9
5
  }) : (function(o, m, k, k2) {
10
6
  if (k2 === undefined) k2 = k;
11
7
  o[k2] = m[k];
package/lib/index.mjs CHANGED
@@ -278,10 +278,23 @@ const defaultErrorMap = (issue, _ctx) => {
278
278
  message = `Invalid date`;
279
279
  break;
280
280
  case ZodIssueCode.invalid_string:
281
- if (issue.validation !== "regex")
281
+ if (typeof issue.validation === "object") {
282
+ if ("startsWith" in issue.validation) {
283
+ message = `Invalid input: must start with "${issue.validation.startsWith}"`;
284
+ }
285
+ else if ("endsWith" in issue.validation) {
286
+ message = `Invalid input: must start with "${issue.validation.endsWith}"`;
287
+ }
288
+ else {
289
+ util.assertNever(issue.validation);
290
+ }
291
+ }
292
+ else if (issue.validation !== "regex") {
282
293
  message = `Invalid ${issue.validation}`;
283
- else
294
+ }
295
+ else {
284
296
  message = "Invalid";
297
+ }
285
298
  break;
286
299
  case ZodIssueCode.too_small:
287
300
  if (issue.type === "array")
@@ -354,7 +367,7 @@ function addIssueToContext(ctx, issueData) {
354
367
  ctx.common.contextualErrorMap,
355
368
  ctx.schemaErrorMap,
356
369
  overrideErrorMap,
357
- defaultErrorMap, // then global default map
370
+ defaultErrorMap,
358
371
  ].filter((x) => !!x),
359
372
  });
360
373
  ctx.common.issues.push(issue);
@@ -823,6 +836,28 @@ class ZodString extends ZodType {
823
836
  else if (check.kind === "trim") {
824
837
  input.data = input.data.trim();
825
838
  }
839
+ else if (check.kind === "startsWith") {
840
+ if (!input.data.startsWith(check.value)) {
841
+ ctx = this._getOrReturnCtx(input, ctx);
842
+ addIssueToContext(ctx, {
843
+ code: ZodIssueCode.invalid_string,
844
+ validation: { startsWith: check.value },
845
+ message: check.message,
846
+ });
847
+ status.dirty();
848
+ }
849
+ }
850
+ else if (check.kind === "endsWith") {
851
+ if (!input.data.endsWith(check.value)) {
852
+ ctx = this._getOrReturnCtx(input, ctx);
853
+ addIssueToContext(ctx, {
854
+ code: ZodIssueCode.invalid_string,
855
+ validation: { endsWith: check.value },
856
+ message: check.message,
857
+ });
858
+ status.dirty();
859
+ }
860
+ }
826
861
  else {
827
862
  util.assertNever(check);
828
863
  }
@@ -854,6 +889,20 @@ class ZodString extends ZodType {
854
889
  ...errorUtil.errToObj(message),
855
890
  });
856
891
  }
892
+ startsWith(value, message) {
893
+ return this._addCheck({
894
+ kind: "startsWith",
895
+ value: value,
896
+ ...errorUtil.errToObj(message),
897
+ });
898
+ }
899
+ endsWith(value, message) {
900
+ return this._addCheck({
901
+ kind: "endsWith",
902
+ value: value,
903
+ ...errorUtil.errToObj(message),
904
+ });
905
+ }
857
906
  min(minLength, message) {
858
907
  return this._addCheck({
859
908
  kind: "min",
@@ -884,25 +933,23 @@ class ZodString extends ZodType {
884
933
  return !!this._def.checks.find((ch) => ch.kind === "cuid");
885
934
  }
886
935
  get minLength() {
887
- let min = -Infinity;
888
- this._def.checks.map((ch) => {
936
+ let min = null;
937
+ for (const ch of this._def.checks) {
889
938
  if (ch.kind === "min") {
890
- if (min === null || ch.value > min) {
939
+ if (min === null || ch.value > min)
891
940
  min = ch.value;
892
- }
893
941
  }
894
- });
942
+ }
895
943
  return min;
896
944
  }
897
945
  get maxLength() {
898
946
  let max = null;
899
- this._def.checks.map((ch) => {
947
+ for (const ch of this._def.checks) {
900
948
  if (ch.kind === "max") {
901
- if (max === null || ch.value < max) {
949
+ if (max === null || ch.value < max)
902
950
  max = ch.value;
903
- }
904
951
  }
905
- });
952
+ }
906
953
  return max;
907
954
  }
908
955
  }
@@ -1388,7 +1435,7 @@ var objectUtil;
1388
1435
  objectUtil.mergeShapes = (first, second) => {
1389
1436
  return {
1390
1437
  ...first,
1391
- ...second, // second overwrites first
1438
+ ...second,
1392
1439
  };
1393
1440
  };
1394
1441
  })(objectUtil || (objectUtil = {}));
package/lib/index.umd.js CHANGED
@@ -284,10 +284,23 @@
284
284
  message = `Invalid date`;
285
285
  break;
286
286
  case ZodIssueCode.invalid_string:
287
- if (issue.validation !== "regex")
287
+ if (typeof issue.validation === "object") {
288
+ if ("startsWith" in issue.validation) {
289
+ message = `Invalid input: must start with "${issue.validation.startsWith}"`;
290
+ }
291
+ else if ("endsWith" in issue.validation) {
292
+ message = `Invalid input: must start with "${issue.validation.endsWith}"`;
293
+ }
294
+ else {
295
+ util.assertNever(issue.validation);
296
+ }
297
+ }
298
+ else if (issue.validation !== "regex") {
288
299
  message = `Invalid ${issue.validation}`;
289
- else
300
+ }
301
+ else {
290
302
  message = "Invalid";
303
+ }
291
304
  break;
292
305
  case ZodIssueCode.too_small:
293
306
  if (issue.type === "array")
@@ -360,7 +373,7 @@
360
373
  ctx.common.contextualErrorMap,
361
374
  ctx.schemaErrorMap,
362
375
  exports.overrideErrorMap,
363
- defaultErrorMap, // then global default map
376
+ defaultErrorMap,
364
377
  ].filter((x) => !!x),
365
378
  });
366
379
  ctx.common.issues.push(issue);
@@ -829,6 +842,28 @@
829
842
  else if (check.kind === "trim") {
830
843
  input.data = input.data.trim();
831
844
  }
845
+ else if (check.kind === "startsWith") {
846
+ if (!input.data.startsWith(check.value)) {
847
+ ctx = this._getOrReturnCtx(input, ctx);
848
+ addIssueToContext(ctx, {
849
+ code: ZodIssueCode.invalid_string,
850
+ validation: { startsWith: check.value },
851
+ message: check.message,
852
+ });
853
+ status.dirty();
854
+ }
855
+ }
856
+ else if (check.kind === "endsWith") {
857
+ if (!input.data.endsWith(check.value)) {
858
+ ctx = this._getOrReturnCtx(input, ctx);
859
+ addIssueToContext(ctx, {
860
+ code: ZodIssueCode.invalid_string,
861
+ validation: { endsWith: check.value },
862
+ message: check.message,
863
+ });
864
+ status.dirty();
865
+ }
866
+ }
832
867
  else {
833
868
  util.assertNever(check);
834
869
  }
@@ -860,6 +895,20 @@
860
895
  ...errorUtil.errToObj(message),
861
896
  });
862
897
  }
898
+ startsWith(value, message) {
899
+ return this._addCheck({
900
+ kind: "startsWith",
901
+ value: value,
902
+ ...errorUtil.errToObj(message),
903
+ });
904
+ }
905
+ endsWith(value, message) {
906
+ return this._addCheck({
907
+ kind: "endsWith",
908
+ value: value,
909
+ ...errorUtil.errToObj(message),
910
+ });
911
+ }
863
912
  min(minLength, message) {
864
913
  return this._addCheck({
865
914
  kind: "min",
@@ -890,25 +939,23 @@
890
939
  return !!this._def.checks.find((ch) => ch.kind === "cuid");
891
940
  }
892
941
  get minLength() {
893
- let min = -Infinity;
894
- this._def.checks.map((ch) => {
942
+ let min = null;
943
+ for (const ch of this._def.checks) {
895
944
  if (ch.kind === "min") {
896
- if (min === null || ch.value > min) {
945
+ if (min === null || ch.value > min)
897
946
  min = ch.value;
898
- }
899
947
  }
900
- });
948
+ }
901
949
  return min;
902
950
  }
903
951
  get maxLength() {
904
952
  let max = null;
905
- this._def.checks.map((ch) => {
953
+ for (const ch of this._def.checks) {
906
954
  if (ch.kind === "max") {
907
- if (max === null || ch.value < max) {
955
+ if (max === null || ch.value < max)
908
956
  max = ch.value;
909
- }
910
957
  }
911
- });
958
+ }
912
959
  return max;
913
960
  }
914
961
  }
@@ -1394,7 +1441,7 @@
1394
1441
  objectUtil.mergeShapes = (first, second) => {
1395
1442
  return {
1396
1443
  ...first,
1397
- ...second, // second overwrites first
1444
+ ...second,
1398
1445
  };
1399
1446
  };
1400
1447
  })(exports.objectUtil || (exports.objectUtil = {}));