@gabrielbryk/json-schema-to-zod 2.8.0 → 2.10.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 (69) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/cjs/core/analyzeSchema.js +62 -0
  3. package/dist/cjs/core/emitZod.js +141 -0
  4. package/dist/cjs/generators/generateBundle.js +117 -63
  5. package/dist/cjs/index.js +4 -0
  6. package/dist/cjs/jsonSchemaToZod.js +5 -167
  7. package/dist/cjs/parsers/parseAllOf.js +12 -6
  8. package/dist/cjs/parsers/parseBoolean.js +1 -3
  9. package/dist/cjs/parsers/parseIfThenElse.js +1 -1
  10. package/dist/cjs/parsers/parseNull.js +1 -3
  11. package/dist/cjs/parsers/parseObject.js +8 -3
  12. package/dist/cjs/parsers/parseSchema.js +130 -26
  13. package/dist/cjs/parsers/parseString.js +1 -1
  14. package/dist/cjs/utils/buildRefRegistry.js +56 -0
  15. package/dist/cjs/utils/omit.js +3 -2
  16. package/dist/cjs/utils/resolveUri.js +16 -0
  17. package/dist/esm/Types.js +1 -2
  18. package/dist/esm/cli.js +10 -12
  19. package/dist/esm/core/analyzeSchema.js +58 -0
  20. package/dist/esm/core/emitZod.js +137 -0
  21. package/dist/esm/generators/generateBundle.js +118 -68
  22. package/dist/esm/index.js +34 -46
  23. package/dist/esm/jsonSchemaToZod.js +5 -171
  24. package/dist/esm/parsers/parseAllOf.js +17 -14
  25. package/dist/esm/parsers/parseAnyOf.js +6 -10
  26. package/dist/esm/parsers/parseArray.js +11 -15
  27. package/dist/esm/parsers/parseBoolean.js +1 -7
  28. package/dist/esm/parsers/parseConst.js +1 -5
  29. package/dist/esm/parsers/parseDefault.js +3 -7
  30. package/dist/esm/parsers/parseEnum.js +1 -5
  31. package/dist/esm/parsers/parseIfThenElse.js +6 -10
  32. package/dist/esm/parsers/parseMultipleType.js +3 -7
  33. package/dist/esm/parsers/parseNot.js +4 -8
  34. package/dist/esm/parsers/parseNull.js +1 -7
  35. package/dist/esm/parsers/parseNullable.js +4 -8
  36. package/dist/esm/parsers/parseNumber.js +11 -15
  37. package/dist/esm/parsers/parseObject.js +33 -31
  38. package/dist/esm/parsers/parseOneOf.js +6 -10
  39. package/dist/esm/parsers/parseSchema.js +187 -87
  40. package/dist/esm/parsers/parseSimpleDiscriminatedOneOf.js +6 -10
  41. package/dist/esm/parsers/parseString.js +12 -16
  42. package/dist/esm/utils/anyOrUnknown.js +1 -5
  43. package/dist/esm/utils/buildRefRegistry.js +52 -0
  44. package/dist/esm/utils/cliTools.js +7 -13
  45. package/dist/esm/utils/cycles.js +3 -9
  46. package/dist/esm/utils/half.js +1 -5
  47. package/dist/esm/utils/jsdocs.js +3 -8
  48. package/dist/esm/utils/omit.js +4 -7
  49. package/dist/esm/utils/resolveUri.js +12 -0
  50. package/dist/esm/utils/withMessage.js +1 -4
  51. package/dist/esm/zodToJsonSchema.js +1 -4
  52. package/dist/types/Types.d.ts +34 -3
  53. package/dist/types/core/analyzeSchema.d.ts +24 -0
  54. package/dist/types/core/emitZod.d.ts +2 -0
  55. package/dist/types/generators/generateBundle.d.ts +5 -0
  56. package/dist/types/index.d.ts +4 -0
  57. package/dist/types/jsonSchemaToZod.d.ts +1 -1
  58. package/dist/types/parsers/parseBoolean.d.ts +1 -4
  59. package/dist/types/parsers/parseNull.d.ts +1 -4
  60. package/dist/types/parsers/parseSchema.d.ts +2 -1
  61. package/dist/types/utils/buildRefRegistry.d.ts +12 -0
  62. package/dist/types/utils/resolveUri.d.ts +1 -0
  63. package/docs/proposals/bundle-refactor.md +43 -0
  64. package/docs/proposals/ref-anchor-support.md +65 -0
  65. package/eslint.config.js +26 -0
  66. package/package.json +10 -4
  67. /package/{jest.config.js → jest.config.cjs} +0 -0
  68. /package/{postcjs.js → postcjs.cjs} +0 -0
  69. /package/{postesm.js → postesm.cjs} +0 -0
@@ -1,14 +1,11 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseObject = parseObject;
4
- const parseAnyOf_js_1 = require("./parseAnyOf.js");
5
- const parseOneOf_js_1 = require("./parseOneOf.js");
6
- const parseSchema_js_1 = require("./parseSchema.js");
7
- const parseAllOf_js_1 = require("./parseAllOf.js");
8
- const parseIfThenElse_js_1 = require("./parseIfThenElse.js");
9
- const jsdocs_js_1 = require("../utils/jsdocs.js");
10
- const anyOrUnknown_js_1 = require("../utils/anyOrUnknown.js");
11
- function parseObject(objectSchema, refs) {
1
+ import { parseAnyOf } from "./parseAnyOf.js";
2
+ import { parseOneOf } from "./parseOneOf.js";
3
+ import { its, parseSchema } from "./parseSchema.js";
4
+ import { parseAllOf } from "./parseAllOf.js";
5
+ import { parseIfThenElse } from "./parseIfThenElse.js";
6
+ import { addJsdocs } from "../utils/jsdocs.js";
7
+ import { anyOrUnknown } from "../utils/anyOrUnknown.js";
8
+ export function parseObject(objectSchema, refs) {
12
9
  let properties = undefined;
13
10
  if (objectSchema.properties) {
14
11
  if (!Object.keys(objectSchema.properties).length) {
@@ -19,7 +16,7 @@ function parseObject(objectSchema, refs) {
19
16
  properties += Object.keys(objectSchema.properties)
20
17
  .map((key) => {
21
18
  const propSchema = objectSchema.properties[key];
22
- const parsedProp = (0, parseSchema_js_1.parseSchema)(propSchema, {
19
+ const parsedProp = parseSchema(propSchema, {
23
20
  ...refs,
24
21
  path: [...refs.path, "properties", key],
25
22
  });
@@ -35,7 +32,7 @@ function parseObject(objectSchema, refs) {
35
32
  ? `get ${JSON.stringify(key)}(){ return ${valueWithOptional} }`
36
33
  : `${JSON.stringify(key)}: ${valueWithOptional}`;
37
34
  if (refs.withJsdocs && typeof propSchema === "object") {
38
- result = (0, jsdocs_js_1.addJsdocs)(propSchema, result);
35
+ result = addJsdocs(propSchema, result);
39
36
  }
40
37
  return result;
41
38
  })
@@ -44,7 +41,7 @@ function parseObject(objectSchema, refs) {
44
41
  }
45
42
  }
46
43
  const additionalProperties = objectSchema.additionalProperties !== undefined
47
- ? (0, parseSchema_js_1.parseSchema)(objectSchema.additionalProperties, {
44
+ ? parseSchema(objectSchema.additionalProperties, {
48
45
  ...refs,
49
46
  path: [...refs.path, "additionalProperties"],
50
47
  })
@@ -55,7 +52,7 @@ function parseObject(objectSchema, refs) {
55
52
  const parsedPatternProperties = Object.fromEntries(Object.entries(objectSchema.patternProperties).map(([key, value]) => {
56
53
  return [
57
54
  key,
58
- (0, parseSchema_js_1.parseSchema)(value, {
55
+ parseSchema(value, {
59
56
  ...refs,
60
57
  path: [...refs.path, "patternProperties", key],
61
58
  }),
@@ -154,7 +151,7 @@ function parseObject(objectSchema, refs) {
154
151
  // Check if there will be an .and() call that adds properties from oneOf/anyOf/allOf/if-then-else
155
152
  // In that case, we should NOT use .strict() because it will reject the additional keys
156
153
  // before the union gets a chance to validate them.
157
- const hasCompositionKeywords = parseSchema_js_1.its.an.anyOf(objectSchema) || parseSchema_js_1.its.a.oneOf(objectSchema) || parseSchema_js_1.its.an.allOf(objectSchema) || parseSchema_js_1.its.a.conditional(objectSchema);
154
+ const hasCompositionKeywords = its.an.anyOf(objectSchema) || its.a.oneOf(objectSchema) || its.an.allOf(objectSchema) || its.a.conditional(objectSchema);
158
155
  let output = properties
159
156
  ? patternProperties
160
157
  ? properties + patternProperties
@@ -170,12 +167,12 @@ function parseObject(objectSchema, refs) {
170
167
  ? patternProperties
171
168
  : additionalProperties
172
169
  ? `z.record(z.string(), ${additionalProperties})`
173
- : `z.record(z.string(), ${(0, anyOrUnknown_js_1.anyOrUnknown)(refs)})`;
170
+ : `z.record(z.string(), ${anyOrUnknown(refs)})`;
174
171
  if (unevaluated === false && properties && !hasCompositionKeywords) {
175
172
  output += ".strict()";
176
173
  }
177
174
  else if (unevaluated && typeof unevaluated !== 'boolean') {
178
- const unevaluatedSchema = (0, parseSchema_js_1.parseSchema)(unevaluated, {
175
+ const unevaluatedSchema = parseSchema(unevaluated, {
179
176
  ...refs,
180
177
  path: [...refs.path, "unevaluatedProperties"],
181
178
  });
@@ -196,30 +193,33 @@ function parseObject(objectSchema, refs) {
196
193
  }
197
194
  })`;
198
195
  }
199
- if (parseSchema_js_1.its.an.anyOf(objectSchema)) {
200
- output += `.and(${(0, parseAnyOf_js_1.parseAnyOf)({
196
+ if (its.an.anyOf(objectSchema)) {
197
+ output += `.and(${parseAnyOf({
201
198
  ...objectSchema,
202
199
  anyOf: objectSchema.anyOf.map((x) => typeof x === "object" &&
200
+ x !== null &&
203
201
  !x.type &&
204
202
  (x.properties || x.additionalProperties || x.patternProperties)
205
203
  ? { ...x, type: "object" }
206
204
  : x),
207
205
  }, refs)})`;
208
206
  }
209
- if (parseSchema_js_1.its.a.oneOf(objectSchema)) {
210
- output += `.and(${(0, parseOneOf_js_1.parseOneOf)({
207
+ if (its.a.oneOf(objectSchema)) {
208
+ output += `.and(${parseOneOf({
211
209
  ...objectSchema,
212
210
  oneOf: objectSchema.oneOf.map((x) => typeof x === "object" &&
211
+ x !== null &&
213
212
  !x.type &&
214
213
  (x.properties || x.additionalProperties || x.patternProperties)
215
214
  ? { ...x, type: "object" }
216
215
  : x),
217
216
  }, refs)})`;
218
217
  }
219
- if (parseSchema_js_1.its.an.allOf(objectSchema)) {
220
- output += `.and(${(0, parseAllOf_js_1.parseAllOf)({
218
+ if (its.an.allOf(objectSchema)) {
219
+ output += `.and(${parseAllOf({
221
220
  ...objectSchema,
222
221
  allOf: objectSchema.allOf.map((x) => typeof x === "object" &&
222
+ x !== null &&
223
223
  !x.type &&
224
224
  (x.properties || x.additionalProperties || x.patternProperties)
225
225
  ? { ...x, type: "object" }
@@ -227,17 +227,18 @@ function parseObject(objectSchema, refs) {
227
227
  }, refs)})`;
228
228
  }
229
229
  // Handle if/then/else conditionals on object schemas
230
- if (parseSchema_js_1.its.a.conditional(objectSchema)) {
231
- output += `.and(${(0, parseIfThenElse_js_1.parseIfThenElse)(objectSchema, refs)})`;
230
+ if (its.a.conditional(objectSchema)) {
231
+ output += `.and(${parseIfThenElse(objectSchema, refs)})`;
232
232
  }
233
233
  // propertyNames
234
234
  if (objectSchema.propertyNames) {
235
235
  const normalizedPropNames = typeof objectSchema.propertyNames === "object" &&
236
+ objectSchema.propertyNames !== null &&
236
237
  !objectSchema.propertyNames.type &&
237
238
  objectSchema.propertyNames.pattern
238
239
  ? { ...objectSchema.propertyNames, type: "string" }
239
240
  : objectSchema.propertyNames;
240
- const propNameSchema = (0, parseSchema_js_1.parseSchema)(normalizedPropNames, {
241
+ const propNameSchema = parseSchema(normalizedPropNames, {
241
242
  ...refs,
242
243
  path: [...refs.path, "propertyNames"],
243
244
  });
@@ -261,12 +262,12 @@ function parseObject(objectSchema, refs) {
261
262
  if (entries.length) {
262
263
  output += `.superRefine((obj, ctx) => {
263
264
  ${entries
264
- .map(([key, schema], idx) => {
265
- const parsed = (0, parseSchema_js_1.parseSchema)(schema, { ...refs, path: [...refs.path, "dependentSchemas", key] });
265
+ .map(([key, schema]) => {
266
+ const parsed = parseSchema(schema, { ...refs, path: [...refs.path, "dependentSchemas", key] });
266
267
  return `if (Object.prototype.hasOwnProperty.call(obj, ${JSON.stringify(key)})) {
267
268
  const result = ${parsed}.safeParse(obj);
268
269
  if (!result.success) {
269
- ctx.addIssue({ code: "custom", message: "Dependent schema failed", path: [], params: { issues: result.error.issues } });
270
+ ctx.addIssue({ code: "custom", message: ${objectSchema.errorMessage?.dependentSchemas ?? JSON.stringify("Dependent schema failed")}, path: [], params: { issues: result.error.issues } });
270
271
  }
271
272
  }`;
272
273
  })
@@ -278,7 +279,8 @@ function parseObject(objectSchema, refs) {
278
279
  if (objectSchema.dependentRequired && typeof objectSchema.dependentRequired === "object") {
279
280
  const entries = Object.entries(objectSchema.dependentRequired);
280
281
  if (entries.length) {
281
- const depRequiredMessage = objectSchema.errorMessage?.dependentRequired ?? "Dependent required properties missing";
282
+ const depRequiredMessage = objectSchema.errorMessage?.dependentRequired ??
283
+ "Dependent required properties missing";
282
284
  output += `.superRefine((obj, ctx) => {
283
285
  ${entries
284
286
  .map(([prop, deps]) => {
@@ -1,20 +1,17 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseOneOf = void 0;
4
- const parseSchema_js_1 = require("./parseSchema.js");
5
- const anyOrUnknown_js_1 = require("../utils/anyOrUnknown.js");
6
- const parseOneOf = (schema, refs) => {
1
+ import { parseSchema } from "./parseSchema.js";
2
+ import { anyOrUnknown } from "../utils/anyOrUnknown.js";
3
+ export const parseOneOf = (schema, refs) => {
7
4
  if (!schema.oneOf.length) {
8
- return (0, anyOrUnknown_js_1.anyOrUnknown)(refs);
5
+ return anyOrUnknown(refs);
9
6
  }
10
7
  if (schema.oneOf.length === 1) {
11
- return (0, parseSchema_js_1.parseSchema)(schema.oneOf[0], {
8
+ return parseSchema(schema.oneOf[0], {
12
9
  ...refs,
13
10
  path: [...refs.path, "oneOf", 0],
14
11
  });
15
12
  }
16
13
  // Generate parsed schemas for each oneOf option
17
- const parsedSchemas = schema.oneOf.map((s, i) => (0, parseSchema_js_1.parseSchema)(s, {
14
+ const parsedSchemas = schema.oneOf.map((s, i) => parseSchema(s, {
18
15
  ...refs,
19
16
  path: [...refs.path, "oneOf", i],
20
17
  }));
@@ -50,4 +47,3 @@ const parseOneOf = (schema, refs) => {
50
47
  // Default: use simple z.union() (at least one must match)
51
48
  return `z.union([${parsedSchemas.join(", ")}])`;
52
49
  };
53
- exports.parseOneOf = parseOneOf;
@@ -1,36 +1,46 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.its = exports.parseSchema = void 0;
4
- const parseAnyOf_js_1 = require("./parseAnyOf.js");
5
- const parseBoolean_js_1 = require("./parseBoolean.js");
6
- const parseDefault_js_1 = require("./parseDefault.js");
7
- const parseMultipleType_js_1 = require("./parseMultipleType.js");
8
- const parseNot_js_1 = require("./parseNot.js");
9
- const parseNull_js_1 = require("./parseNull.js");
10
- const parseAllOf_js_1 = require("./parseAllOf.js");
11
- const parseArray_js_1 = require("./parseArray.js");
12
- const parseConst_js_1 = require("./parseConst.js");
13
- const parseEnum_js_1 = require("./parseEnum.js");
14
- const parseIfThenElse_js_1 = require("./parseIfThenElse.js");
15
- const parseNumber_js_1 = require("./parseNumber.js");
16
- const parseObject_js_1 = require("./parseObject.js");
17
- const parseString_js_1 = require("./parseString.js");
18
- const parseOneOf_js_1 = require("./parseOneOf.js");
19
- const parseSimpleDiscriminatedOneOf_js_1 = require("./parseSimpleDiscriminatedOneOf.js");
20
- const parseNullable_js_1 = require("./parseNullable.js");
21
- const anyOrUnknown_js_1 = require("../utils/anyOrUnknown.js");
22
- const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) => {
1
+ import { parseAnyOf } from "./parseAnyOf.js";
2
+ import { parseBoolean } from "./parseBoolean.js";
3
+ import { parseDefault } from "./parseDefault.js";
4
+ import { parseMultipleType } from "./parseMultipleType.js";
5
+ import { parseNot } from "./parseNot.js";
6
+ import { parseNull } from "./parseNull.js";
7
+ import { parseAllOf } from "./parseAllOf.js";
8
+ import { parseArray } from "./parseArray.js";
9
+ import { parseConst } from "./parseConst.js";
10
+ import { parseEnum } from "./parseEnum.js";
11
+ import { parseIfThenElse } from "./parseIfThenElse.js";
12
+ import { parseNumber } from "./parseNumber.js";
13
+ import { parseObject } from "./parseObject.js";
14
+ import { parseString } from "./parseString.js";
15
+ import { parseOneOf } from "./parseOneOf.js";
16
+ import { parseSimpleDiscriminatedOneOf } from "./parseSimpleDiscriminatedOneOf.js";
17
+ import { parseNullable } from "./parseNullable.js";
18
+ import { anyOrUnknown } from "../utils/anyOrUnknown.js";
19
+ import { resolveUri } from "../utils/resolveUri.js";
20
+ import { buildRefRegistry } from "../utils/buildRefRegistry.js";
21
+ export const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) => {
23
22
  // Ensure ref bookkeeping exists so $ref declarations and getter-based recursion work
24
23
  refs.root = refs.root ?? schema;
24
+ refs.rootBaseUri = refs.rootBaseUri ?? "root:///";
25
25
  refs.declarations = refs.declarations ?? new Map();
26
26
  refs.dependencies = refs.dependencies ?? new Map();
27
27
  refs.inProgress = refs.inProgress ?? new Set();
28
28
  refs.refNameByPointer = refs.refNameByPointer ?? new Map();
29
29
  refs.usedNames = refs.usedNames ?? new Set();
30
30
  if (typeof schema !== "object")
31
- return schema ? (0, anyOrUnknown_js_1.anyOrUnknown)(refs) : "z.never()";
31
+ return schema ? anyOrUnknown(refs) : "z.never()";
32
+ const parentBase = refs.currentBaseUri ?? refs.rootBaseUri ?? "root:///";
33
+ const baseUri = typeof schema.$id === "string" ? resolveUri(parentBase, schema.$id) : parentBase;
34
+ const dynamicAnchors = Array.isArray(refs.dynamicAnchors) ? [...refs.dynamicAnchors] : [];
35
+ if (typeof schema.$dynamicAnchor === "string") {
36
+ dynamicAnchors.push({
37
+ name: schema.$dynamicAnchor,
38
+ uri: baseUri,
39
+ path: refs.path,
40
+ });
41
+ }
32
42
  if (refs.parserOverride) {
33
- const custom = refs.parserOverride(schema, refs);
43
+ const custom = refs.parserOverride(schema, { ...refs, currentBaseUri: baseUri, dynamicAnchors });
34
44
  if (typeof custom === "string") {
35
45
  return custom;
36
46
  }
@@ -41,7 +51,7 @@ const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) =>
41
51
  return seen.r;
42
52
  }
43
53
  if (refs.depth === undefined || seen.n >= refs.depth) {
44
- return (0, anyOrUnknown_js_1.anyOrUnknown)(refs);
54
+ return anyOrUnknown(refs);
45
55
  }
46
56
  seen.n += 1;
47
57
  }
@@ -49,15 +59,15 @@ const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) =>
49
59
  seen = { r: undefined, n: 0 };
50
60
  refs.seen.set(schema, seen);
51
61
  }
52
- if (exports.its.a.ref(schema)) {
53
- const parsedRef = parseRef(schema, refs);
62
+ if (its.a.ref(schema)) {
63
+ const parsedRef = parseRef(schema, { ...refs, currentBaseUri: baseUri, dynamicAnchors });
54
64
  seen.r = parsedRef;
55
65
  return parsedRef;
56
66
  }
57
- let parsed = selectParser(schema, refs);
67
+ let parsed = selectParser(schema, { ...refs, currentBaseUri: baseUri, dynamicAnchors });
58
68
  if (!blockMeta) {
59
69
  if (!refs.withoutDescribes) {
60
- parsed = addDescribes(schema, parsed, refs);
70
+ parsed = addDescribes(schema, parsed, { ...refs, currentBaseUri: baseUri, dynamicAnchors });
61
71
  }
62
72
  if (!refs.withoutDefaults) {
63
73
  parsed = addDefaults(schema, parsed);
@@ -67,19 +77,24 @@ const parseSchema = (schema, refs = { seen: new Map(), path: [] }, blockMeta) =>
67
77
  seen.r = parsed;
68
78
  return parsed;
69
79
  };
70
- exports.parseSchema = parseSchema;
71
80
  const parseRef = (schema, refs) => {
72
- const resolved = resolveRef(refs.root, schema.$ref);
81
+ const refValue = schema.$dynamicRef ?? schema.$ref;
82
+ if (typeof refValue !== "string") {
83
+ return anyOrUnknown(refs);
84
+ }
85
+ const resolved = resolveRef(schema, refValue, refs);
73
86
  if (!resolved) {
74
- return (0, anyOrUnknown_js_1.anyOrUnknown)(refs);
87
+ refs.onUnresolvedRef?.(refValue, refs.path);
88
+ return anyOrUnknown(refs);
75
89
  }
76
- const { schema: target, path } = resolved;
77
- const refName = getOrCreateRefName(schema.$ref, path, refs);
90
+ const { schema: target, path, pointerKey } = resolved;
91
+ const refName = getOrCreateRefName(pointerKey, path, refs);
78
92
  if (!refs.declarations.has(refName) && !refs.inProgress.has(refName)) {
79
93
  refs.inProgress.add(refName);
80
- const declaration = (0, exports.parseSchema)(target, {
94
+ const declaration = parseSchema(target, {
81
95
  ...refs,
82
96
  path,
97
+ currentBaseUri: resolved.baseUri,
83
98
  currentSchemaName: refName,
84
99
  root: refs.root,
85
100
  });
@@ -132,23 +147,96 @@ const addDescribes = (schema, parsed, refs) => {
132
147
  }
133
148
  return parsed;
134
149
  };
135
- const resolveRef = (root, ref) => {
136
- if (!root || !ref.startsWith("#/"))
137
- return undefined;
138
- const rawSegments = ref
139
- .slice(2)
140
- .split("/")
141
- .filter((segment) => segment.length > 0)
142
- .map(decodePointerSegment);
143
- let current = root;
144
- for (const segment of rawSegments) {
145
- if (typeof current !== "object" || current === null)
146
- return undefined;
147
- current = current[segment];
148
- }
149
- return { schema: current, path: rawSegments };
150
+ const resolveRef = (schemaNode, ref, refs) => {
151
+ const base = refs.currentBaseUri ?? refs.rootBaseUri ?? "root:///";
152
+ // Handle dynamicRef lookup via dynamicAnchors stack
153
+ const isDynamic = typeof schemaNode.$dynamicRef === "string";
154
+ if (isDynamic && refs.dynamicAnchors && ref.startsWith("#")) {
155
+ const name = ref.slice(1);
156
+ for (let i = refs.dynamicAnchors.length - 1; i >= 0; i -= 1) {
157
+ const entry = refs.dynamicAnchors[i];
158
+ if (entry.name === name) {
159
+ const key = `${entry.uri}#${name}`;
160
+ const target = refs.refRegistry?.get(key);
161
+ if (target) {
162
+ return { schema: target.schema, path: target.path, baseUri: target.baseUri, pointerKey: key };
163
+ }
164
+ }
165
+ }
166
+ }
167
+ // Resolve URI against base
168
+ const resolvedUri = resolveUri(base, ref);
169
+ const [uriBase, fragment] = resolvedUri.split("#");
170
+ const key = fragment ? `${uriBase}#${fragment}` : uriBase;
171
+ let regEntry = refs.refRegistry?.get(key);
172
+ if (regEntry) {
173
+ return { schema: regEntry.schema, path: regEntry.path, baseUri: regEntry.baseUri, pointerKey: key };
174
+ }
175
+ // Legacy recursive ref: treat as dynamic to __recursive__
176
+ if (schemaNode.$recursiveRef) {
177
+ const recursiveKey = `${base}#__recursive__`;
178
+ regEntry = refs.refRegistry?.get(recursiveKey);
179
+ if (regEntry) {
180
+ return {
181
+ schema: regEntry.schema,
182
+ path: regEntry.path,
183
+ baseUri: regEntry.baseUri,
184
+ pointerKey: recursiveKey,
185
+ };
186
+ }
187
+ }
188
+ // External resolver hook
189
+ const extBase = uriBaseFromRef(resolvedUri);
190
+ if (refs.resolveExternalRef && extBase && !isLocalBase(extBase, refs.rootBaseUri ?? "")) {
191
+ const loaded = refs.resolveExternalRef(extBase);
192
+ if (loaded) {
193
+ // If async resolver is used synchronously here, it will be ignored; keep simple sync for now
194
+ const maybePromise = loaded;
195
+ const schema = typeof maybePromise.then === "function"
196
+ ? undefined
197
+ : loaded;
198
+ if (schema) {
199
+ const { registry } = buildRefRegistry(schema, extBase);
200
+ registry.forEach((entry, k) => refs.refRegistry?.set(k, entry));
201
+ regEntry = refs.refRegistry?.get(key);
202
+ if (regEntry) {
203
+ return {
204
+ schema: regEntry.schema,
205
+ path: regEntry.path,
206
+ baseUri: regEntry.baseUri,
207
+ pointerKey: key,
208
+ };
209
+ }
210
+ }
211
+ }
212
+ }
213
+ // Backward compatibility: JSON Pointer into root
214
+ if (refs.root && ref.startsWith("#/")) {
215
+ const rawSegments = ref
216
+ .slice(2)
217
+ .split("/")
218
+ .filter((segment) => segment.length > 0)
219
+ .map(decodePointerSegment);
220
+ let current = refs.root;
221
+ for (const segment of rawSegments) {
222
+ if (typeof current !== "object" || current === null)
223
+ return undefined;
224
+ current = current[segment];
225
+ }
226
+ return { schema: current, path: rawSegments, baseUri: base, pointerKey: ref };
227
+ }
228
+ return undefined;
150
229
  };
151
230
  const decodePointerSegment = (segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~");
231
+ const uriBaseFromRef = (resolvedUri) => {
232
+ const hashIdx = resolvedUri.indexOf("#");
233
+ return hashIdx === -1 ? resolvedUri : resolvedUri.slice(0, hashIdx);
234
+ };
235
+ const isLocalBase = (base, rootBase) => {
236
+ if (!rootBase)
237
+ return false;
238
+ return base === rootBase;
239
+ };
152
240
  const getOrCreateRefName = (pointer, path, refs) => {
153
241
  if (refs.refNameByPointer?.has(pointer)) {
154
242
  return refs.refNameByPointer.get(pointer);
@@ -159,12 +247,24 @@ const getOrCreateRefName = (pointer, path, refs) => {
159
247
  return preferred;
160
248
  };
161
249
  const buildNameFromPath = (path, used) => {
162
- const filtered = path.filter((segment) => segment !== "$defs" && segment !== "definitions" && segment !== "properties");
250
+ const filtered = path
251
+ .map((segment, idx) => {
252
+ if (idx === 0 && (segment === "$defs" || segment === "definitions")) {
253
+ return undefined; // root-level defs prefix is redundant for naming
254
+ }
255
+ if (segment === "properties")
256
+ return undefined; // skip noisy properties segment
257
+ if (segment === "$defs" || segment === "definitions")
258
+ return "Defs";
259
+ return segment;
260
+ })
261
+ .filter((segment) => segment !== undefined);
163
262
  const base = filtered.length
164
263
  ? filtered
165
264
  .map((segment) => typeof segment === "number"
166
265
  ? `Ref${segment}`
167
266
  : segment
267
+ .toString()
168
268
  .replace(/[^a-zA-Z0-9_$]/g, " ")
169
269
  .split(" ")
170
270
  .filter(Boolean)
@@ -201,60 +301,60 @@ const addAnnotations = (schema, parsed) => {
201
301
  return parsed;
202
302
  };
203
303
  const selectParser = (schema, refs) => {
204
- if (exports.its.a.nullable(schema)) {
205
- return (0, parseNullable_js_1.parseNullable)(schema, refs);
304
+ if (its.a.nullable(schema)) {
305
+ return parseNullable(schema, refs);
206
306
  }
207
- else if (exports.its.an.object(schema)) {
208
- return (0, parseObject_js_1.parseObject)(schema, refs);
307
+ else if (its.an.object(schema)) {
308
+ return parseObject(schema, refs);
209
309
  }
210
- else if (exports.its.an.array(schema)) {
211
- return (0, parseArray_js_1.parseArray)(schema, refs);
310
+ else if (its.an.array(schema)) {
311
+ return parseArray(schema, refs);
212
312
  }
213
- else if (exports.its.an.anyOf(schema)) {
214
- return (0, parseAnyOf_js_1.parseAnyOf)(schema, refs);
313
+ else if (its.an.anyOf(schema)) {
314
+ return parseAnyOf(schema, refs);
215
315
  }
216
- else if (exports.its.an.allOf(schema)) {
217
- return (0, parseAllOf_js_1.parseAllOf)(schema, refs);
316
+ else if (its.an.allOf(schema)) {
317
+ return parseAllOf(schema, refs);
218
318
  }
219
- else if (exports.its.a.simpleDiscriminatedOneOf(schema)) {
220
- return (0, parseSimpleDiscriminatedOneOf_js_1.parseSimpleDiscriminatedOneOf)(schema, refs);
319
+ else if (its.a.simpleDiscriminatedOneOf(schema)) {
320
+ return parseSimpleDiscriminatedOneOf(schema, refs);
221
321
  }
222
- else if (exports.its.a.oneOf(schema)) {
223
- return (0, parseOneOf_js_1.parseOneOf)(schema, refs);
322
+ else if (its.a.oneOf(schema)) {
323
+ return parseOneOf(schema, refs);
224
324
  }
225
- else if (exports.its.a.not(schema)) {
226
- return (0, parseNot_js_1.parseNot)(schema, refs);
325
+ else if (its.a.not(schema)) {
326
+ return parseNot(schema, refs);
227
327
  }
228
- else if (exports.its.an.enum(schema)) {
229
- return (0, parseEnum_js_1.parseEnum)(schema); //<-- needs to come before primitives
328
+ else if (its.an.enum(schema)) {
329
+ return parseEnum(schema); //<-- needs to come before primitives
230
330
  }
231
- else if (exports.its.a.const(schema)) {
232
- return (0, parseConst_js_1.parseConst)(schema);
331
+ else if (its.a.const(schema)) {
332
+ return parseConst(schema);
233
333
  }
234
- else if (exports.its.a.multipleType(schema)) {
235
- return (0, parseMultipleType_js_1.parseMultipleType)(schema, refs);
334
+ else if (its.a.multipleType(schema)) {
335
+ return parseMultipleType(schema, refs);
236
336
  }
237
- else if (exports.its.a.primitive(schema, "string")) {
238
- return (0, parseString_js_1.parseString)(schema, refs);
337
+ else if (its.a.primitive(schema, "string")) {
338
+ return parseString(schema, refs);
239
339
  }
240
- else if (exports.its.a.primitive(schema, "number") ||
241
- exports.its.a.primitive(schema, "integer")) {
242
- return (0, parseNumber_js_1.parseNumber)(schema);
340
+ else if (its.a.primitive(schema, "number") ||
341
+ its.a.primitive(schema, "integer")) {
342
+ return parseNumber(schema);
243
343
  }
244
- else if (exports.its.a.primitive(schema, "boolean")) {
245
- return (0, parseBoolean_js_1.parseBoolean)(schema);
344
+ else if (its.a.primitive(schema, "boolean")) {
345
+ return parseBoolean();
246
346
  }
247
- else if (exports.its.a.primitive(schema, "null")) {
248
- return (0, parseNull_js_1.parseNull)(schema);
347
+ else if (its.a.primitive(schema, "null")) {
348
+ return parseNull();
249
349
  }
250
- else if (exports.its.a.conditional(schema)) {
251
- return (0, parseIfThenElse_js_1.parseIfThenElse)(schema, refs);
350
+ else if (its.a.conditional(schema)) {
351
+ return parseIfThenElse(schema, refs);
252
352
  }
253
353
  else {
254
- return (0, parseDefault_js_1.parseDefault)(schema, refs);
354
+ return parseDefault(schema, refs);
255
355
  }
256
356
  };
257
- exports.its = {
357
+ export const its = {
258
358
  an: {
259
359
  object: (x) => x.type === "object",
260
360
  array: (x) => x.type === "array",
@@ -266,7 +366,7 @@ exports.its = {
266
366
  nullable: (x) => x.nullable === true,
267
367
  multipleType: (x) => Array.isArray(x.type),
268
368
  not: (x) => x.not !== undefined,
269
- ref: (x) => typeof x.$ref === "string",
369
+ ref: (x) => typeof x.$ref === "string" || typeof x.$dynamicRef === "string",
270
370
  const: (x) => x.const !== undefined,
271
371
  primitive: (x, p) => x.type === p,
272
372
  conditional: (x) => Boolean("if" in x && x.if && "then" in x && "else" in x && x.then && x.else),
@@ -1,21 +1,17 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseSimpleDiscriminatedOneOf = void 0;
4
- const parseSchema_js_1 = require("./parseSchema.js");
5
- const anyOrUnknown_js_1 = require("../utils/anyOrUnknown.js");
6
- const parseSimpleDiscriminatedOneOf = (schema, refs) => {
1
+ import { parseSchema } from "./parseSchema.js";
2
+ import { anyOrUnknown } from "../utils/anyOrUnknown.js";
3
+ export const parseSimpleDiscriminatedOneOf = (schema, refs) => {
7
4
  const discriminator = schema.discriminator.propertyName;
8
- const options = schema.oneOf.map((option, i) => (0, parseSchema_js_1.parseSchema)(option, {
5
+ const options = schema.oneOf.map((option, i) => parseSchema(option, {
9
6
  ...refs,
10
7
  path: [...refs.path, "oneOf", i],
11
8
  }));
12
9
  return schema.oneOf.length
13
10
  ? schema.oneOf.length === 1
14
- ? (0, parseSchema_js_1.parseSchema)(schema.oneOf[0], {
11
+ ? parseSchema(schema.oneOf[0], {
15
12
  ...refs,
16
13
  path: [...refs.path, "oneOf", 0],
17
14
  })
18
15
  : `z.discriminatedUnion("${discriminator}", [${options.join(", ")}])`
19
- : (0, anyOrUnknown_js_1.anyOrUnknown)(refs);
16
+ : anyOrUnknown(refs);
20
17
  };
21
- exports.parseSimpleDiscriminatedOneOf = parseSimpleDiscriminatedOneOf;