zod 4.6.1 → 4.6.2

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.6.1",
3
+ "version": "4.6.2",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Colin McDonnell <zod@colinhacks.com>",
@@ -1,6 +1,76 @@
1
1
  import { expect, expectTypeOf, test } from "vitest";
2
2
  import { z } from "zod/v4";
3
3
 
4
+ test.each([false, true])("undefined prefault preserves object keys (jitless: %s)", async (jitless) => {
5
+ const previous = z.config().jitless;
6
+ z.config({ jitless });
7
+ try {
8
+ let calls = 0;
9
+ const field = z.union([z.string(), z.undefined()]).prefault(() => {
10
+ calls++;
11
+ return undefined;
12
+ });
13
+ const schema = z.object({ a: field });
14
+ expectTypeOf<z.output<typeof field>>().toEqualTypeOf<string | undefined>();
15
+ expectTypeOf<z.input<typeof schema>>().toEqualTypeOf<{ a?: string | undefined }>();
16
+ expectTypeOf<z.output<typeof schema>>().toEqualTypeOf<{ a: string | undefined }>();
17
+ expect(schema.parse({})).toStrictEqual({ a: undefined });
18
+ expect(calls).toBe(1);
19
+ expect(schema.parse({ a: undefined })).toStrictEqual({ a: undefined });
20
+ expect(calls).toBe(2);
21
+ expect(await schema.parseAsync({})).toStrictEqual({ a: undefined });
22
+ expect(calls).toBe(3);
23
+ expect(schema.parse({ a: "value" })).toStrictEqual({ a: "value" });
24
+ expect(schema.safeParse({ a: 123 }).success).toBe(false);
25
+ expect(calls).toBe(3);
26
+
27
+ const literal = z.prefault(z.string().optional(), undefined);
28
+ expect(z.core.compileFn(z.object({ a: literal }))({})).toStrictEqual({ a: undefined });
29
+ for (const wrapped of [literal, literal.readonly(), literal.nullable(), z.lazy(() => literal)]) {
30
+ expect(z.object({ a: wrapped }).parse({})).toStrictEqual({ a: undefined });
31
+ }
32
+ expect(z.object({ a: literal.optional() }).parse({})).toStrictEqual({});
33
+ expect(z.object({ a: literal.optional() }).parse({ a: undefined })).toStrictEqual({ a: undefined });
34
+ expect(z.tuple([literal]).parse([])).toStrictEqual([undefined]);
35
+ } finally {
36
+ z.config({ jitless: previous });
37
+ }
38
+ });
39
+
40
+ test("prefault preserves transformed undefined output", async () => {
41
+ const field = z
42
+ .string()
43
+ .transform(() => undefined)
44
+ .prefault("fallback");
45
+ expectTypeOf<z.output<typeof field>>().toEqualTypeOf<undefined>();
46
+ expect(field.parse(undefined)).toBeUndefined();
47
+ const schema = z.object({ a: field, optional: z.string().optional() });
48
+ expectTypeOf<z.output<typeof schema>>().toEqualTypeOf<{ a: undefined; optional?: string | undefined }>();
49
+ expect(schema.parse({})).toStrictEqual({ a: undefined });
50
+ expect(z.core.compileFn(schema)({})).toStrictEqual({ a: undefined });
51
+ expect(z.compile(schema).parse({})).toStrictEqual({ a: undefined });
52
+ expect(
53
+ await z
54
+ .object({
55
+ a: z
56
+ .string()
57
+ .transform(async () => undefined)
58
+ .prefault("fallback"),
59
+ })
60
+ .parseAsync({})
61
+ ).toStrictEqual({ a: undefined });
62
+ expect(
63
+ z
64
+ .object({
65
+ a: z
66
+ .string()
67
+ .prefault("fallback")
68
+ .transform(() => undefined),
69
+ })
70
+ .parse({})
71
+ ).toStrictEqual({ a: undefined });
72
+ });
73
+
4
74
  test("basic prefault", () => {
5
75
  const a = z.prefault(z.string().trim(), " default ");
6
76
  expect(a).toBeInstanceOf(z.ZodPrefault);
@@ -1270,9 +1270,9 @@ function generateObjectCheck(
1270
1270
  }
1271
1271
  // else: strip mode (no catchall) - unknown keys ignored, only include known keys
1272
1272
 
1273
- // Shape keys in declared order, then unknown keys in for...in order. A middle-rung key is included iff present on the input, else iff its output is not undefined.
1273
+ // defaulted required outputs keep their keys even when undefined
1274
1274
  const outputVar = newVar(ctx);
1275
- const hasConditionalKeys = allKeys.some((k) => mayOutputUndefined(propShape[k]!) || dropsWhenAbsent(propShape[k]!));
1275
+ const hasConditionalKeys = allKeys.some((k) => mayOmitUndefined(propShape[k]!) || dropsWhenAbsent(propShape[k]!));
1276
1276
 
1277
1277
  // Assert mode: every declared key is validated above, so the output literal and the unknown-key copy are pure waste. A `never` catchall already emitted its rejection loop; a schema catchall still has to validate the values it would otherwise have stored.
1278
1278
  if (!buildsValue) {
@@ -1301,7 +1301,7 @@ function generateObjectCheck(
1301
1301
  const out = propOutputs.get(k);
1302
1302
  if (dropsWhenAbsent(propShape[k]!)) {
1303
1303
  doc.write(`if (${kx} in ${accessor}) ${outputVar}[${kx}] = ${out};`);
1304
- } else if (mayOutputUndefined(propShape[k]!)) {
1304
+ } else if (mayOmitUndefined(propShape[k]!)) {
1305
1305
  doc.write(`if (${out} !== undefined || ${kx} in ${accessor}) ${outputVar}[${kx}] = ${out};`);
1306
1306
  } else {
1307
1307
  doc.write(`${outputVar}[${kx}] = ${out};`);
@@ -1464,9 +1464,11 @@ function dropsWhenAbsent(schema: SomeType): boolean {
1464
1464
  return schema._zod.optin === "optional" && schema._zod.optout === "optional";
1465
1465
  }
1466
1466
 
1467
- // Whether a schema's success-path output can be `undefined`. Object output
1468
- // assembly gives such props the runtime's value-or-presence inclusion rule;
1469
- // everything else keeps the unconditional object-literal slot.
1467
+ function mayOmitUndefined(schema: SomeType): boolean {
1468
+ return (schema._zod.optin !== "defaulted" || schema._zod.optout === "optional") && mayOutputUndefined(schema);
1469
+ }
1470
+
1471
+ // whether a schema's success-path output can be undefined
1470
1472
  function mayOutputUndefined(schema: SomeType): boolean {
1471
1473
  const def = schema._zod.def as {
1472
1474
  type: string;
@@ -1983,7 +1983,7 @@ function handlePropertyResult(
1983
1983
  }
1984
1984
 
1985
1985
  if (result.value === undefined) {
1986
- if (isPresent) {
1986
+ if (isPresent || (optin === "defaulted" && !isOptionalOut)) {
1987
1987
  (final.value as any)[key] = undefined;
1988
1988
  }
1989
1989
  } else {
@@ -2314,16 +2314,16 @@ export const $ZodObjectJIT: core.$constructor<$ZodObject> = /*@__PURE__*/ core.$
2314
2314
  doc.write(`
2315
2315
  if (${id}.issues.length) {${prefixStr(id, k)}
2316
2316
  }
2317
-
2318
- if (${id}.value === undefined) {
2319
- if (${isPresent}) {
2320
- newResult[${k}] = undefined;
2321
- }
2322
- } else {
2317
+ `);
2318
+ if (optin === "defaulted") {
2319
+ doc.write(`newResult[${k}] = ${id}.value;`);
2320
+ } else {
2321
+ doc.write(`
2322
+ if (${id}.value !== undefined || ${isPresent}) {
2323
2323
  newResult[${k}] = ${id}.value;
2324
2324
  }
2325
-
2326
2325
  `);
2326
+ }
2327
2327
  }
2328
2328
  }
2329
2329
 
@@ -4077,7 +4077,7 @@ export interface $ZodPrefaultDef<T extends SomeType = $ZodType> extends $ZodType
4077
4077
  }
4078
4078
 
4079
4079
  export interface $ZodPrefaultInternals<T extends SomeType = $ZodType>
4080
- extends $ZodTypeInternals<util.NoUndefined<core.output<T>>, core.input<T> | undefined> {
4080
+ extends $ZodTypeInternals<core.output<T>, core.input<T> | undefined> {
4081
4081
  def: $ZodPrefaultDef<T>;
4082
4082
  optin: "defaulted";
4083
4083
  optout?: "optional" | undefined;
@@ -1,5 +1,5 @@
1
1
  export const version = {
2
2
  major: 4,
3
3
  minor: 6,
4
- patch: 1 as number,
4
+ patch: 2 as number,
5
5
  } as const;
@@ -1597,7 +1597,7 @@ export const ZodMiniPrefault: core.$constructor<ZodMiniPrefault> = /*@__PURE__*/
1597
1597
  // @__NO_SIDE_EFFECTS__
1598
1598
  export function prefault<T extends SomeType>(
1599
1599
  innerType: T,
1600
- defaultValue: util.NoUndefined<core.input<T>> | (() => util.NoUndefined<core.input<T>>)
1600
+ defaultValue: core.input<T> | (() => core.input<T>)
1601
1601
  ): ZodMiniPrefault<T> {
1602
1602
  return new ZodMiniPrefault({
1603
1603
  type: "prefault",
@@ -165,6 +165,28 @@ test("z.iso.duration", () => {
165
165
  expect(z.safeParse(b, d2).success).toEqual(false);
166
166
  });
167
167
 
168
+ test("z.prefault preserves undefined output", async () => {
169
+ const field = z.prefault(z.union([z.string(), z.undefined()]), () => undefined);
170
+ const schema = z.object({ a: field });
171
+ expectTypeOf<z.output<typeof field>>().toEqualTypeOf<string | undefined>();
172
+ expectTypeOf<z.output<typeof schema>>().toEqualTypeOf<{ a: string | undefined }>();
173
+ expect(z.parse(schema, {})).toStrictEqual({ a: undefined });
174
+ expect(z.parse(schema, { a: undefined })).toStrictEqual({ a: undefined });
175
+ expect(z.parse(schema, { a: "value" })).toStrictEqual({ a: "value" });
176
+ expect(z.safeParse(schema, { a: 123 }).success).toBe(false);
177
+ expect(await z.parseAsync(schema, {})).toStrictEqual({ a: undefined });
178
+ expect(z.parse(z.object({ a: z.prefault(z.optional(z.string()), undefined) }), {})).toStrictEqual({ a: undefined });
179
+ const transformed = z.prefault(
180
+ z.pipe(
181
+ z.string(),
182
+ z.transform(() => undefined)
183
+ ),
184
+ "fallback"
185
+ );
186
+ expectTypeOf<z.output<typeof transformed>>().toEqualTypeOf<undefined>();
187
+ expect(z.parse(z.object({ a: transformed }), {})).toStrictEqual({ a: undefined });
188
+ });
189
+
168
190
  test("z.undefined", () => {
169
191
  const a = z.undefined();
170
192
  expect(z.parse(a, undefined)).toEqual(undefined);
@@ -1010,9 +1010,9 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
1010
1010
  }
1011
1011
  }
1012
1012
  // else: strip mode (no catchall) - unknown keys ignored, only include known keys
1013
- // Shape keys in declared order, then unknown keys in for...in order. A middle-rung key is included iff present on the input, else iff its output is not undefined.
1013
+ // defaulted required outputs keep their keys even when undefined
1014
1014
  const outputVar = newVar(ctx);
1015
- const hasConditionalKeys = allKeys.some((k) => mayOutputUndefined(propShape[k]) || dropsWhenAbsent(propShape[k]));
1015
+ const hasConditionalKeys = allKeys.some((k) => mayOmitUndefined(propShape[k]) || dropsWhenAbsent(propShape[k]));
1016
1016
  // Assert mode: every declared key is validated above, so the output literal and the unknown-key copy are pure waste. A `never` catchall already emitted its rejection loop; a schema catchall still has to validate the values it would otherwise have stored.
1017
1017
  if (!buildsValue) {
1018
1018
  if (unknownKeysMode === "schema") {
@@ -1042,7 +1042,7 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
1042
1042
  if (dropsWhenAbsent(propShape[k])) {
1043
1043
  doc.write(`if (${kx} in ${accessor}) ${outputVar}[${kx}] = ${out};`);
1044
1044
  }
1045
- else if (mayOutputUndefined(propShape[k])) {
1045
+ else if (mayOmitUndefined(propShape[k])) {
1046
1046
  doc.write(`if (${out} !== undefined || ${kx} in ${accessor}) ${outputVar}[${kx}] = ${out};`);
1047
1047
  }
1048
1048
  else {
@@ -1185,9 +1185,10 @@ function fastPathAcceptsAbsence(schema) {
1185
1185
  function dropsWhenAbsent(schema) {
1186
1186
  return schema._zod.optin === "optional" && schema._zod.optout === "optional";
1187
1187
  }
1188
- // Whether a schema's success-path output can be `undefined`. Object output
1189
- // assembly gives such props the runtime's value-or-presence inclusion rule;
1190
- // everything else keeps the unconditional object-literal slot.
1188
+ function mayOmitUndefined(schema) {
1189
+ return (schema._zod.optin !== "defaulted" || schema._zod.optout === "optional") && mayOutputUndefined(schema);
1190
+ }
1191
+ // whether a schema's success-path output can be undefined
1191
1192
  function mayOutputUndefined(schema) {
1192
1193
  const def = schema._zod.def;
1193
1194
  switch (def.type) {
@@ -979,9 +979,9 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
979
979
  }
980
980
  }
981
981
  // else: strip mode (no catchall) - unknown keys ignored, only include known keys
982
- // Shape keys in declared order, then unknown keys in for...in order. A middle-rung key is included iff present on the input, else iff its output is not undefined.
982
+ // defaulted required outputs keep their keys even when undefined
983
983
  const outputVar = newVar(ctx);
984
- const hasConditionalKeys = allKeys.some((k) => mayOutputUndefined(propShape[k]) || dropsWhenAbsent(propShape[k]));
984
+ const hasConditionalKeys = allKeys.some((k) => mayOmitUndefined(propShape[k]) || dropsWhenAbsent(propShape[k]));
985
985
  // Assert mode: every declared key is validated above, so the output literal and the unknown-key copy are pure waste. A `never` catchall already emitted its rejection loop; a schema catchall still has to validate the values it would otherwise have stored.
986
986
  if (!buildsValue) {
987
987
  if (unknownKeysMode === "schema") {
@@ -1011,7 +1011,7 @@ function generateObjectCheck(doc, ctx, schema, accessor, buildsValue = true) {
1011
1011
  if (dropsWhenAbsent(propShape[k])) {
1012
1012
  doc.write(`if (${kx} in ${accessor}) ${outputVar}[${kx}] = ${out};`);
1013
1013
  }
1014
- else if (mayOutputUndefined(propShape[k])) {
1014
+ else if (mayOmitUndefined(propShape[k])) {
1015
1015
  doc.write(`if (${out} !== undefined || ${kx} in ${accessor}) ${outputVar}[${kx}] = ${out};`);
1016
1016
  }
1017
1017
  else {
@@ -1154,9 +1154,10 @@ function fastPathAcceptsAbsence(schema) {
1154
1154
  function dropsWhenAbsent(schema) {
1155
1155
  return schema._zod.optin === "optional" && schema._zod.optout === "optional";
1156
1156
  }
1157
- // Whether a schema's success-path output can be `undefined`. Object output
1158
- // assembly gives such props the runtime's value-or-presence inclusion rule;
1159
- // everything else keeps the unconditional object-literal slot.
1157
+ function mayOmitUndefined(schema) {
1158
+ return (schema._zod.optin !== "defaulted" || schema._zod.optout === "optional") && mayOutputUndefined(schema);
1159
+ }
1160
+ // whether a schema's success-path output can be undefined
1160
1161
  function mayOutputUndefined(schema) {
1161
1162
  const def = schema._zod.def;
1162
1163
  switch (def.type) {
@@ -911,7 +911,7 @@ function handlePropertyResult(result, final, key, input, optin, optout) {
911
911
  return;
912
912
  }
913
913
  if (result.value === undefined) {
914
- if (isPresent) {
914
+ if (isPresent || (optin === "defaulted" && !isOptionalOut)) {
915
915
  final.value[key] = undefined;
916
916
  }
917
917
  }
@@ -1166,16 +1166,17 @@ exports.$ZodObjectJIT = core.$constructor("$ZodObjectJIT", (inst, def) => {
1166
1166
  doc.write(`
1167
1167
  if (${id}.issues.length) {${prefixStr(id, k)}
1168
1168
  }
1169
-
1170
- if (${id}.value === undefined) {
1171
- if (${isPresent}) {
1172
- newResult[${k}] = undefined;
1173
- }
1174
- } else {
1169
+ `);
1170
+ if (optin === "defaulted") {
1171
+ doc.write(`newResult[${k}] = ${id}.value;`);
1172
+ }
1173
+ else {
1174
+ doc.write(`
1175
+ if (${id}.value !== undefined || ${isPresent}) {
1175
1176
  newResult[${k}] = ${id}.value;
1176
1177
  }
1177
-
1178
1178
  `);
1179
+ }
1179
1180
  }
1180
1181
  }
1181
1182
  doc.write(`payload.value = newResult;`);
@@ -1010,7 +1010,7 @@ export interface $ZodPrefaultDef<T extends SomeType = $ZodType> extends $ZodType
1010
1010
  /** The default value. May be a getter. */
1011
1011
  defaultValue: core.input<T>;
1012
1012
  }
1013
- export interface $ZodPrefaultInternals<T extends SomeType = $ZodType> extends $ZodTypeInternals<util.NoUndefined<core.output<T>>, core.input<T> | undefined> {
1013
+ export interface $ZodPrefaultInternals<T extends SomeType = $ZodType> extends $ZodTypeInternals<core.output<T>, core.input<T> | undefined> {
1014
1014
  def: $ZodPrefaultDef<T>;
1015
1015
  optin: "defaulted";
1016
1016
  optout?: "optional" | undefined;
@@ -1010,7 +1010,7 @@ export interface $ZodPrefaultDef<T extends SomeType = $ZodType> extends $ZodType
1010
1010
  /** The default value. May be a getter. */
1011
1011
  defaultValue: core.input<T>;
1012
1012
  }
1013
- export interface $ZodPrefaultInternals<T extends SomeType = $ZodType> extends $ZodTypeInternals<util.NoUndefined<core.output<T>>, core.input<T> | undefined> {
1013
+ export interface $ZodPrefaultInternals<T extends SomeType = $ZodType> extends $ZodTypeInternals<core.output<T>, core.input<T> | undefined> {
1014
1014
  def: $ZodPrefaultDef<T>;
1015
1015
  optin: "defaulted";
1016
1016
  optout?: "optional" | undefined;
@@ -869,7 +869,7 @@ function handlePropertyResult(result, final, key, input, optin, optout) {
869
869
  return;
870
870
  }
871
871
  if (result.value === undefined) {
872
- if (isPresent) {
872
+ if (isPresent || (optin === "defaulted" && !isOptionalOut)) {
873
873
  final.value[key] = undefined;
874
874
  }
875
875
  }
@@ -1124,16 +1124,17 @@ export const $ZodObjectJIT = /*@__PURE__*/ core.$constructor("$ZodObjectJIT", (i
1124
1124
  doc.write(`
1125
1125
  if (${id}.issues.length) {${prefixStr(id, k)}
1126
1126
  }
1127
-
1128
- if (${id}.value === undefined) {
1129
- if (${isPresent}) {
1130
- newResult[${k}] = undefined;
1131
- }
1132
- } else {
1127
+ `);
1128
+ if (optin === "defaulted") {
1129
+ doc.write(`newResult[${k}] = ${id}.value;`);
1130
+ }
1131
+ else {
1132
+ doc.write(`
1133
+ if (${id}.value !== undefined || ${isPresent}) {
1133
1134
  newResult[${k}] = ${id}.value;
1134
1135
  }
1135
-
1136
1136
  `);
1137
+ }
1137
1138
  }
1138
1139
  }
1139
1140
  doc.write(`payload.value = newResult;`);
@@ -4,7 +4,7 @@ exports.version = void 0;
4
4
  exports.version = {
5
5
  major: 4,
6
6
  minor: 6,
7
- patch: 1,
7
+ patch: 2,
8
8
  };
9
9
 
10
10
  // seal-cjs-exports
@@ -1,5 +1,5 @@
1
1
  export const version = {
2
2
  major: 4,
3
3
  minor: 6,
4
- patch: 1,
4
+ patch: 2,
5
5
  };
@@ -367,7 +367,7 @@ export declare function _default<T extends SomeType>(innerType: T, defaultValue:
367
367
  export interface ZodMiniPrefault<T extends SomeType = core.$ZodType> extends _ZodMiniType<core.$ZodPrefaultInternals<T>> {
368
368
  }
369
369
  export declare const ZodMiniPrefault: core.$constructor<ZodMiniPrefault>;
370
- export declare function prefault<T extends SomeType>(innerType: T, defaultValue: util.NoUndefined<core.input<T>> | (() => util.NoUndefined<core.input<T>>)): ZodMiniPrefault<T>;
370
+ export declare function prefault<T extends SomeType>(innerType: T, defaultValue: core.input<T> | (() => core.input<T>)): ZodMiniPrefault<T>;
371
371
  export interface ZodMiniNonOptional<T extends SomeType = core.$ZodType> extends _ZodMiniType<core.$ZodNonOptionalInternals<T>> {
372
372
  }
373
373
  export declare const ZodMiniNonOptional: core.$constructor<ZodMiniNonOptional>;
@@ -367,7 +367,7 @@ export declare function _default<T extends SomeType>(innerType: T, defaultValue:
367
367
  export interface ZodMiniPrefault<T extends SomeType = core.$ZodType> extends _ZodMiniType<core.$ZodPrefaultInternals<T>> {
368
368
  }
369
369
  export declare const ZodMiniPrefault: core.$constructor<ZodMiniPrefault>;
370
- export declare function prefault<T extends SomeType>(innerType: T, defaultValue: util.NoUndefined<core.input<T>> | (() => util.NoUndefined<core.input<T>>)): ZodMiniPrefault<T>;
370
+ export declare function prefault<T extends SomeType>(innerType: T, defaultValue: core.input<T> | (() => core.input<T>)): ZodMiniPrefault<T>;
371
371
  export interface ZodMiniNonOptional<T extends SomeType = core.$ZodType> extends _ZodMiniType<core.$ZodNonOptionalInternals<T>> {
372
372
  }
373
373
  export declare const ZodMiniNonOptional: core.$constructor<ZodMiniNonOptional>;