agent-relay 6.0.2 → 6.0.4
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 +10 -3
- package/dist/index.cjs +2024 -962
- package/dist/src/cli/commands/cloud.d.ts.map +1 -1
- package/dist/src/cli/commands/cloud.js +17 -144
- package/dist/src/cli/commands/cloud.js.map +1 -1
- package/dist/src/cli/commands/setup.d.ts +2 -39
- package/dist/src/cli/commands/setup.d.ts.map +1 -1
- package/dist/src/cli/commands/setup.js +4 -333
- package/dist/src/cli/commands/setup.js.map +1 -1
- package/dist/src/cli/lib/auth-ssh.d.ts +2 -33
- package/dist/src/cli/lib/auth-ssh.d.ts.map +1 -1
- package/dist/src/cli/lib/auth-ssh.js +3 -39
- package/dist/src/cli/lib/auth-ssh.js.map +1 -1
- package/dist/src/cli/lib/ssh-interactive.d.ts +4 -66
- package/dist/src/cli/lib/ssh-interactive.d.ts.map +1 -1
- package/dist/src/cli/lib/ssh-interactive.js +4 -436
- package/dist/src/cli/lib/ssh-interactive.js.map +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/README.md +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/locales/package.json +2 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/mini/package.json +2 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/package.json +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v3/tests/all-errors.test.ts +3 -3
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v3/tests/object.test.ts +5 -5
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v3/tests/partials.test.ts +3 -3
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/errors.ts +2 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/external.ts +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/from-json-schema.ts +39 -23
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/parse.ts +6 -6
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/schemas.ts +389 -148
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/catch.test.ts +4 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/codec.test.ts +142 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/continuability.test.ts +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/datetime.test.ts +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/default.test.ts +44 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/detached-methods.test.ts +197 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/discriminated-unions.test.ts +31 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/error-utils.test.ts +214 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/from-json-schema.test.ts +161 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/function.test.ts +6 -6
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/global-config.test.ts +39 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/index.test.ts +2 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/jitless-allows-eval.test.ts +46 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/locales_ka.test.ts +29 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/locales_ro.test.ts +24 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/number.test.ts +55 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/object.test.ts +83 -6
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/optional.test.ts +114 -4
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/partial.test.ts +28 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/prefault.test.ts +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/record.test.ts +85 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/recursive-types.test.ts +49 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/refine.test.ts +63 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/string.test.ts +50 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/template-literal.test.ts +4 -4
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/to-json-schema.test.ts +128 -14
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/transform.test.ts +17 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/tuple.test.ts +315 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/union.test.ts +54 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/api.ts +25 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/checks.ts +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/core.ts +17 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/errors.ts +31 -24
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/json-schema-processors.ts +15 -17
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/parse.ts +7 -7
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/regexes.ts +8 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/schemas.ts +218 -66
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/tests/locales/el.test.ts +215 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/tests/locales/fr.test.ts +72 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/tests/locales/hr.test.ts +163 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/tests/locales/uz.test.ts +22 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/tests/record-constructor.test.ts +58 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/to-json-schema.ts +9 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/util.ts +52 -35
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/core/versions.ts +2 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/locales/el.ts +121 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/locales/en.ts +4 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/locales/fr.ts +24 -8
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/locales/hr.ts +131 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/locales/index.ts +3 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/locales/it.ts +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/locales/ka.ts +8 -8
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/locales/ro.ts +129 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/locales/uz.ts +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/mini/external.ts +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/mini/schemas.ts +55 -24
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/mini/tests/codec.test.ts +19 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/mini/tests/index.test.ts +27 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/mini/tests/object.test.ts +9 -9
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/mini/tests/recursive-types.test.ts +51 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/mini/tests/string.test.ts +5 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v3/package.json +2 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/classic/errors.js +2 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/classic/external.d.cts +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/classic/external.d.ts +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/classic/from-json-schema.cjs +31 -16
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/classic/from-json-schema.js +31 -16
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/classic/package.json +2 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/classic/schemas.cjs +346 -117
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/classic/schemas.d.cts +34 -12
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/classic/schemas.d.ts +34 -12
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/classic/schemas.js +345 -117
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/api.cjs +7 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/api.d.cts +20 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/api.d.ts +20 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/api.js +7 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/checks.d.cts +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/checks.d.ts +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/core.cjs +3 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/core.js +4 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/errors.cjs +26 -23
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/errors.d.cts +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/errors.d.ts +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/errors.js +26 -23
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/json-schema-processors.cjs +14 -19
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/json-schema-processors.js +14 -19
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/package.json +2 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/parse.cjs +7 -7
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/parse.js +7 -7
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/regexes.cjs +9 -3
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/regexes.d.cts +6 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/regexes.d.ts +6 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/regexes.js +7 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/schemas.cjs +196 -59
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/schemas.d.cts +21 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/schemas.d.ts +21 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/schemas.js +196 -59
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/to-json-schema.cjs +10 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/to-json-schema.js +10 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/util.cjs +54 -30
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/util.d.cts +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/util.d.ts +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/util.js +55 -32
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/versions.cjs +2 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/versions.d.cts +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/versions.d.ts +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/core/versions.js +2 -2
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/el.cjs +136 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/el.d.cts +5 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/el.d.ts +4 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/el.js +109 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/en.cjs +4 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/en.js +4 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/fr.cjs +24 -7
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/fr.js +24 -7
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/hr.cjs +149 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/hr.d.cts +5 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/hr.d.ts +4 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/hr.js +122 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/index.cjs +8 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/index.d.cts +3 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/index.d.ts +3 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/index.js +3 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/it.cjs +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/it.js +1 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/ka.cjs +8 -8
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/ka.js +8 -8
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/package.json +2 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/ro.cjs +146 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/ro.d.cts +5 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/ro.d.ts +4 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/ro.js +119 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/uz.cjs +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/locales/uz.js +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/mini/external.d.cts +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/mini/external.d.ts +1 -0
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/mini/package.json +2 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/mini/schemas.cjs +41 -4
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/mini/schemas.d.cts +27 -9
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/mini/schemas.d.ts +27 -9
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/mini/schemas.js +40 -4
- package/node_modules/@relaycast/sdk/node_modules/zod/v4/package.json +2 -1
- package/node_modules/@relaycast/sdk/node_modules/zod/v4-mini/package.json +2 -1
- package/package.json +10 -10
package/node_modules/@relaycast/sdk/node_modules/zod/src/v4/classic/tests/to-json-schema.test.ts
CHANGED
|
@@ -212,7 +212,7 @@ describe("toJSONSchema", () => {
|
|
|
212
212
|
{
|
|
213
213
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
214
214
|
"format": "cuid",
|
|
215
|
-
"pattern": "^[cC][
|
|
215
|
+
"pattern": "^[cC][0-9a-z]{6,}$",
|
|
216
216
|
"type": "string",
|
|
217
217
|
}
|
|
218
218
|
`);
|
|
@@ -647,6 +647,47 @@ describe("toJSONSchema", () => {
|
|
|
647
647
|
`);
|
|
648
648
|
});
|
|
649
649
|
|
|
650
|
+
test("number constraints intersection draft-04", () => {
|
|
651
|
+
// When both minimum (from .int()) and exclusiveMinimum (from .positive()) exist,
|
|
652
|
+
// the more restrictive constraint should be used
|
|
653
|
+
expect(z.toJSONSchema(z.number().int().positive().lte(65535), { target: "draft-04" })).toMatchInlineSnapshot(`
|
|
654
|
+
{
|
|
655
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
656
|
+
"exclusiveMinimum": true,
|
|
657
|
+
"maximum": 65535,
|
|
658
|
+
"minimum": 0,
|
|
659
|
+
"type": "integer",
|
|
660
|
+
}
|
|
661
|
+
`);
|
|
662
|
+
// Same for openapi-3.0
|
|
663
|
+
expect(z.toJSONSchema(z.number().int().positive().lte(65535), { target: "openapi-3.0" })).toMatchInlineSnapshot(`
|
|
664
|
+
{
|
|
665
|
+
"exclusiveMinimum": true,
|
|
666
|
+
"maximum": 65535,
|
|
667
|
+
"minimum": 0,
|
|
668
|
+
"type": "integer",
|
|
669
|
+
}
|
|
670
|
+
`);
|
|
671
|
+
// When inclusive minimum is more restrictive than exclusive minimum
|
|
672
|
+
expect(z.toJSONSchema(z.number().gt(3).gte(10), { target: "draft-04" })).toMatchInlineSnapshot(`
|
|
673
|
+
{
|
|
674
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
675
|
+
"minimum": 10,
|
|
676
|
+
"type": "number",
|
|
677
|
+
}
|
|
678
|
+
`);
|
|
679
|
+
// Same logic for maximum constraints
|
|
680
|
+
expect(z.toJSONSchema(z.number().int().negative(), { target: "draft-04" })).toMatchInlineSnapshot(`
|
|
681
|
+
{
|
|
682
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
|
683
|
+
"exclusiveMaximum": true,
|
|
684
|
+
"maximum": 0,
|
|
685
|
+
"minimum": -9007199254740991,
|
|
686
|
+
"type": "integer",
|
|
687
|
+
}
|
|
688
|
+
`);
|
|
689
|
+
});
|
|
690
|
+
|
|
650
691
|
test("target normalization draft-04 and draft-07", () => {
|
|
651
692
|
// Test that both old (draft-4, draft-7) and new (draft-04, draft-07) target formats work
|
|
652
693
|
// Test draft-04 / draft-4
|
|
@@ -1219,11 +1260,9 @@ describe("toJSONSchema", () => {
|
|
|
1219
1260
|
},
|
|
1220
1261
|
"definitions": {
|
|
1221
1262
|
"primary": {
|
|
1222
|
-
"id": "primary",
|
|
1223
1263
|
"type": "string",
|
|
1224
1264
|
},
|
|
1225
1265
|
"rest": {
|
|
1226
|
-
"id": "rest",
|
|
1227
1266
|
"type": "number",
|
|
1228
1267
|
},
|
|
1229
1268
|
},
|
|
@@ -2014,11 +2053,9 @@ test("extract schemas with id", () => {
|
|
|
2014
2053
|
{
|
|
2015
2054
|
"$defs": {
|
|
2016
2055
|
"age": {
|
|
2017
|
-
"id": "age",
|
|
2018
2056
|
"type": "number",
|
|
2019
2057
|
},
|
|
2020
2058
|
"name": {
|
|
2021
|
-
"id": "name",
|
|
2022
2059
|
"type": "string",
|
|
2023
2060
|
},
|
|
2024
2061
|
},
|
|
@@ -2106,7 +2143,6 @@ test("describe with id", () => {
|
|
|
2106
2143
|
{
|
|
2107
2144
|
"$defs": {
|
|
2108
2145
|
"jobId": {
|
|
2109
|
-
"id": "jobId",
|
|
2110
2146
|
"type": "string",
|
|
2111
2147
|
},
|
|
2112
2148
|
},
|
|
@@ -2131,6 +2167,45 @@ test("describe with id", () => {
|
|
|
2131
2167
|
`);
|
|
2132
2168
|
});
|
|
2133
2169
|
|
|
2170
|
+
test("id is stripped from $defs entries (draft-2020-12)", () => {
|
|
2171
|
+
// The `id` in `.meta()` is a registration tag — it determines the $defs key
|
|
2172
|
+
// but should not leak into the definition body, where it is redundant.
|
|
2173
|
+
const inner = z.string().meta({ id: "Inner" });
|
|
2174
|
+
const result = z.toJSONSchema(z.object({ a: inner, b: inner }));
|
|
2175
|
+
expect(result.$defs?.Inner).toEqual({ type: "string" });
|
|
2176
|
+
expect((result.$defs?.Inner as any).id).toBeUndefined();
|
|
2177
|
+
});
|
|
2178
|
+
|
|
2179
|
+
test("id is stripped from definitions entries (draft-04)", () => {
|
|
2180
|
+
// In draft-04, `id` is a reserved keyword that sets a base URI for the
|
|
2181
|
+
// subschema. Leaking Zod's registration tag here is semantically wrong, so
|
|
2182
|
+
// ensure it is stripped.
|
|
2183
|
+
const inner = z.string().meta({ id: "Inner" });
|
|
2184
|
+
const result = z.toJSONSchema(z.object({ a: inner, b: inner }), { target: "draft-04" }) as any;
|
|
2185
|
+
expect(result.definitions?.Inner).toEqual({ type: "string" });
|
|
2186
|
+
expect(result.definitions?.Inner?.id).toBeUndefined();
|
|
2187
|
+
});
|
|
2188
|
+
|
|
2189
|
+
test("id is stripped from root schema", () => {
|
|
2190
|
+
// The registration tag should not appear on the root either.
|
|
2191
|
+
const A = z.object({ name: z.string() }).meta({ id: "A" });
|
|
2192
|
+
const result = z.toJSONSchema(A);
|
|
2193
|
+
expect((result as any).id).toBeUndefined();
|
|
2194
|
+
});
|
|
2195
|
+
|
|
2196
|
+
test("id is observable in override callback", () => {
|
|
2197
|
+
// The strip happens after override callbacks run, so userland override code
|
|
2198
|
+
// can still read `jsonSchema.id` if it wants to.
|
|
2199
|
+
const inner = z.string().meta({ id: "Inner" });
|
|
2200
|
+
const seenIds: Array<string | undefined> = [];
|
|
2201
|
+
z.toJSONSchema(z.object({ a: inner }), {
|
|
2202
|
+
override: ({ jsonSchema }) => {
|
|
2203
|
+
if (jsonSchema.id !== undefined) seenIds.push(jsonSchema.id as string);
|
|
2204
|
+
},
|
|
2205
|
+
});
|
|
2206
|
+
expect(seenIds).toContain("Inner");
|
|
2207
|
+
});
|
|
2208
|
+
|
|
2134
2209
|
test("describe with id on wrapper", () => {
|
|
2135
2210
|
// Test that $ref propagation works when processor sets a different ref (readonly -> innerType)
|
|
2136
2211
|
// but parent was extracted due to having an id
|
|
@@ -2146,7 +2221,6 @@ test("describe with id on wrapper", () => {
|
|
|
2146
2221
|
{
|
|
2147
2222
|
"$defs": {
|
|
2148
2223
|
"roJobId": {
|
|
2149
|
-
"id": "roJobId",
|
|
2150
2224
|
"readOnly": true,
|
|
2151
2225
|
"type": "string",
|
|
2152
2226
|
},
|
|
@@ -2185,12 +2259,10 @@ test("overwrite id", () => {
|
|
|
2185
2259
|
{
|
|
2186
2260
|
"$defs": {
|
|
2187
2261
|
"aaa": {
|
|
2188
|
-
"id": "aaa",
|
|
2189
2262
|
"type": "string",
|
|
2190
2263
|
},
|
|
2191
2264
|
"bbb": {
|
|
2192
2265
|
"$ref": "#/$defs/aaa",
|
|
2193
|
-
"id": "bbb",
|
|
2194
2266
|
},
|
|
2195
2267
|
},
|
|
2196
2268
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -2224,12 +2296,10 @@ test("overwrite id", () => {
|
|
|
2224
2296
|
{
|
|
2225
2297
|
"$defs": {
|
|
2226
2298
|
"aaa": {
|
|
2227
|
-
"id": "aaa",
|
|
2228
2299
|
"type": "string",
|
|
2229
2300
|
},
|
|
2230
2301
|
"ccc": {
|
|
2231
2302
|
"$ref": "#/$defs/aaa",
|
|
2232
|
-
"id": "ccc",
|
|
2233
2303
|
},
|
|
2234
2304
|
},
|
|
2235
2305
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -2351,7 +2421,6 @@ test("top-level readonly", () => {
|
|
|
2351
2421
|
"$defs": {
|
|
2352
2422
|
"B": {
|
|
2353
2423
|
"additionalProperties": false,
|
|
2354
|
-
"id": "B",
|
|
2355
2424
|
"properties": {
|
|
2356
2425
|
"a": {
|
|
2357
2426
|
"$ref": "#",
|
|
@@ -2370,7 +2439,6 @@ test("top-level readonly", () => {
|
|
|
2370
2439
|
},
|
|
2371
2440
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
2372
2441
|
"additionalProperties": false,
|
|
2373
|
-
"id": "A",
|
|
2374
2442
|
"properties": {
|
|
2375
2443
|
"b": {
|
|
2376
2444
|
"$ref": "#/$defs/B",
|
|
@@ -2497,7 +2565,6 @@ test("_ref", () => {
|
|
|
2497
2565
|
{
|
|
2498
2566
|
"$defs": {
|
|
2499
2567
|
"foo": {
|
|
2500
|
-
"id": "foo",
|
|
2501
2568
|
"type": "string",
|
|
2502
2569
|
},
|
|
2503
2570
|
},
|
|
@@ -2560,6 +2627,38 @@ test("defaults/prefaults", () => {
|
|
|
2560
2627
|
`);
|
|
2561
2628
|
});
|
|
2562
2629
|
|
|
2630
|
+
test("falsy prefaults (false, 0, empty string)", () => {
|
|
2631
|
+
// boolean prefault false
|
|
2632
|
+
const a = z.boolean().prefault(false);
|
|
2633
|
+
expect(z.toJSONSchema(a, { io: "input" })).toMatchInlineSnapshot(`
|
|
2634
|
+
{
|
|
2635
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
2636
|
+
"default": false,
|
|
2637
|
+
"type": "boolean",
|
|
2638
|
+
}
|
|
2639
|
+
`);
|
|
2640
|
+
|
|
2641
|
+
// number prefault 0
|
|
2642
|
+
const b = z.number().prefault(0);
|
|
2643
|
+
expect(z.toJSONSchema(b, { io: "input" })).toMatchInlineSnapshot(`
|
|
2644
|
+
{
|
|
2645
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
2646
|
+
"default": 0,
|
|
2647
|
+
"type": "number",
|
|
2648
|
+
}
|
|
2649
|
+
`);
|
|
2650
|
+
|
|
2651
|
+
// string prefault empty string
|
|
2652
|
+
const c = z.string().prefault("");
|
|
2653
|
+
expect(z.toJSONSchema(c, { io: "input" })).toMatchInlineSnapshot(`
|
|
2654
|
+
{
|
|
2655
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
2656
|
+
"default": "",
|
|
2657
|
+
"type": "string",
|
|
2658
|
+
}
|
|
2659
|
+
`);
|
|
2660
|
+
});
|
|
2661
|
+
|
|
2563
2662
|
test("input type", () => {
|
|
2564
2663
|
const schema = z.object({
|
|
2565
2664
|
a: z.string(),
|
|
@@ -2988,3 +3087,18 @@ test("cycle detection - mutual recursion", () => {
|
|
|
2988
3087
|
Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.]
|
|
2989
3088
|
`);
|
|
2990
3089
|
});
|
|
3090
|
+
|
|
3091
|
+
test("recursive lazy with describe does not stack overflow", () => {
|
|
3092
|
+
const NodeSchema: z.ZodType = z.lazy(() =>
|
|
3093
|
+
z
|
|
3094
|
+
.object({
|
|
3095
|
+
value: z.string().describe("node value"),
|
|
3096
|
+
children: z.array(NodeSchema.describe("child node")).optional().describe("child list"),
|
|
3097
|
+
})
|
|
3098
|
+
.describe("tree node")
|
|
3099
|
+
);
|
|
3100
|
+
|
|
3101
|
+
const result = z.toJSONSchema(NodeSchema, { cycles: "ref", reused: "ref" });
|
|
3102
|
+
expect(result).toBeDefined();
|
|
3103
|
+
expect(result.$defs).toBeDefined();
|
|
3104
|
+
});
|
|
@@ -359,3 +359,20 @@ test("encode error", () => {
|
|
|
359
359
|
`[ZodEncodeError: Encountered unidirectional transform during encode: ZodTransform]`
|
|
360
360
|
);
|
|
361
361
|
});
|
|
362
|
+
|
|
363
|
+
test("transform context should have addIssue", () => {
|
|
364
|
+
const schema = z.transform((val, ctx) => {
|
|
365
|
+
ctx.addIssue({
|
|
366
|
+
code: "custom",
|
|
367
|
+
message: "Not valid",
|
|
368
|
+
});
|
|
369
|
+
return val;
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const result = schema.safeParse("test");
|
|
373
|
+
|
|
374
|
+
expect(result.success).toBe(false);
|
|
375
|
+
if (!result.success) {
|
|
376
|
+
expect(result.error.issues[0].message).toBe("Not valid");
|
|
377
|
+
}
|
|
378
|
+
});
|
|
@@ -107,7 +107,9 @@ test("async validation", async () => {
|
|
|
107
107
|
|
|
108
108
|
test("tuple with optional elements", () => {
|
|
109
109
|
const myTuple = z.tuple([z.string(), z.number().optional(), z.string().optional()]).rest(z.boolean());
|
|
110
|
-
expectTypeOf<typeof myTuple._output>().toEqualTypeOf<
|
|
110
|
+
expectTypeOf<typeof myTuple._output>().toEqualTypeOf<
|
|
111
|
+
[string, (number | undefined)?, (string | undefined)?, ...boolean[]]
|
|
112
|
+
>();
|
|
111
113
|
|
|
112
114
|
const goodData = [["asdf"], ["asdf", 1234], ["asdf", 1234, "asdf"], ["asdf", 1234, "asdf", true, false, true]];
|
|
113
115
|
for (const data of goodData) {
|
|
@@ -149,7 +151,9 @@ test("tuple with optional elements followed by required", () => {
|
|
|
149
151
|
|
|
150
152
|
test("tuple with all optional elements", () => {
|
|
151
153
|
const allOptionalTuple = z.tuple([z.string().optional(), z.number().optional(), z.boolean().optional()]);
|
|
152
|
-
expectTypeOf<typeof allOptionalTuple._output>().toEqualTypeOf<
|
|
154
|
+
expectTypeOf<typeof allOptionalTuple._output>().toEqualTypeOf<
|
|
155
|
+
[(string | undefined)?, (number | undefined)?, (boolean | undefined)?]
|
|
156
|
+
>();
|
|
153
157
|
|
|
154
158
|
// Empty array should be valid (all items optional)
|
|
155
159
|
expect(allOptionalTuple.parse([])).toEqual([]);
|
|
@@ -165,6 +169,227 @@ test("tuple with all optional elements", () => {
|
|
|
165
169
|
expect(() => allOptionalTuple.parse(["hello", 42, true, "extra"])).toThrow();
|
|
166
170
|
});
|
|
167
171
|
|
|
172
|
+
test("tuple fills defaults for missing trailing elements", () => {
|
|
173
|
+
// Issue #5229: trailing `.default()`/`.prefault()` elements should be
|
|
174
|
+
// filled in when the input array is shorter than the tuple.
|
|
175
|
+
const t = z.tuple([z.string(), z.string().default("bravo")]);
|
|
176
|
+
expectTypeOf<typeof t._output>().toEqualTypeOf<[string, string]>();
|
|
177
|
+
expectTypeOf<typeof t._input>().toEqualTypeOf<[string, (string | undefined)?]>();
|
|
178
|
+
|
|
179
|
+
expect(t.parse(["alpha", "charlie"])).toEqual(["alpha", "charlie"]);
|
|
180
|
+
expect(t.parse(["alpha"])).toEqual(["alpha", "bravo"]);
|
|
181
|
+
|
|
182
|
+
// Multiple trailing defaults
|
|
183
|
+
const multi = z.tuple([z.string(), z.number().default(42), z.boolean().default(true)]);
|
|
184
|
+
expect(multi.parse(["hello"])).toEqual(["hello", 42, true]);
|
|
185
|
+
expect(multi.parse(["hello", 100])).toEqual(["hello", 100, true]);
|
|
186
|
+
expect(multi.parse(["hello", 100, false])).toEqual(["hello", 100, false]);
|
|
187
|
+
|
|
188
|
+
// Prefault parity
|
|
189
|
+
expect(z.tuple([z.string(), z.string().prefault("delta")]).parse(["alpha"])).toEqual(["alpha", "delta"]);
|
|
190
|
+
|
|
191
|
+
// Defaults wrapped in modifiers: `optout` propagates through these, so the
|
|
192
|
+
// fix is not type-name specific.
|
|
193
|
+
expect(z.tuple([z.string(), z.string().default("x").nullable()]).parse(["alpha"])).toEqual(["alpha", "x"]);
|
|
194
|
+
expect(z.tuple([z.string(), z.string().default("x").readonly()]).parse(["alpha"])).toEqual(["alpha", "x"]);
|
|
195
|
+
expect(z.tuple([z.string(), z.string().default("x").catch("y")]).parse(["alpha"])).toEqual(["alpha", "x"]);
|
|
196
|
+
expect(z.tuple([z.string(), z.string().default("x").pipe(z.string())]).parse(["alpha"])).toEqual(["alpha", "x"]);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("tuple fills defaults under async parse", async () => {
|
|
200
|
+
const t = z.tuple([z.string(), z.string().default("zulu")]);
|
|
201
|
+
await expect(t.parseAsync(["alpha"])).resolves.toEqual(["alpha", "zulu"]);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("tuple keeps length-1 array for missing `.optional()` elements", () => {
|
|
205
|
+
// Backwards compat: a trailing `.optional()` element that is omitted from
|
|
206
|
+
// the input must NOT be filled with `undefined` — the result stays length-1.
|
|
207
|
+
// Only schemas that produce a defined value get materialized.
|
|
208
|
+
const t = z.tuple([z.string(), z.string().optional()]);
|
|
209
|
+
const out = t.parse(["alpha"]);
|
|
210
|
+
expect(out).toEqual(["alpha"]);
|
|
211
|
+
expect(out.length).toEqual(1);
|
|
212
|
+
|
|
213
|
+
// `z.undefined()` is NOT a synonym for `.optional()` — its value type is
|
|
214
|
+
// *must be undefined*, so the slot is required input. Omitting it triggers
|
|
215
|
+
// a single `too_small` (no element-level errors, matching v3's abort
|
|
216
|
+
// semantics); passing explicit `undefined` succeeds and is preserved.
|
|
217
|
+
expect(z.tuple([z.string(), z.undefined()]).safeParse(["alpha"]).error!.issues).toMatchInlineSnapshot(`
|
|
218
|
+
[
|
|
219
|
+
{
|
|
220
|
+
"code": "too_small",
|
|
221
|
+
"inclusive": true,
|
|
222
|
+
"message": "Too small: expected array to have >=2 items",
|
|
223
|
+
"minimum": 2,
|
|
224
|
+
"origin": "array",
|
|
225
|
+
"path": [],
|
|
226
|
+
},
|
|
227
|
+
]
|
|
228
|
+
`);
|
|
229
|
+
expect(z.tuple([z.string(), z.undefined()]).parse(["alpha", undefined])).toHaveLength(2);
|
|
230
|
+
|
|
231
|
+
// `.optional().nullable()` still trims — `.optional()` propagates the
|
|
232
|
+
// optin/optout flags through the nullable wrapper.
|
|
233
|
+
expect(z.tuple([z.string(), z.string().optional().nullable()]).parse(["alpha"])).toHaveLength(1);
|
|
234
|
+
|
|
235
|
+
// Multiple trailing optionals trim the same way — we don't fill the tail
|
|
236
|
+
// with literal `undefined`s.
|
|
237
|
+
const many = z.tuple([z.string(), z.string().optional(), z.string().optional(), z.string().optional()]);
|
|
238
|
+
expect(many.parse(["alpha"])).toEqual(["alpha"]);
|
|
239
|
+
expect(many.parse(["alpha", "beta"])).toEqual(["alpha", "beta"]);
|
|
240
|
+
|
|
241
|
+
// Explicit `undefined` inside `input.length` IS preserved — only slots
|
|
242
|
+
// past the input get trimmed.
|
|
243
|
+
const r = many.parse(["alpha", undefined]);
|
|
244
|
+
expect(r.length).toEqual(2);
|
|
245
|
+
expect(1 in r).toEqual(true);
|
|
246
|
+
|
|
247
|
+
// Trailing optionals after a default that fires are still trimmed.
|
|
248
|
+
expect(
|
|
249
|
+
z.tuple([z.string(), z.string().default("d"), z.string().optional(), z.string().optional()]).parse(["alpha"])
|
|
250
|
+
).toEqual(["alpha", "d"]);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("tuple result is dense when optional precedes a default", () => {
|
|
254
|
+
// `.optional()` before a `.default()` must produce an explicit `undefined`
|
|
255
|
+
// (not a sparse hole), otherwise `1 in r`, `JSON.stringify`, `Object.keys`,
|
|
256
|
+
// and iteration all behave wrong.
|
|
257
|
+
const t = z.tuple([z.string(), z.string().optional(), z.string().default("z")]);
|
|
258
|
+
const r = t.parse(["alpha"]);
|
|
259
|
+
expect(r).toEqual(["alpha", undefined, "z"]);
|
|
260
|
+
expect(r.length).toEqual(3);
|
|
261
|
+
expect(1 in r).toEqual(true);
|
|
262
|
+
expect(JSON.stringify(r)).toEqual('["alpha",null,"z"]');
|
|
263
|
+
|
|
264
|
+
// Trailing optional after a default is still dropped (no later default
|
|
265
|
+
// forces it to materialize).
|
|
266
|
+
expect(z.tuple([z.string(), z.string().default("d"), z.string().optional()]).parse(["alpha"])).toEqual([
|
|
267
|
+
"alpha",
|
|
268
|
+
"d",
|
|
269
|
+
]);
|
|
270
|
+
|
|
271
|
+
// Multiple interleaved optional/default — every slot up to the last
|
|
272
|
+
// default must be present and dense.
|
|
273
|
+
const interleaved = z.tuple([
|
|
274
|
+
z.string(),
|
|
275
|
+
z.string().optional(),
|
|
276
|
+
z.string().default("d"),
|
|
277
|
+
z.string().optional(),
|
|
278
|
+
z.string().default("e"),
|
|
279
|
+
]);
|
|
280
|
+
const out = interleaved.parse(["alpha"]);
|
|
281
|
+
expect(out).toEqual(["alpha", undefined, "d", undefined, "e"]);
|
|
282
|
+
expect(1 in out && 3 in out).toEqual(true);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
test("tuple truncates absent optional rejections only when the output tail is optional", () => {
|
|
286
|
+
// An absent optional-output slot can only be swallowed when every later
|
|
287
|
+
// output slot is optional too. If a later default would make the output tail
|
|
288
|
+
// required, truncating would violate the tuple's output type.
|
|
289
|
+
const refusesUndefined = z
|
|
290
|
+
.string()
|
|
291
|
+
.optional()
|
|
292
|
+
.refine((s) => s !== undefined, "must not be undefined");
|
|
293
|
+
|
|
294
|
+
const trailingDefault = z.tuple([z.string(), refusesUndefined, z.string().default("d")]);
|
|
295
|
+
const r1 = trailingDefault.safeParse(["alpha"]);
|
|
296
|
+
expect(r1.success).toBe(false);
|
|
297
|
+
expect(r1.error!.issues[0].path).toEqual([1]);
|
|
298
|
+
|
|
299
|
+
// Optional slots BEFORE the rejected one still cannot hide a later required
|
|
300
|
+
// output slot.
|
|
301
|
+
const beforeReject = z.tuple([z.string(), z.string().optional(), refusesUndefined, z.string().default("d")]);
|
|
302
|
+
const r2 = beforeReject.safeParse(["alpha"]);
|
|
303
|
+
expect(r2.success).toBe(false);
|
|
304
|
+
expect(r2.error!.issues[0].path).toEqual([2]);
|
|
305
|
+
|
|
306
|
+
// No default after — truncate still applies, no spurious issue surfaces.
|
|
307
|
+
const noTrailingDefault = z.tuple([z.string(), refusesUndefined]);
|
|
308
|
+
const r3 = noTrailingDefault.safeParse(["alpha"]);
|
|
309
|
+
expect(r3.success).toBe(true);
|
|
310
|
+
expect(r3.data).toEqual(["alpha"]);
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test("tuple rejects absent optional before required output under async parse", async () => {
|
|
314
|
+
const refusesUndefined = z
|
|
315
|
+
.string()
|
|
316
|
+
.optional()
|
|
317
|
+
.refine(async (s) => s !== undefined, "must not be undefined");
|
|
318
|
+
|
|
319
|
+
const schema = z.tuple([z.string(), refusesUndefined, z.string().default("d")]);
|
|
320
|
+
const r = await schema.safeParseAsync(["alpha"]);
|
|
321
|
+
expect(r.success).toBe(false);
|
|
322
|
+
expect(r.error!.issues[0].path).toEqual([1]);
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
test("tuple rejects absent exact optional before defaulted output", () => {
|
|
326
|
+
const schema = z.tuple([z.string(), z.string().exactOptional(), z.string().default("fallback")]);
|
|
327
|
+
expectTypeOf<typeof schema._output>().toEqualTypeOf<[string, string, string]>();
|
|
328
|
+
|
|
329
|
+
const missingExact = schema.safeParse(["alpha"]);
|
|
330
|
+
expect(missingExact.success).toBe(false);
|
|
331
|
+
expect(missingExact.error!.issues[0].path).toEqual([1]);
|
|
332
|
+
|
|
333
|
+
expect(schema.parse(["alpha", "bravo"])).toEqual(["alpha", "bravo", "fallback"]);
|
|
334
|
+
expect(schema.safeParse(["alpha", undefined]).success).toBe(false);
|
|
335
|
+
|
|
336
|
+
// With no later required output slot, exact optional still behaves like an
|
|
337
|
+
// omitted tuple tail and truncates cleanly.
|
|
338
|
+
expect(z.tuple([z.string(), z.string().exactOptional(), z.string().optional()]).parse(["alpha"])).toEqual(["alpha"]);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
test("tuple preserves explicit undefined inside input even for optional-out schemas", () => {
|
|
342
|
+
// The trim only runs for slots PAST `input.length`. An explicit `undefined`
|
|
343
|
+
// value supplied by the caller at index < input.length must survive, even
|
|
344
|
+
// when the schema produces undefined as a valid output (e.g.
|
|
345
|
+
// `z.string().or(z.undefined())`, `z.string().optional()`, `z.undefined()`).
|
|
346
|
+
const orUndefined = z.tuple([z.string(), z.string().or(z.undefined())]);
|
|
347
|
+
const r1 = orUndefined.parse(["alpha", undefined]);
|
|
348
|
+
expect(r1.length).toEqual(2);
|
|
349
|
+
expect(r1[1]).toBeUndefined();
|
|
350
|
+
expect(1 in r1).toEqual(true);
|
|
351
|
+
expect(JSON.stringify(r1)).toEqual('["alpha",null]');
|
|
352
|
+
|
|
353
|
+
// Same for `.optional()`.
|
|
354
|
+
const opt = z.tuple([z.string(), z.string().optional()]);
|
|
355
|
+
const r2 = opt.parse(["alpha", undefined]);
|
|
356
|
+
expect(r2.length).toEqual(2);
|
|
357
|
+
expect(1 in r2).toEqual(true);
|
|
358
|
+
|
|
359
|
+
// Same for `z.undefined()` literal.
|
|
360
|
+
const lit = z.tuple([z.string(), z.undefined()]);
|
|
361
|
+
const r3 = lit.parse(["alpha", undefined]);
|
|
362
|
+
expect(r3.length).toEqual(2);
|
|
363
|
+
expect(1 in r3).toEqual(true);
|
|
364
|
+
|
|
365
|
+
// Mid-tuple explicit undefined surrounded by defined values is also kept.
|
|
366
|
+
const mid = z.tuple([z.string(), z.string().or(z.undefined()), z.string()]);
|
|
367
|
+
const r4 = mid.parse(["alpha", undefined, "gamma"]);
|
|
368
|
+
expect(r4).toEqual(["alpha", undefined, "gamma"]);
|
|
369
|
+
expect(r4.length).toEqual(3);
|
|
370
|
+
expect(1 in r4).toEqual(true);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
test("tuple does NOT break when a required slot fails past input length", () => {
|
|
374
|
+
// A required slot (no `.optional()` chain, so optout !== "optional") past
|
|
375
|
+
// input length must still surface an issue rather than silently swallowing
|
|
376
|
+
// it. Otherwise we'd accept arbitrarily short tuples for required-tail
|
|
377
|
+
// schemas. The precheck collapses this into a single `too_small`.
|
|
378
|
+
const schema = z.tuple([z.string(), z.string()]);
|
|
379
|
+
expect(schema.safeParse(["alpha"]).error!.issues).toMatchInlineSnapshot(`
|
|
380
|
+
[
|
|
381
|
+
{
|
|
382
|
+
"code": "too_small",
|
|
383
|
+
"inclusive": true,
|
|
384
|
+
"message": "Too small: expected array to have >=2 items",
|
|
385
|
+
"minimum": 2,
|
|
386
|
+
"origin": "array",
|
|
387
|
+
"path": [],
|
|
388
|
+
},
|
|
389
|
+
]
|
|
390
|
+
`);
|
|
391
|
+
});
|
|
392
|
+
|
|
168
393
|
test("tuple with rest schema", () => {
|
|
169
394
|
const myTuple = z.tuple([z.string(), z.number()]).rest(z.boolean());
|
|
170
395
|
expect(myTuple.parse(["asdf", 1234, true, false, true])).toEqual(["asdf", 1234, true, false, true]);
|
|
@@ -181,3 +406,91 @@ test("sparse array input", () => {
|
|
|
181
406
|
const schema = z.tuple([z.string(), z.number()]);
|
|
182
407
|
expect(() => schema.parse(new Array(2))).toThrow();
|
|
183
408
|
});
|
|
409
|
+
|
|
410
|
+
test("under-length tuple emits a single too_small with optStart minimum", () => {
|
|
411
|
+
const allRequired = z.tuple([z.string(), z.string()]);
|
|
412
|
+
expect(allRequired.safeParse(["a"]).error!.issues).toMatchInlineSnapshot(`
|
|
413
|
+
[
|
|
414
|
+
{
|
|
415
|
+
"code": "too_small",
|
|
416
|
+
"inclusive": true,
|
|
417
|
+
"message": "Too small: expected array to have >=2 items",
|
|
418
|
+
"minimum": 2,
|
|
419
|
+
"origin": "array",
|
|
420
|
+
"path": [],
|
|
421
|
+
},
|
|
422
|
+
]
|
|
423
|
+
`);
|
|
424
|
+
expect(allRequired.safeParse([]).error!.issues).toMatchInlineSnapshot(`
|
|
425
|
+
[
|
|
426
|
+
{
|
|
427
|
+
"code": "too_small",
|
|
428
|
+
"inclusive": true,
|
|
429
|
+
"message": "Too small: expected array to have >=2 items",
|
|
430
|
+
"minimum": 2,
|
|
431
|
+
"origin": "array",
|
|
432
|
+
"path": [],
|
|
433
|
+
},
|
|
434
|
+
]
|
|
435
|
+
`);
|
|
436
|
+
|
|
437
|
+
const trailingOptional = z.tuple([z.string(), z.number().optional()]);
|
|
438
|
+
expect(trailingOptional.safeParse([]).error!.issues).toMatchInlineSnapshot(`
|
|
439
|
+
[
|
|
440
|
+
{
|
|
441
|
+
"code": "too_small",
|
|
442
|
+
"inclusive": true,
|
|
443
|
+
"message": "Too small: expected array to have >=1 items",
|
|
444
|
+
"minimum": 1,
|
|
445
|
+
"origin": "array",
|
|
446
|
+
"path": [],
|
|
447
|
+
},
|
|
448
|
+
]
|
|
449
|
+
`);
|
|
450
|
+
|
|
451
|
+
const interiorOptional = z.tuple([z.string(), z.number().optional(), z.string()]);
|
|
452
|
+
expect(interiorOptional.safeParse(["a", 1]).error!.issues).toMatchInlineSnapshot(`
|
|
453
|
+
[
|
|
454
|
+
{
|
|
455
|
+
"code": "too_small",
|
|
456
|
+
"inclusive": true,
|
|
457
|
+
"message": "Too small: expected array to have >=3 items",
|
|
458
|
+
"minimum": 3,
|
|
459
|
+
"origin": "array",
|
|
460
|
+
"path": [],
|
|
461
|
+
},
|
|
462
|
+
]
|
|
463
|
+
`);
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
test("too_big tuple still surfaces element-wise type errors for present indices", () => {
|
|
467
|
+
const schema = z.tuple([z.string(), z.number()]);
|
|
468
|
+
expect(schema.safeParse([1, "x", "extra"]).error!.issues).toMatchInlineSnapshot(`
|
|
469
|
+
[
|
|
470
|
+
{
|
|
471
|
+
"code": "too_big",
|
|
472
|
+
"inclusive": true,
|
|
473
|
+
"maximum": 2,
|
|
474
|
+
"message": "Too big: expected array to have <=2 items",
|
|
475
|
+
"origin": "array",
|
|
476
|
+
"path": [],
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
"code": "invalid_type",
|
|
480
|
+
"expected": "string",
|
|
481
|
+
"message": "Invalid input: expected string, received number",
|
|
482
|
+
"path": [
|
|
483
|
+
0,
|
|
484
|
+
],
|
|
485
|
+
},
|
|
486
|
+
{
|
|
487
|
+
"code": "invalid_type",
|
|
488
|
+
"expected": "number",
|
|
489
|
+
"message": "Invalid input: expected number, received string",
|
|
490
|
+
"path": [
|
|
491
|
+
1,
|
|
492
|
+
],
|
|
493
|
+
},
|
|
494
|
+
]
|
|
495
|
+
`);
|
|
496
|
+
});
|
|
@@ -217,3 +217,57 @@ test("z.xor() type inference", () => {
|
|
|
217
217
|
type Result = z.infer<typeof schema>;
|
|
218
218
|
expectTypeOf<Result>().toEqualTypeOf<string | number | boolean>();
|
|
219
219
|
});
|
|
220
|
+
|
|
221
|
+
test("z.union([]) constructs and rejects all input", () => {
|
|
222
|
+
const schema = z.union([]);
|
|
223
|
+
expectTypeOf<z.infer<typeof schema>>().toEqualTypeOf<never>();
|
|
224
|
+
const result = schema.safeParse("anything");
|
|
225
|
+
expect(result.success).toEqual(false);
|
|
226
|
+
if (!result.success) {
|
|
227
|
+
expect(result.error.issues).toMatchInlineSnapshot(`
|
|
228
|
+
[
|
|
229
|
+
{
|
|
230
|
+
"code": "invalid_union",
|
|
231
|
+
"errors": [],
|
|
232
|
+
"message": "Invalid input",
|
|
233
|
+
"path": [],
|
|
234
|
+
},
|
|
235
|
+
]
|
|
236
|
+
`);
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("z.xor([]) constructs and rejects all input", () => {
|
|
241
|
+
const schema = z.xor([]);
|
|
242
|
+
expectTypeOf<z.infer<typeof schema>>().toEqualTypeOf<never>();
|
|
243
|
+
const result = schema.safeParse("anything");
|
|
244
|
+
expect(result.success).toEqual(false);
|
|
245
|
+
if (!result.success) {
|
|
246
|
+
expect(result.error.issues).toMatchInlineSnapshot(`
|
|
247
|
+
[
|
|
248
|
+
{
|
|
249
|
+
"code": "invalid_union",
|
|
250
|
+
"errors": [],
|
|
251
|
+
"message": "Invalid input",
|
|
252
|
+
"path": [],
|
|
253
|
+
},
|
|
254
|
+
]
|
|
255
|
+
`);
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("z.discriminatedUnion with empty options constructs and rejects", () => {
|
|
260
|
+
const schema = z.discriminatedUnion("type", [] as any);
|
|
261
|
+
const nonObject = schema.safeParse("nope");
|
|
262
|
+
expect(nonObject.success).toEqual(false);
|
|
263
|
+
if (!nonObject.success) {
|
|
264
|
+
expect(nonObject.error.issues[0].code).toBe("invalid_type");
|
|
265
|
+
}
|
|
266
|
+
const obj = schema.safeParse({ type: "x" });
|
|
267
|
+
expect(obj.success).toEqual(false);
|
|
268
|
+
if (!obj.success) {
|
|
269
|
+
expect(obj.error.issues[0].code).toBe("invalid_union");
|
|
270
|
+
expect((obj.error.issues[0] as any).errors).toEqual([]);
|
|
271
|
+
expect((obj.error.issues[0] as any).options).toEqual([]);
|
|
272
|
+
}
|
|
273
|
+
});
|