zod 3.17.3 → 3.17.6

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,8 @@ 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
+ - [`zod-xlsx`](https://github.com/sidwebworks/zod-xlsx): A xlsx based resource validator using Zod schemas.
317
+ - [`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
318
 
285
319
  #### Form integrations
286
320
 
@@ -306,7 +340,7 @@ There are a growing number of tools that are built atop or support Zod natively!
306
340
  }
307
341
  ```
308
342
 
309
- ### Node/NPM
343
+ ### Node/npm
310
344
 
311
345
  To install Zod v3:
312
346
 
@@ -318,7 +352,7 @@ pnpm add zod # pnpm
318
352
 
319
353
  ### Deno
320
354
 
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:
355
+ Unlike Node, Deno relies on direct URL imports instead of a package manager like NPM. Zod is available on [deno.land/x](https://deno.land/x). The latest version can be imported like so:
322
356
 
323
357
  ```ts
324
358
  import { z } from "https://deno.land/x/zod/mod.ts";
@@ -327,10 +361,10 @@ import { z } from "https://deno.land/x/zod/mod.ts";
327
361
  You can also specify a particular version:
328
362
 
329
363
  ```ts
330
- import { z } from from "https://deno.land/x/zod@v3.16.1/mod.ts"
364
+ import { z } from "https://deno.land/x/zod@v3.16.1/mod.ts";
331
365
  ```
332
366
 
333
- > The rest of this README assumes you are using NPM and importing directly from the `"zod"` package.
367
+ > The rest of this README assumes you are using npm and importing directly from the `"zod"` package.
334
368
 
335
369
  ## Basic usage
336
370
 
@@ -405,7 +439,7 @@ const tru = z.literal(true);
405
439
  tuna.value; // "tuna"
406
440
  ```
407
441
 
408
- > Currently there is no support for Date or bigint literals in Zod. If you have a use case for this feature, please file an issue.
442
+ > Currently there is no support for Date literals in Zod. If you have a use case for this feature, please file an issue.
409
443
 
410
444
  ## Strings
411
445
 
@@ -420,6 +454,8 @@ z.string().url();
420
454
  z.string().uuid();
421
455
  z.string().cuid();
422
456
  z.string().regex(regex);
457
+ z.string().startsWith(string);
458
+ z.string().endsWith(string);
423
459
 
424
460
  // trim whitespace
425
461
  z.string().trim();
@@ -433,7 +469,7 @@ z.string().nonempty({ message: "Can't be empty" });
433
469
 
434
470
  > Check out [validator.js](https://github.com/validatorjs/validator.js) for a bunch of other useful string validation functions.
435
471
 
436
- You can customize some common errors messages when creating a string schema.
472
+ You can customize some common error messages when creating a string schema.
437
473
 
438
474
  ```ts
439
475
  const name = z.string({
@@ -451,6 +487,8 @@ z.string().length(5, { message: "Must be exactly 5 characters long" });
451
487
  z.string().email({ message: "Invalid email address" });
452
488
  z.string().url({ message: "Invalid url" });
453
489
  z.string().uuid({ message: "Invalid UUID" });
490
+ z.string().startsWith("https://", { message: "Must provide secure URL" });
491
+ z.string().endsWith(".com", { message: "Only .com domains allowed" });
454
492
  ```
455
493
 
456
494
  ## Numbers
@@ -547,7 +585,7 @@ const VALUES = ["Salmon", "Tuna", "Trout"] as const;
547
585
  const FishEnum = z.enum(VALUES);
548
586
  ```
549
587
 
550
- This is not allowed, since Zod isn't able to infer the exact values of each elements.
588
+ This is not allowed, since Zod isn't able to infer the exact values of each element.
551
589
 
552
590
  ```ts
553
591
  const fish = ["Salmon", "Tuna", "Trout"];
@@ -728,7 +766,7 @@ Dog.shape.age; // => number schema
728
766
 
729
767
  ### `.extend`
730
768
 
731
- You can add additional fields an object schema with the `.extend` method.
769
+ You can add additional fields to an object schema with the `.extend` method.
732
770
 
733
771
  ```ts
734
772
  const DogWithBreed = Dog.extend({
@@ -848,7 +886,7 @@ const deepPartialUser = user.deepPartial();
848
886
 
849
887
  ### `.passthrough`
850
888
 
851
- By default Zod objects schemas strip out unrecognized keys during parsing.
889
+ By default Zod object schemas strip out unrecognized keys during parsing.
852
890
 
853
891
  ```ts
854
892
  const person = z.object({
@@ -875,7 +913,7 @@ person.passthrough().parse({
875
913
 
876
914
  ### `.strict`
877
915
 
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.
916
+ 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
917
 
880
918
  ```ts
881
919
  const person = z
@@ -1035,7 +1073,7 @@ const item = z
1035
1073
 
1036
1074
  Record schemas are used to validate types such as `{ [k: string]: number }`.
1037
1075
 
1038
- If you want to validate the _values_ of an object against some schema but don't care about the keys, use `Record`.
1076
+ 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
1077
 
1040
1078
  ```ts
1041
1079
  const NumberCache = z.record(z.number());
@@ -1058,9 +1096,22 @@ userStore["77d2586b-9e8e-4ecf-8b21-ea7e0530eadd"] = {
1058
1096
  }; // TypeError
1059
1097
  ```
1060
1098
 
1099
+ ### Record key type
1100
+
1101
+ If you want to validate both the keys and the values, use
1102
+ `z.record(keyType, valueType)`:
1103
+
1104
+ ```ts
1105
+ const NoEmptyKeysSchema = z.record(z.string().min(1), z.number());
1106
+ NoEmptyKeysSchema.parse({ count: 1 }); // => { 'count': 1 }
1107
+ NoEmptyKeysSchema.parse({ "": 1 }); // fails
1108
+ ```
1109
+
1110
+ _(Notice how when passing two arguments, `valueType` is the second argument)_
1111
+
1061
1112
  **A note on numerical keys**
1062
1113
 
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?
1114
+ 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
1115
 
1065
1116
  As it turns out, TypeScript's behavior surrounding `[k: number]` is a little unintuitive:
1066
1117
 
@@ -1310,7 +1361,7 @@ type myFunction = z.infer<typeof myFunction>;
1310
1361
  // => (arg0: string)=>number
1311
1362
  ``` -->
1312
1363
 
1313
- Function schemas have an `.implement()` method which accepts a function and returns a new function that automatically validates it's inputs and outputs.
1364
+ Function schemas have an `.implement()` method which accepts a function and returns a new function that automatically validates its inputs and outputs.
1314
1365
 
1315
1366
  ```ts
1316
1367
  const trimmedLength = z
@@ -1528,7 +1579,7 @@ const userId = z.string().refine(async (id) => {
1528
1579
  });
1529
1580
  ```
1530
1581
 
1531
- > ⚠️If you use async refinements, you must use the `.parseAsync` method to parse data! Otherwise Zod will throw an error.
1582
+ > ⚠️ If you use async refinements, you must use the `.parseAsync` method to parse data! Otherwise Zod will throw an error.
1532
1583
 
1533
1584
  #### Relationship to transforms
1534
1585
 
@@ -1584,7 +1635,7 @@ const Strings = z.array(z.string()).superRefine((val, ctx) => {
1584
1635
  if (val.length !== new Set(val).size) {
1585
1636
  ctx.addIssue({
1586
1637
  code: z.ZodIssueCode.custom,
1587
- message: `No duplicated allowed.`,
1638
+ message: `No duplicates allowed.`,
1588
1639
  });
1589
1640
  }
1590
1641
  });
@@ -1977,13 +2028,13 @@ Branded -->
1977
2028
  * Missing support for parsing cyclical data (maybe)
1978
2029
  * Missing error customization -->
1979
2030
 
1980
- **Joi**
2031
+ ### Joi
1981
2032
 
1982
2033
  [https://github.com/hapijs/joi](https://github.com/hapijs/joi)
1983
2034
 
1984
2035
  Doesn't support static type inference 😕
1985
2036
 
1986
- **Yup**
2037
+ ### Yup
1987
2038
 
1988
2039
  [https://github.com/jquense/yup](https://github.com/jquense/yup)
1989
2040
 
@@ -1999,7 +2050,7 @@ Yup is a full-featured library that was implemented first in vanilla JS, and lat
1999
2050
 
2000
2051
  <!-- ¹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
2052
 
2002
- **io-ts**
2053
+ ### io-ts
2003
2054
 
2004
2055
  [https://github.com/gcanti/io-ts](https://github.com/gcanti/io-ts)
2005
2056
 
@@ -2050,7 +2101,7 @@ This more declarative API makes schema definitions vastly more concise.
2050
2101
  - Missing promise schemas
2051
2102
  - Missing function schemas
2052
2103
 
2053
- **Runtypes**
2104
+ ### Runtypes
2054
2105
 
2055
2106
  [https://github.com/pelotom/runtypes](https://github.com/pelotom/runtypes)
2056
2107
 
@@ -2063,7 +2114,7 @@ Good type inference support, but limited options for object type masking (no `.p
2063
2114
  - Missing promise schemas
2064
2115
  - Missing error customization
2065
2116
 
2066
- **Ow**
2117
+ ### Ow
2067
2118
 
2068
2119
  [https://github.com/sindresorhus/ow](https://github.com/sindresorhus/ow)
2069
2120
 
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;
@@ -146,8 +150,8 @@ export declare type ZodErrorMap = typeof defaultErrorMap;
146
150
  export declare const defaultErrorMap: (issue: ZodIssueOptionalMessage, _ctx: ErrorMapCtx) => {
147
151
  message: string;
148
152
  };
149
- export declare let overrideErrorMap: (issue: ZodIssueOptionalMessage, _ctx: ErrorMapCtx) => {
153
+ export declare function setErrorMap(map: ZodErrorMap): void;
154
+ export declare function getErrorMap(): (issue: ZodIssueOptionalMessage, _ctx: ErrorMapCtx) => {
150
155
  message: string;
151
156
  };
152
- export declare const setErrorMap: (map: ZodErrorMap) => void;
153
157
  export {};
package/lib/ZodError.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setErrorMap = exports.overrideErrorMap = exports.defaultErrorMap = exports.ZodError = exports.quotelessJson = exports.ZodIssueCode = void 0;
3
+ exports.getErrorMap = exports.setErrorMap = exports.defaultErrorMap = exports.ZodError = exports.quotelessJson = exports.ZodIssueCode = void 0;
4
+ const parseUtil_1 = require("./helpers/parseUtil");
4
5
  const util_1 = require("./helpers/util");
5
6
  exports.ZodIssueCode = util_1.util.arrayToEnum([
6
7
  "invalid_type",
@@ -101,7 +102,7 @@ class ZodError extends Error {
101
102
  return this.message;
102
103
  }
103
104
  get message() {
104
- return JSON.stringify(this.issues, null, 2);
105
+ return JSON.stringify(this.issues, parseUtil_1.jsonStringifyReplacer, 2);
105
106
  }
106
107
  get isEmpty() {
107
108
  return this.issues.length === 0;
@@ -141,7 +142,7 @@ const defaultErrorMap = (issue, _ctx) => {
141
142
  }
142
143
  break;
143
144
  case exports.ZodIssueCode.invalid_literal:
144
- message = `Invalid literal value, expected ${JSON.stringify(issue.expected)}`;
145
+ message = `Invalid literal value, expected ${JSON.stringify(issue.expected, parseUtil_1.jsonStringifyReplacer)}`;
145
146
  break;
146
147
  case exports.ZodIssueCode.unrecognized_keys:
147
148
  message = `Unrecognized key(s) in object: ${util_1.util.joinValues(issue.keys, ", ")}`;
@@ -165,10 +166,23 @@ const defaultErrorMap = (issue, _ctx) => {
165
166
  message = `Invalid date`;
166
167
  break;
167
168
  case exports.ZodIssueCode.invalid_string:
168
- if (issue.validation !== "regex")
169
+ if (typeof issue.validation === "object") {
170
+ if ("startsWith" in issue.validation) {
171
+ message = `Invalid input: must start with "${issue.validation.startsWith}"`;
172
+ }
173
+ else if ("endsWith" in issue.validation) {
174
+ message = `Invalid input: must start with "${issue.validation.endsWith}"`;
175
+ }
176
+ else {
177
+ util_1.util.assertNever(issue.validation);
178
+ }
179
+ }
180
+ else if (issue.validation !== "regex") {
169
181
  message = `Invalid ${issue.validation}`;
170
- else
182
+ }
183
+ else {
171
184
  message = "Invalid";
185
+ }
172
186
  break;
173
187
  case exports.ZodIssueCode.too_small:
174
188
  if (issue.type === "array")
@@ -206,8 +220,12 @@ const defaultErrorMap = (issue, _ctx) => {
206
220
  return { message };
207
221
  };
208
222
  exports.defaultErrorMap = defaultErrorMap;
209
- exports.overrideErrorMap = exports.defaultErrorMap;
210
- const setErrorMap = (map) => {
211
- exports.overrideErrorMap = map;
212
- };
223
+ let overrideErrorMap = exports.defaultErrorMap;
224
+ function setErrorMap(map) {
225
+ overrideErrorMap = map;
226
+ }
213
227
  exports.setErrorMap = setErrorMap;
228
+ function getErrorMap() {
229
+ return overrideErrorMap;
230
+ }
231
+ exports.getErrorMap = getErrorMap;
@@ -76,3 +76,4 @@ export declare const isAborted: (x: ParseReturnType<any>) => x is INVALID;
76
76
  export declare const isDirty: <T>(x: ParseReturnType<T>) => x is OK<T> | DIRTY<T>;
77
77
  export declare const isValid: <T>(x: ParseReturnType<T>) => x is OK<T> | DIRTY<T>;
78
78
  export declare const isAsync: <T>(x: ParseReturnType<T>) => x is AsyncParseReturnType<T>;
79
+ export declare const jsonStringifyReplacer: (_: string, value: any) => any;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isAsync = exports.isValid = exports.isDirty = exports.isAborted = exports.OK = exports.DIRTY = exports.INVALID = exports.ParseStatus = exports.addIssueToContext = exports.EMPTY_PATH = exports.makeIssue = void 0;
3
+ exports.jsonStringifyReplacer = exports.isAsync = exports.isValid = exports.isDirty = exports.isAborted = exports.OK = exports.DIRTY = exports.INVALID = exports.ParseStatus = exports.addIssueToContext = exports.EMPTY_PATH = exports.makeIssue = void 0;
4
4
  const ZodError_1 = require("../ZodError");
5
5
  const makeIssue = (params) => {
6
6
  const { data, path, errorMaps, issueData } = params;
@@ -33,7 +33,7 @@ function addIssueToContext(ctx, issueData) {
33
33
  errorMaps: [
34
34
  ctx.common.contextualErrorMap,
35
35
  ctx.schemaErrorMap,
36
- ZodError_1.overrideErrorMap,
36
+ ZodError_1.getErrorMap(),
37
37
  ZodError_1.defaultErrorMap,
38
38
  ].filter((x) => !!x),
39
39
  });
@@ -108,3 +108,10 @@ const isValid = (x) => x.status === "valid";
108
108
  exports.isValid = isValid;
109
109
  const isAsync = (x) => typeof Promise !== undefined && x instanceof Promise;
110
110
  exports.isAsync = isAsync;
111
+ const jsonStringifyReplacer = (_, value) => {
112
+ if (typeof value === "bigint") {
113
+ return value.toString();
114
+ }
115
+ return value;
116
+ };
117
+ exports.jsonStringifyReplacer = jsonStringifyReplacer;
package/lib/index.mjs CHANGED
@@ -215,7 +215,7 @@ class ZodError extends Error {
215
215
  return this.message;
216
216
  }
217
217
  get message() {
218
- return JSON.stringify(this.issues, null, 2);
218
+ return JSON.stringify(this.issues, jsonStringifyReplacer, 2);
219
219
  }
220
220
  get isEmpty() {
221
221
  return this.issues.length === 0;
@@ -254,7 +254,7 @@ const defaultErrorMap = (issue, _ctx) => {
254
254
  }
255
255
  break;
256
256
  case ZodIssueCode.invalid_literal:
257
- message = `Invalid literal value, expected ${JSON.stringify(issue.expected)}`;
257
+ message = `Invalid literal value, expected ${JSON.stringify(issue.expected, jsonStringifyReplacer)}`;
258
258
  break;
259
259
  case ZodIssueCode.unrecognized_keys:
260
260
  message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, ", ")}`;
@@ -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")
@@ -319,9 +332,12 @@ const defaultErrorMap = (issue, _ctx) => {
319
332
  return { message };
320
333
  };
321
334
  let overrideErrorMap = defaultErrorMap;
322
- const setErrorMap = (map) => {
335
+ function setErrorMap(map) {
323
336
  overrideErrorMap = map;
324
- };
337
+ }
338
+ function getErrorMap() {
339
+ return overrideErrorMap;
340
+ }
325
341
 
326
342
  const makeIssue = (params) => {
327
343
  const { data, path, errorMaps, issueData } = params;
@@ -353,7 +369,7 @@ function addIssueToContext(ctx, issueData) {
353
369
  errorMaps: [
354
370
  ctx.common.contextualErrorMap,
355
371
  ctx.schemaErrorMap,
356
- overrideErrorMap,
372
+ getErrorMap(),
357
373
  defaultErrorMap,
358
374
  ].filter((x) => !!x),
359
375
  });
@@ -420,6 +436,12 @@ const isAborted = (x) => x.status === "aborted";
420
436
  const isDirty = (x) => x.status === "dirty";
421
437
  const isValid = (x) => x.status === "valid";
422
438
  const isAsync = (x) => typeof Promise !== undefined && x instanceof Promise;
439
+ const jsonStringifyReplacer = (_, value) => {
440
+ if (typeof value === "bigint") {
441
+ return value.toString();
442
+ }
443
+ return value;
444
+ };
423
445
 
424
446
  var errorUtil;
425
447
  (function (errorUtil) {
@@ -823,6 +845,28 @@ class ZodString extends ZodType {
823
845
  else if (check.kind === "trim") {
824
846
  input.data = input.data.trim();
825
847
  }
848
+ else if (check.kind === "startsWith") {
849
+ if (!input.data.startsWith(check.value)) {
850
+ ctx = this._getOrReturnCtx(input, ctx);
851
+ addIssueToContext(ctx, {
852
+ code: ZodIssueCode.invalid_string,
853
+ validation: { startsWith: check.value },
854
+ message: check.message,
855
+ });
856
+ status.dirty();
857
+ }
858
+ }
859
+ else if (check.kind === "endsWith") {
860
+ if (!input.data.endsWith(check.value)) {
861
+ ctx = this._getOrReturnCtx(input, ctx);
862
+ addIssueToContext(ctx, {
863
+ code: ZodIssueCode.invalid_string,
864
+ validation: { endsWith: check.value },
865
+ message: check.message,
866
+ });
867
+ status.dirty();
868
+ }
869
+ }
826
870
  else {
827
871
  util.assertNever(check);
828
872
  }
@@ -854,6 +898,20 @@ class ZodString extends ZodType {
854
898
  ...errorUtil.errToObj(message),
855
899
  });
856
900
  }
901
+ startsWith(value, message) {
902
+ return this._addCheck({
903
+ kind: "startsWith",
904
+ value: value,
905
+ ...errorUtil.errToObj(message),
906
+ });
907
+ }
908
+ endsWith(value, message) {
909
+ return this._addCheck({
910
+ kind: "endsWith",
911
+ value: value,
912
+ ...errorUtil.errToObj(message),
913
+ });
914
+ }
857
915
  min(minLength, message) {
858
916
  return this._addCheck({
859
917
  kind: "min",
@@ -884,25 +942,23 @@ class ZodString extends ZodType {
884
942
  return !!this._def.checks.find((ch) => ch.kind === "cuid");
885
943
  }
886
944
  get minLength() {
887
- let min = -Infinity;
888
- this._def.checks.map((ch) => {
945
+ let min = null;
946
+ for (const ch of this._def.checks) {
889
947
  if (ch.kind === "min") {
890
- if (min === null || ch.value > min) {
948
+ if (min === null || ch.value > min)
891
949
  min = ch.value;
892
- }
893
950
  }
894
- });
951
+ }
895
952
  return min;
896
953
  }
897
954
  get maxLength() {
898
955
  let max = null;
899
- this._def.checks.map((ch) => {
956
+ for (const ch of this._def.checks) {
900
957
  if (ch.kind === "max") {
901
- if (max === null || ch.value < max) {
958
+ if (max === null || ch.value < max)
902
959
  max = ch.value;
903
- }
904
960
  }
905
- });
961
+ }
906
962
  return max;
907
963
  }
908
964
  }
@@ -2256,7 +2312,7 @@ class ZodFunction extends ZodType {
2256
2312
  errorMaps: [
2257
2313
  ctx.common.contextualErrorMap,
2258
2314
  ctx.schemaErrorMap,
2259
- overrideErrorMap,
2315
+ getErrorMap(),
2260
2316
  defaultErrorMap,
2261
2317
  ].filter((x) => !!x),
2262
2318
  issueData: {
@@ -2272,7 +2328,7 @@ class ZodFunction extends ZodType {
2272
2328
  errorMaps: [
2273
2329
  ctx.common.contextualErrorMap,
2274
2330
  ctx.schemaErrorMap,
2275
- overrideErrorMap,
2331
+ getErrorMap(),
2276
2332
  defaultErrorMap,
2277
2333
  ].filter((x) => !!x),
2278
2334
  issueData: {
@@ -2836,6 +2892,7 @@ var mod = /*#__PURE__*/Object.freeze({
2836
2892
  isDirty: isDirty,
2837
2893
  isValid: isValid,
2838
2894
  isAsync: isAsync,
2895
+ jsonStringifyReplacer: jsonStringifyReplacer,
2839
2896
  ZodType: ZodType,
2840
2897
  ZodString: ZodString,
2841
2898
  ZodNumber: ZodNumber,
@@ -2916,8 +2973,8 @@ var mod = /*#__PURE__*/Object.freeze({
2916
2973
  quotelessJson: quotelessJson,
2917
2974
  ZodError: ZodError,
2918
2975
  defaultErrorMap: defaultErrorMap,
2919
- get overrideErrorMap () { return overrideErrorMap; },
2920
- setErrorMap: setErrorMap
2976
+ setErrorMap: setErrorMap,
2977
+ getErrorMap: getErrorMap
2921
2978
  });
2922
2979
 
2923
- export { DIRTY, EMPTY_PATH, INVALID, OK, ParseStatus, ZodType as Schema, ZodAny, ZodArray, ZodBigInt, ZodBoolean, ZodDate, ZodDefault, ZodDiscriminatedUnion, ZodEffects, ZodEnum, ZodError, ZodFirstPartyTypeKind, ZodFunction, ZodIntersection, ZodIssueCode, ZodLazy, ZodLiteral, ZodMap, ZodNaN, ZodNativeEnum, ZodNever, ZodNull, ZodNullable, ZodNumber, ZodObject, ZodOptional, ZodParsedType, ZodPromise, ZodRecord, ZodType as ZodSchema, ZodSet, ZodString, ZodEffects as ZodTransformer, ZodTuple, ZodType, ZodUndefined, ZodUnion, ZodUnknown, ZodVoid, addIssueToContext, anyType as any, arrayType as array, bigIntType as bigint, booleanType as boolean, custom, dateType as date, mod as default, defaultErrorMap, discriminatedUnionType as discriminatedUnion, effectsType as effect, enumType as enum, functionType as function, getParsedType, instanceOfType as instanceof, intersectionType as intersection, isAborted, isAsync, isDirty, isValid, late, lazyType as lazy, literalType as literal, makeIssue, mapType as map, nanType as nan, nativeEnumType as nativeEnum, neverType as never, nullType as null, nullableType as nullable, numberType as number, objectType as object, objectUtil, oboolean, onumber, optionalType as optional, ostring, overrideErrorMap, preprocessType as preprocess, promiseType as promise, quotelessJson, recordType as record, setType as set, setErrorMap, strictObjectType as strictObject, stringType as string, effectsType as transformer, tupleType as tuple, undefinedType as undefined, unionType as union, unknownType as unknown, voidType as void, mod as z };
2980
+ export { DIRTY, EMPTY_PATH, INVALID, OK, ParseStatus, ZodType as Schema, ZodAny, ZodArray, ZodBigInt, ZodBoolean, ZodDate, ZodDefault, ZodDiscriminatedUnion, ZodEffects, ZodEnum, ZodError, ZodFirstPartyTypeKind, ZodFunction, ZodIntersection, ZodIssueCode, ZodLazy, ZodLiteral, ZodMap, ZodNaN, ZodNativeEnum, ZodNever, ZodNull, ZodNullable, ZodNumber, ZodObject, ZodOptional, ZodParsedType, ZodPromise, ZodRecord, ZodType as ZodSchema, ZodSet, ZodString, ZodEffects as ZodTransformer, ZodTuple, ZodType, ZodUndefined, ZodUnion, ZodUnknown, ZodVoid, addIssueToContext, anyType as any, arrayType as array, bigIntType as bigint, booleanType as boolean, custom, dateType as date, mod as default, defaultErrorMap, discriminatedUnionType as discriminatedUnion, effectsType as effect, enumType as enum, functionType as function, getErrorMap, getParsedType, instanceOfType as instanceof, intersectionType as intersection, isAborted, isAsync, isDirty, isValid, jsonStringifyReplacer, late, lazyType as lazy, literalType as literal, makeIssue, mapType as map, nanType as nan, nativeEnumType as nativeEnum, neverType as never, nullType as null, nullableType as nullable, numberType as number, objectType as object, objectUtil, oboolean, onumber, optionalType as optional, ostring, preprocessType as preprocess, promiseType as promise, quotelessJson, recordType as record, setType as set, setErrorMap, strictObjectType as strictObject, stringType as string, effectsType as transformer, tupleType as tuple, undefinedType as undefined, unionType as union, unknownType as unknown, voidType as void, mod as z };
package/lib/index.umd.js CHANGED
@@ -221,7 +221,7 @@
221
221
  return this.message;
222
222
  }
223
223
  get message() {
224
- return JSON.stringify(this.issues, null, 2);
224
+ return JSON.stringify(this.issues, jsonStringifyReplacer, 2);
225
225
  }
226
226
  get isEmpty() {
227
227
  return this.issues.length === 0;
@@ -260,7 +260,7 @@
260
260
  }
261
261
  break;
262
262
  case ZodIssueCode.invalid_literal:
263
- message = `Invalid literal value, expected ${JSON.stringify(issue.expected)}`;
263
+ message = `Invalid literal value, expected ${JSON.stringify(issue.expected, jsonStringifyReplacer)}`;
264
264
  break;
265
265
  case ZodIssueCode.unrecognized_keys:
266
266
  message = `Unrecognized key(s) in object: ${util.joinValues(issue.keys, ", ")}`;
@@ -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")
@@ -324,10 +337,13 @@
324
337
  }
325
338
  return { message };
326
339
  };
327
- exports.overrideErrorMap = defaultErrorMap;
328
- const setErrorMap = (map) => {
329
- exports.overrideErrorMap = map;
330
- };
340
+ let overrideErrorMap = defaultErrorMap;
341
+ function setErrorMap(map) {
342
+ overrideErrorMap = map;
343
+ }
344
+ function getErrorMap() {
345
+ return overrideErrorMap;
346
+ }
331
347
 
332
348
  const makeIssue = (params) => {
333
349
  const { data, path, errorMaps, issueData } = params;
@@ -359,7 +375,7 @@
359
375
  errorMaps: [
360
376
  ctx.common.contextualErrorMap,
361
377
  ctx.schemaErrorMap,
362
- exports.overrideErrorMap,
378
+ getErrorMap(),
363
379
  defaultErrorMap,
364
380
  ].filter((x) => !!x),
365
381
  });
@@ -426,6 +442,12 @@
426
442
  const isDirty = (x) => x.status === "dirty";
427
443
  const isValid = (x) => x.status === "valid";
428
444
  const isAsync = (x) => typeof Promise !== undefined && x instanceof Promise;
445
+ const jsonStringifyReplacer = (_, value) => {
446
+ if (typeof value === "bigint") {
447
+ return value.toString();
448
+ }
449
+ return value;
450
+ };
429
451
 
430
452
  var errorUtil;
431
453
  (function (errorUtil) {
@@ -829,6 +851,28 @@
829
851
  else if (check.kind === "trim") {
830
852
  input.data = input.data.trim();
831
853
  }
854
+ else if (check.kind === "startsWith") {
855
+ if (!input.data.startsWith(check.value)) {
856
+ ctx = this._getOrReturnCtx(input, ctx);
857
+ addIssueToContext(ctx, {
858
+ code: ZodIssueCode.invalid_string,
859
+ validation: { startsWith: check.value },
860
+ message: check.message,
861
+ });
862
+ status.dirty();
863
+ }
864
+ }
865
+ else if (check.kind === "endsWith") {
866
+ if (!input.data.endsWith(check.value)) {
867
+ ctx = this._getOrReturnCtx(input, ctx);
868
+ addIssueToContext(ctx, {
869
+ code: ZodIssueCode.invalid_string,
870
+ validation: { endsWith: check.value },
871
+ message: check.message,
872
+ });
873
+ status.dirty();
874
+ }
875
+ }
832
876
  else {
833
877
  util.assertNever(check);
834
878
  }
@@ -860,6 +904,20 @@
860
904
  ...errorUtil.errToObj(message),
861
905
  });
862
906
  }
907
+ startsWith(value, message) {
908
+ return this._addCheck({
909
+ kind: "startsWith",
910
+ value: value,
911
+ ...errorUtil.errToObj(message),
912
+ });
913
+ }
914
+ endsWith(value, message) {
915
+ return this._addCheck({
916
+ kind: "endsWith",
917
+ value: value,
918
+ ...errorUtil.errToObj(message),
919
+ });
920
+ }
863
921
  min(minLength, message) {
864
922
  return this._addCheck({
865
923
  kind: "min",
@@ -890,25 +948,23 @@
890
948
  return !!this._def.checks.find((ch) => ch.kind === "cuid");
891
949
  }
892
950
  get minLength() {
893
- let min = -Infinity;
894
- this._def.checks.map((ch) => {
951
+ let min = null;
952
+ for (const ch of this._def.checks) {
895
953
  if (ch.kind === "min") {
896
- if (min === null || ch.value > min) {
954
+ if (min === null || ch.value > min)
897
955
  min = ch.value;
898
- }
899
956
  }
900
- });
957
+ }
901
958
  return min;
902
959
  }
903
960
  get maxLength() {
904
961
  let max = null;
905
- this._def.checks.map((ch) => {
962
+ for (const ch of this._def.checks) {
906
963
  if (ch.kind === "max") {
907
- if (max === null || ch.value < max) {
964
+ if (max === null || ch.value < max)
908
965
  max = ch.value;
909
- }
910
966
  }
911
- });
967
+ }
912
968
  return max;
913
969
  }
914
970
  }
@@ -2262,7 +2318,7 @@
2262
2318
  errorMaps: [
2263
2319
  ctx.common.contextualErrorMap,
2264
2320
  ctx.schemaErrorMap,
2265
- exports.overrideErrorMap,
2321
+ getErrorMap(),
2266
2322
  defaultErrorMap,
2267
2323
  ].filter((x) => !!x),
2268
2324
  issueData: {
@@ -2278,7 +2334,7 @@
2278
2334
  errorMaps: [
2279
2335
  ctx.common.contextualErrorMap,
2280
2336
  ctx.schemaErrorMap,
2281
- exports.overrideErrorMap,
2337
+ getErrorMap(),
2282
2338
  defaultErrorMap,
2283
2339
  ].filter((x) => !!x),
2284
2340
  issueData: {
@@ -2842,6 +2898,7 @@
2842
2898
  isDirty: isDirty,
2843
2899
  isValid: isValid,
2844
2900
  isAsync: isAsync,
2901
+ jsonStringifyReplacer: jsonStringifyReplacer,
2845
2902
  ZodType: ZodType,
2846
2903
  ZodString: ZodString,
2847
2904
  ZodNumber: ZodNumber,
@@ -2922,8 +2979,8 @@
2922
2979
  quotelessJson: quotelessJson,
2923
2980
  ZodError: ZodError,
2924
2981
  defaultErrorMap: defaultErrorMap,
2925
- get overrideErrorMap () { return exports.overrideErrorMap; },
2926
- setErrorMap: setErrorMap
2982
+ setErrorMap: setErrorMap,
2983
+ getErrorMap: getErrorMap
2927
2984
  });
2928
2985
 
2929
2986
  exports.DIRTY = DIRTY;
@@ -2982,6 +3039,7 @@
2982
3039
  exports.effect = effectsType;
2983
3040
  exports["enum"] = enumType;
2984
3041
  exports["function"] = functionType;
3042
+ exports.getErrorMap = getErrorMap;
2985
3043
  exports.getParsedType = getParsedType;
2986
3044
  exports["instanceof"] = instanceOfType;
2987
3045
  exports.intersection = intersectionType;
@@ -2989,6 +3047,7 @@
2989
3047
  exports.isAsync = isAsync;
2990
3048
  exports.isDirty = isDirty;
2991
3049
  exports.isValid = isValid;
3050
+ exports.jsonStringifyReplacer = jsonStringifyReplacer;
2992
3051
  exports.late = late;
2993
3052
  exports.lazy = lazyType;
2994
3053
  exports.literal = literalType;
package/lib/types.d.ts CHANGED
@@ -98,6 +98,14 @@ declare type ZodStringCheck = {
98
98
  } | {
99
99
  kind: "cuid";
100
100
  message?: string;
101
+ } | {
102
+ kind: "startsWith";
103
+ value: string;
104
+ message?: string;
105
+ } | {
106
+ kind: "endsWith";
107
+ value: string;
108
+ message?: string;
101
109
  } | {
102
110
  kind: "regex";
103
111
  regex: RegExp;
@@ -121,6 +129,8 @@ export declare class ZodString extends ZodType<string, ZodStringDef> {
121
129
  uuid(message?: errorUtil.ErrMessage): ZodString;
122
130
  cuid(message?: errorUtil.ErrMessage): ZodString;
123
131
  regex(regex: RegExp, message?: errorUtil.ErrMessage): ZodString;
132
+ startsWith(value: string, message?: errorUtil.ErrMessage): ZodString;
133
+ endsWith(value: string, message?: errorUtil.ErrMessage): ZodString;
124
134
  min(minLength: number, message?: errorUtil.ErrMessage): ZodString;
125
135
  max(maxLength: number, message?: errorUtil.ErrMessage): ZodString;
126
136
  length(len: number, message?: errorUtil.ErrMessage): ZodString;
@@ -136,8 +146,8 @@ export declare class ZodString extends ZodType<string, ZodStringDef> {
136
146
  get isURL(): boolean;
137
147
  get isUUID(): boolean;
138
148
  get isCUID(): boolean;
139
- get minLength(): number;
140
- get maxLength(): null;
149
+ get minLength(): number | null;
150
+ get maxLength(): number | null;
141
151
  static create: (params?: RawCreateParams) => ZodString;
142
152
  }
143
153
  declare type ZodNumberCheck = {
package/lib/types.js CHANGED
@@ -405,6 +405,28 @@ class ZodString extends ZodType {
405
405
  else if (check.kind === "trim") {
406
406
  input.data = input.data.trim();
407
407
  }
408
+ else if (check.kind === "startsWith") {
409
+ if (!input.data.startsWith(check.value)) {
410
+ ctx = this._getOrReturnCtx(input, ctx);
411
+ parseUtil_1.addIssueToContext(ctx, {
412
+ code: ZodError_1.ZodIssueCode.invalid_string,
413
+ validation: { startsWith: check.value },
414
+ message: check.message,
415
+ });
416
+ status.dirty();
417
+ }
418
+ }
419
+ else if (check.kind === "endsWith") {
420
+ if (!input.data.endsWith(check.value)) {
421
+ ctx = this._getOrReturnCtx(input, ctx);
422
+ parseUtil_1.addIssueToContext(ctx, {
423
+ code: ZodError_1.ZodIssueCode.invalid_string,
424
+ validation: { endsWith: check.value },
425
+ message: check.message,
426
+ });
427
+ status.dirty();
428
+ }
429
+ }
408
430
  else {
409
431
  util_1.util.assertNever(check);
410
432
  }
@@ -436,6 +458,20 @@ class ZodString extends ZodType {
436
458
  ...errorUtil_1.errorUtil.errToObj(message),
437
459
  });
438
460
  }
461
+ startsWith(value, message) {
462
+ return this._addCheck({
463
+ kind: "startsWith",
464
+ value: value,
465
+ ...errorUtil_1.errorUtil.errToObj(message),
466
+ });
467
+ }
468
+ endsWith(value, message) {
469
+ return this._addCheck({
470
+ kind: "endsWith",
471
+ value: value,
472
+ ...errorUtil_1.errorUtil.errToObj(message),
473
+ });
474
+ }
439
475
  min(minLength, message) {
440
476
  return this._addCheck({
441
477
  kind: "min",
@@ -466,25 +502,23 @@ class ZodString extends ZodType {
466
502
  return !!this._def.checks.find((ch) => ch.kind === "cuid");
467
503
  }
468
504
  get minLength() {
469
- let min = -Infinity;
470
- this._def.checks.map((ch) => {
505
+ let min = null;
506
+ for (const ch of this._def.checks) {
471
507
  if (ch.kind === "min") {
472
- if (min === null || ch.value > min) {
508
+ if (min === null || ch.value > min)
473
509
  min = ch.value;
474
- }
475
510
  }
476
- });
511
+ }
477
512
  return min;
478
513
  }
479
514
  get maxLength() {
480
515
  let max = null;
481
- this._def.checks.map((ch) => {
516
+ for (const ch of this._def.checks) {
482
517
  if (ch.kind === "max") {
483
- if (max === null || ch.value < max) {
518
+ if (max === null || ch.value < max)
484
519
  max = ch.value;
485
- }
486
520
  }
487
- });
521
+ }
488
522
  return max;
489
523
  }
490
524
  }
@@ -1859,7 +1893,7 @@ class ZodFunction extends ZodType {
1859
1893
  errorMaps: [
1860
1894
  ctx.common.contextualErrorMap,
1861
1895
  ctx.schemaErrorMap,
1862
- ZodError_1.overrideErrorMap,
1896
+ ZodError_1.getErrorMap(),
1863
1897
  ZodError_1.defaultErrorMap,
1864
1898
  ].filter((x) => !!x),
1865
1899
  issueData: {
@@ -1875,7 +1909,7 @@ class ZodFunction extends ZodType {
1875
1909
  errorMaps: [
1876
1910
  ctx.common.contextualErrorMap,
1877
1911
  ctx.schemaErrorMap,
1878
- ZodError_1.overrideErrorMap,
1912
+ ZodError_1.getErrorMap(),
1879
1913
  ZodError_1.defaultErrorMap,
1880
1914
  ].filter((x) => !!x),
1881
1915
  issueData: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod",
3
- "version": "3.17.3",
3
+ "version": "3.17.6",
4
4
  "description": "TypeScript-first schema declaration and validation library with static type inference",
5
5
  "main": "./lib/index.js",
6
6
  "types": "./index.d.ts",
@@ -59,9 +59,9 @@
59
59
  "test": "jest --coverage",
60
60
  "test:deno": "cd deno && deno test",
61
61
  "prepublishOnly": "npm run test && npm run build && npm run build:deno",
62
- "play": "nodemon -e ts -w . -x ts-node src/playground.ts --project tsconfig.json --trace-warnings",
62
+ "play": "nodemon -e ts -w . -x esr src/playground.ts",
63
63
  "depcruise": "depcruise -c .dependency-cruiser.js src",
64
- "benchmark": "ts-node src/benchmarks/index.ts",
64
+ "benchmark": "esr src/benchmarks/index.ts",
65
65
  "prepare": "husky install"
66
66
  },
67
67
  "devDependencies": {
@@ -73,6 +73,8 @@
73
73
  "@typescript-eslint/parser": "^5.15.0",
74
74
  "benchmark": "^2.1.4",
75
75
  "dependency-cruiser": "^9.19.0",
76
+ "esbuild": "^0.14.49",
77
+ "esbuild-runner": "^2.2.1",
76
78
  "eslint": "^8.11.0",
77
79
  "eslint-config-prettier": "^8.5.0",
78
80
  "eslint-plugin-ban": "^1.6.0",
@@ -88,7 +90,6 @@
88
90
  "rollup": "^2.70.1",
89
91
  "ts-jest": "^27.1.3",
90
92
  "ts-morph": "^14.0.0",
91
- "ts-node": "^10.7.0",
92
93
  "tslib": "^2.3.1",
93
94
  "typescript": "4.1"
94
95
  },