@gabrielbryk/json-schema-to-zod 2.7.3 → 2.8.0

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.
Files changed (52) hide show
  1. package/.github/workflows/release.yml +0 -5
  2. package/CHANGELOG.md +17 -0
  3. package/dist/cjs/generators/generateBundle.js +311 -0
  4. package/dist/cjs/index.js +2 -0
  5. package/dist/cjs/jsonSchemaToZod.js +96 -2
  6. package/dist/cjs/parsers/parseArray.js +34 -15
  7. package/dist/cjs/parsers/parseIfThenElse.js +2 -1
  8. package/dist/cjs/parsers/parseNumber.js +81 -39
  9. package/dist/cjs/parsers/parseObject.js +32 -9
  10. package/dist/cjs/parsers/parseSchema.js +23 -1
  11. package/dist/cjs/parsers/parseString.js +294 -54
  12. package/dist/cjs/utils/cycles.js +113 -0
  13. package/dist/cjs/utils/withMessage.js +4 -5
  14. package/dist/esm/Types.js +2 -1
  15. package/dist/esm/cli.js +12 -10
  16. package/dist/esm/generators/generateBundle.js +311 -0
  17. package/dist/esm/index.js +46 -28
  18. package/dist/esm/jsonSchemaToZod.js +105 -7
  19. package/dist/esm/parsers/parseAllOf.js +8 -5
  20. package/dist/esm/parsers/parseAnyOf.js +10 -6
  21. package/dist/esm/parsers/parseArray.js +47 -24
  22. package/dist/esm/parsers/parseBoolean.js +5 -1
  23. package/dist/esm/parsers/parseConst.js +5 -1
  24. package/dist/esm/parsers/parseDefault.js +7 -3
  25. package/dist/esm/parsers/parseEnum.js +5 -1
  26. package/dist/esm/parsers/parseIfThenElse.js +11 -6
  27. package/dist/esm/parsers/parseMultipleType.js +7 -3
  28. package/dist/esm/parsers/parseNot.js +8 -4
  29. package/dist/esm/parsers/parseNull.js +5 -1
  30. package/dist/esm/parsers/parseNullable.js +8 -4
  31. package/dist/esm/parsers/parseNumber.js +88 -42
  32. package/dist/esm/parsers/parseObject.js +59 -33
  33. package/dist/esm/parsers/parseOneOf.js +10 -6
  34. package/dist/esm/parsers/parseSchema.js +85 -59
  35. package/dist/esm/parsers/parseSimpleDiscriminatedOneOf.js +10 -6
  36. package/dist/esm/parsers/parseString.js +303 -59
  37. package/dist/esm/utils/anyOrUnknown.js +5 -1
  38. package/dist/esm/utils/cliTools.js +13 -7
  39. package/dist/esm/utils/cycles.js +113 -0
  40. package/dist/esm/utils/half.js +5 -1
  41. package/dist/esm/utils/jsdocs.js +8 -3
  42. package/dist/esm/utils/omit.js +5 -1
  43. package/dist/esm/utils/withMessage.js +8 -6
  44. package/dist/esm/zodToJsonSchema.js +4 -1
  45. package/dist/types/Types.d.ts +8 -0
  46. package/dist/types/generators/generateBundle.d.ts +57 -0
  47. package/dist/types/index.d.ts +2 -0
  48. package/dist/types/parsers/parseString.d.ts +2 -2
  49. package/dist/types/utils/cycles.d.ts +7 -0
  50. package/dist/types/utils/jsdocs.d.ts +1 -1
  51. package/dist/types/utils/withMessage.d.ts +6 -1
  52. package/package.json +1 -1
@@ -3,71 +3,113 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseNumber = void 0;
4
4
  const withMessage_js_1 = require("../utils/withMessage.js");
5
5
  const parseNumber = (schema) => {
6
- let r = "z.number()";
6
+ const formatError = schema.errorMessage?.format;
7
+ const numericFormatMap = {
8
+ int32: "z.int32",
9
+ uint32: "z.uint32",
10
+ float32: "z.float32",
11
+ float64: "z.float64",
12
+ safeint: "z.safeint",
13
+ int64: "z.int64",
14
+ uint64: "z.uint64",
15
+ };
16
+ const mappedFormat = schema.format && numericFormatMap[schema.format] ? numericFormatMap[schema.format] : undefined;
17
+ const formatParams = formatError !== undefined ? `{ error: ${JSON.stringify(formatError)} }` : "";
18
+ let r = mappedFormat ? `${mappedFormat}(${formatParams})` : "z.number()";
7
19
  if (schema.type === "integer") {
8
- r += (0, withMessage_js_1.withMessage)(schema, "type", () => [".int(", ")"]);
20
+ if (!mappedFormat) {
21
+ r += (0, withMessage_js_1.withMessage)(schema, "type", () => ({
22
+ opener: ".int(",
23
+ closer: ")",
24
+ messagePrefix: "{ error: ",
25
+ messageCloser: " })",
26
+ }));
27
+ }
9
28
  }
10
29
  else {
11
- r += (0, withMessage_js_1.withMessage)(schema, "format", ({ value }) => {
12
- if (value === "int64") {
13
- return [".int(", ")"];
14
- }
15
- });
30
+ if (!mappedFormat) {
31
+ r += (0, withMessage_js_1.withMessage)(schema, "format", ({ value }) => {
32
+ if (value === "int64") {
33
+ return {
34
+ opener: ".int(",
35
+ closer: ")",
36
+ messagePrefix: "{ error: ",
37
+ messageCloser: " })",
38
+ };
39
+ }
40
+ });
41
+ }
16
42
  }
17
43
  r += (0, withMessage_js_1.withMessage)(schema, "multipleOf", ({ value, json }) => {
18
44
  if (value === 1) {
19
45
  if (r.startsWith("z.number().int(")) {
20
46
  return;
21
47
  }
22
- return [".int(", ")"];
48
+ return {
49
+ opener: ".int(",
50
+ closer: ")",
51
+ messagePrefix: "{ error: ",
52
+ messageCloser: " })",
53
+ };
23
54
  }
24
- return [`.multipleOf(${json}`, ", ", ")"];
55
+ return {
56
+ opener: `.multipleOf(${json}`,
57
+ closer: ")",
58
+ messagePrefix: ", { error: ",
59
+ messageCloser: " })",
60
+ };
25
61
  });
26
62
  if (typeof schema.minimum === "number") {
27
63
  if (schema.exclusiveMinimum === true) {
28
- r += (0, withMessage_js_1.withMessage)(schema, "minimum", ({ json }) => [
29
- `.gt(${json}`,
30
- ", ",
31
- ")",
32
- ]);
64
+ r += (0, withMessage_js_1.withMessage)(schema, "minimum", ({ json }) => ({
65
+ opener: `.gt(${json}`,
66
+ closer: ")",
67
+ messagePrefix: ", { error: ",
68
+ messageCloser: " })",
69
+ }));
33
70
  }
34
71
  else {
35
- r += (0, withMessage_js_1.withMessage)(schema, "minimum", ({ json }) => [
36
- `.gte(${json}`,
37
- ", ",
38
- ")",
39
- ]);
72
+ r += (0, withMessage_js_1.withMessage)(schema, "minimum", ({ json }) => ({
73
+ opener: `.gte(${json}`,
74
+ closer: ")",
75
+ messagePrefix: ", { error: ",
76
+ messageCloser: " })",
77
+ }));
40
78
  }
41
79
  }
42
80
  else if (typeof schema.exclusiveMinimum === "number") {
43
- r += (0, withMessage_js_1.withMessage)(schema, "exclusiveMinimum", ({ json }) => [
44
- `.gt(${json}`,
45
- ", ",
46
- ")",
47
- ]);
81
+ r += (0, withMessage_js_1.withMessage)(schema, "exclusiveMinimum", ({ json }) => ({
82
+ opener: `.gt(${json}`,
83
+ closer: ")",
84
+ messagePrefix: ", { error: ",
85
+ messageCloser: " })",
86
+ }));
48
87
  }
49
88
  if (typeof schema.maximum === "number") {
50
89
  if (schema.exclusiveMaximum === true) {
51
- r += (0, withMessage_js_1.withMessage)(schema, "maximum", ({ json }) => [
52
- `.lt(${json}`,
53
- ", ",
54
- ")",
55
- ]);
90
+ r += (0, withMessage_js_1.withMessage)(schema, "maximum", ({ json }) => ({
91
+ opener: `.lt(${json}`,
92
+ closer: ")",
93
+ messagePrefix: ", { error: ",
94
+ messageCloser: " })",
95
+ }));
56
96
  }
57
97
  else {
58
- r += (0, withMessage_js_1.withMessage)(schema, "maximum", ({ json }) => [
59
- `.lte(${json}`,
60
- ", ",
61
- ")",
62
- ]);
98
+ r += (0, withMessage_js_1.withMessage)(schema, "maximum", ({ json }) => ({
99
+ opener: `.lte(${json}`,
100
+ closer: ")",
101
+ messagePrefix: ", { error: ",
102
+ messageCloser: " })",
103
+ }));
63
104
  }
64
105
  }
65
106
  else if (typeof schema.exclusiveMaximum === "number") {
66
- r += (0, withMessage_js_1.withMessage)(schema, "exclusiveMaximum", ({ json }) => [
67
- `.lt(${json}`,
68
- ", ",
69
- ")",
70
- ]);
107
+ r += (0, withMessage_js_1.withMessage)(schema, "exclusiveMaximum", ({ json }) => ({
108
+ opener: `.lt(${json}`,
109
+ closer: ")",
110
+ messagePrefix: ", { error: ",
111
+ messageCloser: " })",
112
+ }));
71
113
  }
72
114
  return r;
73
115
  };
@@ -78,16 +78,16 @@ function parseObject(objectSchema, refs) {
78
78
  }
79
79
  else {
80
80
  if (additionalProperties) {
81
- patternProperties += `z.record(z.union([${[
81
+ patternProperties += `z.record(z.string(), z.union([${[
82
82
  ...Object.values(parsedPatternProperties),
83
83
  additionalProperties,
84
84
  ].join(", ")}]))`;
85
85
  }
86
86
  else if (Object.keys(parsedPatternProperties).length > 1) {
87
- patternProperties += `z.record(z.union([${Object.values(parsedPatternProperties).join(", ")}]))`;
87
+ patternProperties += `z.record(z.string(), z.union([${Object.values(parsedPatternProperties).join(", ")}]))`;
88
88
  }
89
89
  else {
90
- patternProperties += `z.record(${Object.values(parsedPatternProperties)})`;
90
+ patternProperties += `z.record(z.string(), ${Object.values(parsedPatternProperties)})`;
91
91
  }
92
92
  }
93
93
  patternProperties += ".superRefine((value, ctx) => {\n";
@@ -103,9 +103,8 @@ function parseObject(objectSchema, refs) {
103
103
  }
104
104
  }
105
105
  for (const key in objectSchema.patternProperties) {
106
- const escapedPattern = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
107
106
  patternProperties +=
108
- "if (key.match(new RegExp(" + JSON.stringify(escapedPattern) + "))) {\n";
107
+ "if (key.match(new RegExp(" + JSON.stringify(key) + "))) {\n";
109
108
  if (additionalProperties) {
110
109
  patternProperties += "evaluated = true\n";
111
110
  }
@@ -115,7 +114,7 @@ function parseObject(objectSchema, refs) {
115
114
  ".safeParse(value[key])\n";
116
115
  patternProperties += "if (!result.success) {\n";
117
116
  patternProperties += `ctx.addIssue({
118
- path: [...ctx.path, key],
117
+ path: [...(ctx.path ?? []), key],
119
118
  code: 'custom',
120
119
  message: \`Invalid input: Key matching regex /\${key}/ must match schema\`,
121
120
  params: {
@@ -131,7 +130,7 @@ function parseObject(objectSchema, refs) {
131
130
  "const result = " + additionalProperties + ".safeParse(value[key])\n";
132
131
  patternProperties += "if (!result.success) {\n";
133
132
  patternProperties += `ctx.addIssue({
134
- path: [...ctx.path, key],
133
+ path: [...(ctx.path ?? []), key],
135
134
  code: 'custom',
136
135
  message: \`Invalid input: must match catchall schema\`,
137
136
  params: {
@@ -170,8 +169,8 @@ function parseObject(objectSchema, refs) {
170
169
  : patternProperties
171
170
  ? patternProperties
172
171
  : additionalProperties
173
- ? `z.record(${additionalProperties})`
174
- : `z.record(${(0, anyOrUnknown_js_1.anyOrUnknown)(refs)})`;
172
+ ? `z.record(z.string(), ${additionalProperties})`
173
+ : `z.record(z.string(), ${(0, anyOrUnknown_js_1.anyOrUnknown)(refs)})`;
175
174
  if (unevaluated === false && properties && !hasCompositionKeywords) {
176
175
  output += ".strict()";
177
176
  }
@@ -272,6 +271,30 @@ function parseObject(objectSchema, refs) {
272
271
  }`;
273
272
  })
274
273
  .join("\n ")}
274
+ })`;
275
+ }
276
+ }
277
+ // dependentRequired
278
+ if (objectSchema.dependentRequired && typeof objectSchema.dependentRequired === "object") {
279
+ const entries = Object.entries(objectSchema.dependentRequired);
280
+ if (entries.length) {
281
+ const depRequiredMessage = objectSchema.errorMessage?.dependentRequired ?? "Dependent required properties missing";
282
+ output += `.superRefine((obj, ctx) => {
283
+ ${entries
284
+ .map(([prop, deps]) => {
285
+ const arr = Array.isArray(deps) ? deps : [];
286
+ if (!arr.length)
287
+ return "";
288
+ const jsonDeps = JSON.stringify(arr);
289
+ return `if (Object.prototype.hasOwnProperty.call(obj, ${JSON.stringify(prop)})) {
290
+ const missing = ${jsonDeps}.filter((d) => !Object.prototype.hasOwnProperty.call(obj, d));
291
+ if (missing.length) {
292
+ ctx.addIssue({ code: "custom", message: ${JSON.stringify(depRequiredMessage)}, path: [], params: { missing } });
293
+ }
294
+ }`;
295
+ })
296
+ .filter(Boolean)
297
+ .join("\n ")}
275
298
  })`;
276
299
  }
277
300
  }
@@ -23,6 +23,7 @@ const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) =>
23
23
  // Ensure ref bookkeeping exists so $ref declarations and getter-based recursion work
24
24
  refs.root = refs.root ?? schema;
25
25
  refs.declarations = refs.declarations ?? new Map();
26
+ refs.dependencies = refs.dependencies ?? new Map();
26
27
  refs.inProgress = refs.inProgress ?? new Set();
27
28
  refs.refNameByPointer = refs.refNameByPointer ?? new Map();
28
29
  refs.usedNames = refs.usedNames ?? new Set();
@@ -85,6 +86,27 @@ const parseRef = (schema, refs) => {
85
86
  refs.inProgress.delete(refName);
86
87
  refs.declarations.set(refName, declaration);
87
88
  }
89
+ const current = refs.currentSchemaName;
90
+ if (current) {
91
+ const deps = refs.dependencies;
92
+ const set = deps.get(current) ?? new Set();
93
+ set.add(refName);
94
+ deps.set(current, set);
95
+ }
96
+ const currentComponent = refs.currentSchemaName
97
+ ? refs.cycleComponentByName?.get(refs.currentSchemaName)
98
+ : undefined;
99
+ const targetComponent = refs.cycleComponentByName?.get(refName);
100
+ const isSameCycle = currentComponent !== undefined &&
101
+ targetComponent !== undefined &&
102
+ currentComponent === targetComponent &&
103
+ refs.cycleRefNames?.has(refName);
104
+ // Only lazy if the ref stays inside the current strongly-connected component
105
+ // (or is currently being resolved). This avoids TDZ on true cycles while
106
+ // letting ordered, acyclic refs stay direct.
107
+ if (isSameCycle || refs.inProgress.has(refName)) {
108
+ return `z.lazy(() => ${refName})`;
109
+ }
88
110
  return refName;
89
111
  };
90
112
  const addDescribes = (schema, parsed, refs) => {
@@ -213,7 +235,7 @@ const selectParser = (schema, refs) => {
213
235
  return (0, parseMultipleType_js_1.parseMultipleType)(schema, refs);
214
236
  }
215
237
  else if (exports.its.a.primitive(schema, "string")) {
216
- return (0, parseString_js_1.parseString)(schema);
238
+ return (0, parseString_js_1.parseString)(schema, refs);
217
239
  }
218
240
  else if (exports.its.a.primitive(schema, "number") ||
219
241
  exports.its.a.primitive(schema, "integer")) {
@@ -3,75 +3,315 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.parseString = void 0;
4
4
  const withMessage_js_1 = require("../utils/withMessage.js");
5
5
  const parseSchema_js_1 = require("./parseSchema.js");
6
- const parseString = (schema) => {
7
- let r = "z.string()";
8
- r += (0, withMessage_js_1.withMessage)(schema, "format", ({ value }) => {
9
- switch (value) {
10
- case "email":
11
- return [".email(", ")"];
12
- case "ip":
13
- return [".ip(", ")"];
14
- case "ipv4":
15
- return ['.ip({ version: "v4"', ", message: ", " })"];
16
- case "ipv6":
17
- return ['.ip({ version: "v6"', ", message: ", " })"];
18
- case "uri":
19
- return [".url(", ")"];
20
- case "uuid":
21
- return [".uuid(", ")"];
22
- case "date-time":
23
- return [".datetime({ offset: true", ", message: ", " })"];
24
- case "time":
25
- return [".time(", ")"];
26
- case "date":
27
- return [".date(", ")"];
28
- case "binary":
29
- return [".base64(", ")"];
30
- case "duration":
31
- return [".duration(", ")"];
32
- }
33
- });
34
- r += (0, withMessage_js_1.withMessage)(schema, "pattern", ({ json }) => [
35
- `.regex(new RegExp(${json})`,
36
- ", ",
37
- ")",
38
- ]);
39
- r += (0, withMessage_js_1.withMessage)(schema, "minLength", ({ json }) => [
40
- `.min(${json}`,
41
- ", ",
42
- ")",
43
- ]);
44
- r += (0, withMessage_js_1.withMessage)(schema, "maxLength", ({ json }) => [
45
- `.max(${json}`,
46
- ", ",
47
- ")",
48
- ]);
6
+ const parseString = (schema, refs) => {
7
+ const formatError = schema.errorMessage?.format;
8
+ const refContext = ensureRefs(refs);
9
+ const topLevelFormatMap = {
10
+ email: "z.email",
11
+ ipv4: "z.ipv4",
12
+ ipv6: "z.ipv6",
13
+ uri: "z.url",
14
+ uuid: "z.uuid",
15
+ cuid: "z.cuid",
16
+ cuid2: "z.cuid2",
17
+ nanoid: "z.nanoid",
18
+ ulid: "z.ulid",
19
+ jwt: "z.jwt",
20
+ e164: "z.e164",
21
+ base64url: "z.base64url",
22
+ base64: "z.base64",
23
+ emoji: "z.emoji",
24
+ "idn-email": "z.email",
25
+ };
26
+ const formatFn = schema.format && topLevelFormatMap[schema.format];
27
+ const formatParam = formatError !== undefined ? `{ error: ${JSON.stringify(formatError)} }` : "";
28
+ let r = formatFn ? `${formatFn}(${formatParam})` : "z.string()";
29
+ const formatHandled = Boolean(formatFn);
30
+ let formatWasHandled = formatHandled;
31
+ if (!formatHandled) {
32
+ r += (0, withMessage_js_1.withMessage)(schema, "format", ({ value }) => {
33
+ switch (value) {
34
+ case "email":
35
+ formatWasHandled = true;
36
+ return {
37
+ opener: ".email(",
38
+ closer: ")",
39
+ messagePrefix: "{ error: ",
40
+ messageCloser: " })",
41
+ };
42
+ case "ip":
43
+ formatWasHandled = true;
44
+ return {
45
+ opener: ".ip(",
46
+ closer: ")",
47
+ messagePrefix: "{ error: ",
48
+ messageCloser: " })",
49
+ };
50
+ case "ipv4":
51
+ formatWasHandled = true;
52
+ return {
53
+ opener: '.ip({ version: "v4"',
54
+ closer: " })",
55
+ messagePrefix: ", error: ",
56
+ messageCloser: " })",
57
+ };
58
+ case "ipv6":
59
+ formatWasHandled = true;
60
+ return {
61
+ opener: '.ip({ version: "v6"',
62
+ closer: " })",
63
+ messagePrefix: ", error: ",
64
+ messageCloser: " })",
65
+ };
66
+ case "uri":
67
+ formatWasHandled = true;
68
+ return {
69
+ opener: ".url(",
70
+ closer: ")",
71
+ messagePrefix: "{ error: ",
72
+ messageCloser: " })",
73
+ };
74
+ case "uuid":
75
+ formatWasHandled = true;
76
+ return {
77
+ opener: ".uuid(",
78
+ closer: ")",
79
+ messagePrefix: "{ error: ",
80
+ messageCloser: " })",
81
+ };
82
+ case "cuid":
83
+ formatWasHandled = true;
84
+ return {
85
+ opener: ".cuid(",
86
+ closer: ")",
87
+ messagePrefix: "{ error: ",
88
+ messageCloser: " })",
89
+ };
90
+ case "cuid2":
91
+ formatWasHandled = true;
92
+ return {
93
+ opener: ".cuid2(",
94
+ closer: ")",
95
+ messagePrefix: "{ error: ",
96
+ messageCloser: " })",
97
+ };
98
+ case "nanoid":
99
+ formatWasHandled = true;
100
+ return {
101
+ opener: ".nanoid(",
102
+ closer: ")",
103
+ messagePrefix: "{ error: ",
104
+ messageCloser: " })",
105
+ };
106
+ case "ulid":
107
+ formatWasHandled = true;
108
+ return {
109
+ opener: ".ulid(",
110
+ closer: ")",
111
+ messagePrefix: "{ error: ",
112
+ messageCloser: " })",
113
+ };
114
+ case "jwt":
115
+ formatWasHandled = true;
116
+ return {
117
+ opener: ".jwt(",
118
+ closer: ")",
119
+ messagePrefix: "{ error: ",
120
+ messageCloser: " })",
121
+ };
122
+ case "e164":
123
+ formatWasHandled = true;
124
+ return {
125
+ opener: ".e164(",
126
+ closer: ")",
127
+ messagePrefix: "{ error: ",
128
+ messageCloser: " })",
129
+ };
130
+ case "base64url":
131
+ formatWasHandled = true;
132
+ return {
133
+ opener: ".base64url(",
134
+ closer: ")",
135
+ messagePrefix: "{ error: ",
136
+ messageCloser: " })",
137
+ };
138
+ case "emoji":
139
+ formatWasHandled = true;
140
+ return {
141
+ opener: ".emoji(",
142
+ closer: ")",
143
+ messagePrefix: "{ error: ",
144
+ messageCloser: " })",
145
+ };
146
+ case "date-time":
147
+ formatWasHandled = true;
148
+ return {
149
+ opener: ".datetime({ offset: true",
150
+ closer: " })",
151
+ messagePrefix: ", error: ",
152
+ messageCloser: " })",
153
+ };
154
+ case "time":
155
+ formatWasHandled = true;
156
+ return {
157
+ opener: ".time(",
158
+ closer: ")",
159
+ messagePrefix: "{ error: ",
160
+ messageCloser: " })",
161
+ };
162
+ case "date":
163
+ formatWasHandled = true;
164
+ return {
165
+ opener: ".date(",
166
+ closer: ")",
167
+ messagePrefix: "{ error: ",
168
+ messageCloser: " })",
169
+ };
170
+ case "binary":
171
+ formatWasHandled = true;
172
+ return {
173
+ opener: ".base64(",
174
+ closer: ")",
175
+ messagePrefix: "{ error: ",
176
+ messageCloser: " })",
177
+ };
178
+ case "duration":
179
+ formatWasHandled = true;
180
+ return {
181
+ opener: ".duration(",
182
+ closer: ")",
183
+ messagePrefix: "{ error: ",
184
+ messageCloser: " })",
185
+ };
186
+ case "hostname":
187
+ case "idn-hostname":
188
+ formatWasHandled = true;
189
+ return {
190
+ opener: ".refine((val) => { if (typeof val !== \"string\" || val.length === 0 || val.length > 253) return false; return val.split(\".\").every((label) => label.length > 0 && label.length <= 63 && /^[A-Za-z0-9-]+$/.test(label) && label[0] !== \"-\" && label[label.length - 1] !== \"-\"); }",
191
+ closer: ")",
192
+ messagePrefix: ", { error: ",
193
+ messageCloser: " })",
194
+ };
195
+ case "idn-email":
196
+ formatWasHandled = true;
197
+ return {
198
+ opener: ".email(",
199
+ closer: ")",
200
+ messagePrefix: "{ error: ",
201
+ messageCloser: " })",
202
+ };
203
+ case "uri-reference":
204
+ case "iri":
205
+ case "iri-reference":
206
+ formatWasHandled = true;
207
+ return {
208
+ opener: '.refine((val) => { try { new URL(val, "http://example.com"); return true; } catch { return false; } }',
209
+ closer: ")",
210
+ messagePrefix: ", { error: ",
211
+ messageCloser: " })",
212
+ };
213
+ case "json-pointer":
214
+ formatWasHandled = true;
215
+ return {
216
+ opener: ".refine((val) => typeof val === \"string\" && /^(?:\\/(?:[^/~]|~[01])*)*$/.test(val)",
217
+ closer: ")",
218
+ messagePrefix: ", { error: ",
219
+ messageCloser: " })",
220
+ };
221
+ case "relative-json-pointer":
222
+ formatWasHandled = true;
223
+ return {
224
+ opener: ".refine((val) => typeof val === \"string\" && /^(?:0|[1-9][0-9]*)(?:#|(?:\\/(?:[^/~]|~[01])*))*$/.test(val)",
225
+ closer: ")",
226
+ messagePrefix: ", { error: ",
227
+ messageCloser: " })",
228
+ };
229
+ case "uri-template":
230
+ formatWasHandled = true;
231
+ return {
232
+ opener: ".refine((val) => { if (typeof val !== \"string\") return false; const opens = (val.match(/\\{/g) || []).length; const closes = (val.match(/\\}/g) || []).length; return opens === closes; }",
233
+ closer: ")",
234
+ messagePrefix: ", { error: ",
235
+ messageCloser: " })",
236
+ };
237
+ case "regex":
238
+ formatWasHandled = true;
239
+ return {
240
+ opener: ".refine((val) => { try { new RegExp(val); return true; } catch { return false; } }",
241
+ closer: ")",
242
+ messagePrefix: ", { error: ",
243
+ messageCloser: " })",
244
+ };
245
+ }
246
+ });
247
+ }
248
+ if (schema.format && !formatWasHandled) {
249
+ refContext.onUnknownFormat?.(schema.format, refContext.path);
250
+ }
251
+ r += (0, withMessage_js_1.withMessage)(schema, "pattern", ({ json }) => ({
252
+ opener: `.regex(new RegExp(${json})`,
253
+ closer: ")",
254
+ messagePrefix: ", { error: ",
255
+ messageCloser: " })",
256
+ }));
257
+ r += (0, withMessage_js_1.withMessage)(schema, "minLength", ({ json }) => ({
258
+ opener: `.min(${json}`,
259
+ closer: ")",
260
+ messagePrefix: ", { error: ",
261
+ messageCloser: " })",
262
+ }));
263
+ r += (0, withMessage_js_1.withMessage)(schema, "maxLength", ({ json }) => ({
264
+ opener: `.max(${json}`,
265
+ closer: ")",
266
+ messagePrefix: ", { error: ",
267
+ messageCloser: " })",
268
+ }));
49
269
  r += (0, withMessage_js_1.withMessage)(schema, "contentEncoding", ({ value }) => {
50
270
  if (value === "base64") {
51
- return [".base64(", ")"];
271
+ return {
272
+ opener: ".base64(",
273
+ closer: ")",
274
+ messagePrefix: "{ error: ",
275
+ messageCloser: " })",
276
+ };
52
277
  }
53
278
  });
54
279
  const contentMediaType = (0, withMessage_js_1.withMessage)(schema, "contentMediaType", ({ value }) => {
55
280
  if (value === "application/json") {
56
- return [
57
- ".transform((str, ctx) => { try { return JSON.parse(str); } catch (err) { ctx.addIssue({ code: \"custom\", message: \"Invalid JSON\" }); }}",
58
- ", ",
59
- ")"
60
- ];
281
+ return {
282
+ opener: '.transform((str, ctx) => { try { return JSON.parse(str); } catch (err) { ctx.addIssue({ code: "custom", message: "Invalid JSON" }); }}',
283
+ closer: ")",
284
+ messagePrefix: ", { error: ",
285
+ messageCloser: " })",
286
+ };
61
287
  }
62
288
  });
63
289
  if (contentMediaType != "") {
64
290
  r += contentMediaType;
65
291
  r += (0, withMessage_js_1.withMessage)(schema, "contentSchema", ({ value }) => {
66
292
  if (value && value instanceof Object) {
67
- return [
68
- `.pipe(${(0, parseSchema_js_1.parseSchema)(value)}`,
69
- ", ",
70
- ")"
71
- ];
293
+ return {
294
+ opener: `.pipe(${(0, parseSchema_js_1.parseSchema)(value, refContext)}`,
295
+ closer: ")",
296
+ messagePrefix: ", { error: ",
297
+ messageCloser: " })",
298
+ };
72
299
  }
73
300
  });
74
301
  }
75
302
  return r;
76
303
  };
77
304
  exports.parseString = parseString;
305
+ function ensureRefs(refs) {
306
+ if (refs)
307
+ return refs;
308
+ return {
309
+ path: [],
310
+ seen: new Map(),
311
+ declarations: new Map(),
312
+ dependencies: new Map(),
313
+ inProgress: new Set(),
314
+ refNameByPointer: new Map(),
315
+ usedNames: new Set(),
316
+ };
317
+ }