@sdk-it/core 0.43.0 → 0.45.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.
@@ -0,0 +1,23 @@
1
+ declare global {
2
+ interface JSON {
3
+ /**
4
+ * TC39 "JSON.parse source access" proposal, shipped in Node 21+ and
5
+ * evergreen browsers. TypeScript 6 does not ship lib types for it yet.
6
+ */
7
+ rawJSON(text: string): {
8
+ rawJSON: string;
9
+ };
10
+ isRawJSON(value: unknown): value is {
11
+ rawJSON: string;
12
+ };
13
+ }
14
+ }
15
+ /**
16
+ * Spec objects produced by the analyzers carry real bigint values for int64
17
+ * defaults and bounds. JSON.stringify throws on bigint; this replacer writes
18
+ * them as bare JSON numbers instead — lossless on disk (JSON's grammar allows
19
+ * arbitrary-precision numbers) and valid against `type: integer` schemas.
20
+ */
21
+ export declare function specJsonReplacer(_key: string, value: unknown): unknown;
22
+ export declare function stringifySpec(spec: unknown, space?: string | number): string;
23
+ //# sourceMappingURL=spec-json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec-json.d.ts","sourceRoot":"","sources":["../../src/lib/spec-json.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,IAAI;QACZ;;;WAGG;QACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3C,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI;YAAE,OAAO,EAAE,MAAM,CAAA;SAAE,CAAC;KACzD;CACF;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAEtE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAE5E"}
@@ -0,0 +1,11 @@
1
+ function specJsonReplacer(_key, value) {
2
+ return typeof value === "bigint" ? JSON.rawJSON(value.toString()) : value;
3
+ }
4
+ function stringifySpec(spec, space) {
5
+ return JSON.stringify(spec, specJsonReplacer, space);
6
+ }
7
+ export {
8
+ specJsonReplacer,
9
+ stringifySpec
10
+ };
11
+ //# sourceMappingURL=spec-json.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/lib/spec-json.ts"],
4
+ "sourcesContent": ["declare global {\n interface JSON {\n /**\n * TC39 \"JSON.parse source access\" proposal, shipped in Node 21+ and\n * evergreen browsers. TypeScript 6 does not ship lib types for it yet.\n */\n rawJSON(text: string): { rawJSON: string };\n isRawJSON(value: unknown): value is { rawJSON: string };\n }\n}\n\n/**\n * Spec objects produced by the analyzers carry real bigint values for int64\n * defaults and bounds. JSON.stringify throws on bigint; this replacer writes\n * them as bare JSON numbers instead \u2014 lossless on disk (JSON's grammar allows\n * arbitrary-precision numbers) and valid against `type: integer` schemas.\n */\nexport function specJsonReplacer(_key: string, value: unknown): unknown {\n return typeof value === 'bigint' ? JSON.rawJSON(value.toString()) : value;\n}\n\nexport function stringifySpec(spec: unknown, space?: string | number): string {\n return JSON.stringify(spec, specJsonReplacer, space);\n}\n"],
5
+ "mappings": "AAiBO,SAAS,iBAAiB,MAAc,OAAyB;AACtE,SAAO,OAAO,UAAU,WAAW,KAAK,QAAQ,MAAM,SAAS,CAAC,IAAI;AACtE;AAEO,SAAS,cAAc,MAAe,OAAiC;AAC5E,SAAO,KAAK,UAAU,MAAM,kBAAkB,KAAK;AACrD;",
6
+ "names": []
7
+ }
@@ -1,6 +1,7 @@
1
1
  export type InjectImport = {
2
2
  import: string;
3
3
  from: string;
4
+ property?: string;
4
5
  };
5
6
  export declare function evalZod(schema: string, imports?: InjectImport[]): Promise<any>;
6
7
  //# sourceMappingURL=zod-jsonschema.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"zod-jsonschema.d.ts","sourceRoot":"","sources":["../../src/lib/zod-jsonschema.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAUF,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAY,EAAO,gBAgRzE"}
1
+ {"version":3,"file":"zod-jsonschema.d.ts","sourceRoot":"","sources":["../../src/lib/zod-jsonschema.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAUF,wBAAsB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAY,EAAO,gBA0mBzE"}
@@ -7,57 +7,81 @@ async function evalZod(schema, imports = []) {
7
7
  `const filename = "${import.meta.url}";`,
8
8
  `const require = createRequire(filename);`,
9
9
  `const z = require("zod");`,
10
- ...imports.map((imp) => `const ${imp.import} = require('${imp.from}');`),
11
- `const {zodToJsonSchema, ignoreOverride} = require('zod-to-json-schema');`,
10
+ // zod 4 removed ZodString.ip/.cidr; restore them for analyzed user source
11
+ // that still uses the v3 spellings.
12
+ `const stringProto = Object.getPrototypeOf(z.string());
13
+ const installedShims = [];
14
+ function withReceiverChecks(receiver, format) {
15
+ return (receiver._zod.def.checks ?? []).length
16
+ ? z.intersection(receiver, format)
17
+ : format;
18
+ }
19
+ if (!stringProto.ip) {
20
+ installedShims.push('ip');
21
+ stringProto.ip = function (options) {
22
+ if (options?.version === 'v4') return withReceiverChecks(this, z.ipv4());
23
+ if (options?.version === 'v6') return withReceiverChecks(this, z.ipv6());
24
+ return withReceiverChecks(this, z.union([z.ipv4(), z.ipv6()]));
25
+ };
26
+ }
27
+ if (!stringProto.cidr) {
28
+ installedShims.push('cidr');
29
+ stringProto.cidr = function (options) {
30
+ if (options?.version === 'v4') return withReceiverChecks(this, z.cidrv4());
31
+ if (options?.version === 'v6') return withReceiverChecks(this, z.cidrv6());
32
+ return withReceiverChecks(this, z.union([z.cidrv4(), z.cidrv6()]));
33
+ };
34
+ }`,
35
+ ...imports.map(
36
+ (imp) => `const ${imp.import} = require(${JSON.stringify(imp.from)})${imp.property ? `[${JSON.stringify(imp.property)}]` : ""};`
37
+ ),
12
38
  `let optional = false;`,
39
+ `const WRAPPER_TYPES = new Set([
40
+ 'optional',
41
+ 'nullable',
42
+ 'default',
43
+ 'prefault',
44
+ 'catch',
45
+ 'readonly',
46
+ 'nonoptional',
47
+ ]);`,
13
48
  `function unwrapSchemaDef(def) {
14
49
  while (def) {
15
- if (
16
- def.typeName === 'ZodOptional' ||
17
- def.typeName === 'ZodDefault' ||
18
- def.typeName === 'ZodNullable' ||
19
- def.typeName === 'ZodCatch' ||
20
- def.typeName === 'ZodBranded' ||
21
- def.typeName === 'ZodReadonly'
22
- ) {
23
- def = def.innerType?._def;
24
- continue;
25
- }
26
- if (def.typeName === 'ZodEffects') {
27
- def = def.schema?._def;
50
+ if (WRAPPER_TYPES.has(def.type)) {
51
+ def = def.innerType?._zod?.def;
28
52
  continue;
29
53
  }
30
- if (def.typeName === 'ZodPipeline') {
31
- def = def.in?._def;
54
+ if (def.type === 'pipe') {
55
+ def = def.in?._zod?.def;
32
56
  continue;
33
57
  }
34
58
  return def;
35
59
  }
36
60
  return def;
37
61
  }`,
38
- `function matchesDefType(schema, typeName) {
62
+ `function matchesDefType(schema, defType) {
39
63
  if (!schema || typeof schema !== 'object') return false;
40
- if (typeName === 'ZodNumber') {
64
+ if (defType === 'number') {
41
65
  return schema.type === 'number' || schema.type === 'integer';
42
66
  }
43
- if (typeName === 'ZodBigInt') {
67
+ if (defType === 'bigint') {
44
68
  return schema.type === 'integer';
45
69
  }
46
- if (typeName === 'ZodString') {
70
+ if (defType === 'string') {
47
71
  return schema.type === 'string';
48
72
  }
49
- if (typeName === 'ZodBoolean') {
73
+ if (defType === 'boolean') {
50
74
  return schema.type === 'boolean';
51
75
  }
52
- if (typeName === 'ZodDate') {
76
+ if (defType === 'date') {
53
77
  return schema.type === 'string' && schema.format === 'date-time';
54
78
  }
55
79
  return typeof schema.type === 'string';
56
80
  }`,
57
- `function applyZodType(schema, zodType, typeName) {
81
+ `function applyZodType(schema, zodType, defType) {
58
82
  if (!schema || typeof schema !== 'object') return false;
59
83
  if (schema['x-zod-type']) return true;
60
- if (matchesDefType(schema, typeName)) {
84
+ if (matchesDefType(schema, defType)) {
61
85
  schema['x-zod-type'] = zodType;
62
86
  return true;
63
87
  }
@@ -66,17 +90,94 @@ async function evalZod(schema, imports = []) {
66
90
  const candidates = schema[key].filter(
67
91
  (candidate) => candidate && candidate.type !== 'null',
68
92
  );
69
- if (candidates.length === 1 && applyZodType(candidates[0], zodType, typeName)) {
93
+ if (candidates.length === 1 && applyZodType(candidates[0], zodType, defType)) {
70
94
  return true;
71
95
  }
72
96
  for (const candidate of candidates) {
73
- if (applyZodType(candidate, zodType, typeName)) {
97
+ if (applyZodType(candidate, zodType, defType)) {
74
98
  return true;
75
99
  }
76
100
  }
77
101
  }
78
102
  return false;
79
103
  }`,
104
+ `const BIGINT_SENTINEL = '__sdkit_bigint__';`,
105
+ // zod v4 JSON-round-trips default values before the override callback
106
+ // runs, which throws on bigint. Mask them as sentinel strings up front
107
+ // and restore after conversion.
108
+ `function maskBigIntDefaults(schema, seen = new Set()) {
109
+ if (!schema || !schema._zod || seen.has(schema)) return;
110
+ seen.add(schema);
111
+ const def = schema._zod.def;
112
+ if (
113
+ (def.type === 'default' || def.type === 'prefault') &&
114
+ typeof def.defaultValue === 'bigint'
115
+ ) {
116
+ Object.defineProperty(def, 'defaultValue', {
117
+ value: BIGINT_SENTINEL + def.defaultValue.toString(),
118
+ configurable: true,
119
+ });
120
+ }
121
+ for (const key of ['innerType', 'in', 'out', 'element', 'valueType', 'keyType', 'left', 'right', 'catchall', 'rest']) {
122
+ if (def[key]) maskBigIntDefaults(def[key], seen);
123
+ }
124
+ if (typeof def.getter === 'function') {
125
+ const inner = def.getter();
126
+ maskBigIntDefaults(inner, seen);
127
+ Object.defineProperty(def, 'getter', {
128
+ value: () => inner,
129
+ configurable: true,
130
+ });
131
+ }
132
+ if (def.shape) {
133
+ for (const value of Object.values(def.shape)) maskBigIntDefaults(value, seen);
134
+ }
135
+ if (Array.isArray(def.options)) {
136
+ for (const option of def.options) maskBigIntDefaults(option, seen);
137
+ }
138
+ if (Array.isArray(def.items)) {
139
+ for (const item of def.items) maskBigIntDefaults(item, seen);
140
+ }
141
+ }`,
142
+ // int64 is a plain number in sdk-it, so masked bigint defaults unmask to
143
+ // JS numbers — the spec stays plain JSON with no bigint values to trip up
144
+ // JSON.stringify.
145
+ `function unmaskBigIntDefaults(schema) {
146
+ if (!schema || typeof schema !== 'object') return;
147
+ for (const [key, value] of Object.entries(schema)) {
148
+ if (typeof value === 'string' && value.startsWith(BIGINT_SENTINEL)) {
149
+ schema[key] = Number(value.slice(BIGINT_SENTINEL.length));
150
+ } else if (Array.isArray(value)) {
151
+ value.forEach(unmaskBigIntDefaults);
152
+ } else if (value && typeof value === 'object') {
153
+ unmaskBigIntDefaults(value);
154
+ }
155
+ }
156
+ }`,
157
+ `function hasExplicitRegexCheck(def) {
158
+ return (def.checks ?? []).some(
159
+ (check) => check._zod?.def?.format === 'regex',
160
+ );
161
+ }`,
162
+ `const STANDARD_STRING_FORMATS = new Set([
163
+ 'email',
164
+ 'uri',
165
+ 'url',
166
+ 'uuid',
167
+ 'guid',
168
+ 'date-time',
169
+ 'date',
170
+ 'time',
171
+ 'duration',
172
+ 'ipv4',
173
+ 'ipv6',
174
+ 'hostname',
175
+ 'binary',
176
+ ]);`,
177
+ // Nonstandard formats that sdk-it's own converters map to semantic zod
178
+ // validators. Keep both keys: format for our converters, pattern for
179
+ // third-party validators that do not recognize the format.
180
+ `const MAPPED_NONSTANDARD_FORMATS = new Set(['cidrv4', 'cidrv6']);`,
80
181
  `function normalizeDefaultValue(value) {
81
182
  return value instanceof Date ? value.toISOString() : value;
82
183
  }`,
@@ -133,6 +234,13 @@ async function evalZod(schema, imports = []) {
133
234
  `function mergePrimitiveSchemas(schemas) {
134
235
  const merged = {};
135
236
  for (const schema of schemas) {
237
+ if (
238
+ schema &&
239
+ typeof schema === 'object' &&
240
+ Object.keys(schema).length === 0
241
+ ) {
242
+ continue;
243
+ }
136
244
  if (!isMergeablePrimitiveSchema(schema)) {
137
245
  return null;
138
246
  }
@@ -185,7 +293,7 @@ async function evalZod(schema, imports = []) {
185
293
  }
186
294
  return merged;
187
295
  }`,
188
- `function normalizeSchema(schema) {
296
+ `function normalizeSchema(schema, isIntersectionMember = false) {
189
297
  if (!schema || typeof schema !== 'object') {
190
298
  return schema;
191
299
  }
@@ -215,7 +323,55 @@ async function evalZod(schema, imports = []) {
215
323
 
216
324
  for (const key of ['anyOf', 'oneOf', 'allOf']) {
217
325
  if (!Array.isArray(schema[key])) continue;
218
- schema[key] = schema[key].map((candidate) => normalizeSchema(candidate));
326
+ schema[key] = schema[key].map((candidate) =>
327
+ normalizeSchema(candidate, key === 'allOf'),
328
+ );
329
+ }
330
+
331
+ if (Array.isArray(schema.oneOf) && !schema.anyOf) {
332
+ schema.anyOf = schema.oneOf;
333
+ delete schema.oneOf;
334
+ }
335
+
336
+ if (
337
+ Array.isArray(schema.anyOf) &&
338
+ schema.anyOf.every(
339
+ (member) =>
340
+ member &&
341
+ typeof member === 'object' &&
342
+ Object.keys(member).length === 1 &&
343
+ typeof member.type === 'string',
344
+ )
345
+ ) {
346
+ const { anyOf, ...rest } = schema;
347
+ schema = { ...rest, type: anyOf.map((member) => member.type) };
348
+ }
349
+
350
+ if (
351
+ schema.type === 'object' &&
352
+ schema.properties &&
353
+ schema.additionalProperties === undefined &&
354
+ !isIntersectionMember
355
+ ) {
356
+ schema.additionalProperties = false;
357
+ }
358
+
359
+ if (
360
+ schema.type === 'object' &&
361
+ schema.propertyNames &&
362
+ JSON.stringify(schema.propertyNames) === '{"type":"string"}'
363
+ ) {
364
+ delete schema.propertyNames;
365
+ }
366
+
367
+ if (
368
+ typeof schema.$ref === 'string' &&
369
+ !schema.$ref.startsWith('#/components/schemas')
370
+ ) {
371
+ schema.$ref =
372
+ schema.$ref === '#'
373
+ ? '#/components/schemas'
374
+ : schema.$ref.replace(/^#\\//, '#/components/schemas/');
219
375
  }
220
376
 
221
377
  if (Array.isArray(schema.allOf)) {
@@ -224,43 +380,230 @@ async function evalZod(schema, imports = []) {
224
380
  const { allOf, ...rest } = schema;
225
381
  return { ...rest, ...merged };
226
382
  }
383
+ if (schema.allOf.length === 1 && Object.keys(schema).length === 1) {
384
+ return schema.allOf[0];
385
+ }
227
386
  }
228
387
 
229
388
  return schema;
230
389
  }`,
390
+ `function escapePointerSegment(segment) {
391
+ return String(segment).replaceAll('~', '~0').replaceAll('/', '~1');
392
+ }`,
393
+ `function componentPointer(path) {
394
+ return ['#', 'components', 'schemas', ...path.map(escapePointerSegment)].join('/');
395
+ }`,
396
+ // Nested toJsonSchema passes (set/map values, pipe outputs) return
397
+ // self-contained draft-7 documents whose '#' and '#/definitions/*' refs
398
+ // are relative to the embedded document root, not the outer schema.
399
+ // Resolve them to outer-root structural pointers before the outer
400
+ // definitions/normalize passes rewrite them into dangling refs.
401
+ `function resolveEmbeddedDocs(root) {
402
+ function process(node, path, docRoot) {
403
+ if (!node || typeof node !== 'object') return;
404
+ if (Array.isArray(node)) {
405
+ node.forEach((item, index) => process(item, [...path, index], docRoot));
406
+ return;
407
+ }
408
+ if (node['x-sdkit-embedded-root']) {
409
+ delete node['x-sdkit-embedded-root'];
410
+ const defs = node.definitions ?? Object.create(null);
411
+ delete node.definitions;
412
+ docRoot = { path, defs, firstSite: Object.create(null) };
413
+ }
414
+ if (docRoot && typeof node.$ref === 'string') {
415
+ if (node.$ref === '#') {
416
+ node.$ref = componentPointer(docRoot.path);
417
+ return;
418
+ }
419
+ const match = node.$ref.match(/^#\\/definitions\\/(.+)$/);
420
+ if (match) {
421
+ const name = match[1];
422
+ if (name in docRoot.firstSite) {
423
+ node.$ref = docRoot.firstSite[name];
424
+ return;
425
+ }
426
+ docRoot.firstSite[name] = componentPointer(path);
427
+ delete node.$ref;
428
+ Object.assign(node, docRoot.defs[name]);
429
+ }
430
+ }
431
+ for (const [key, value] of Object.entries(node)) {
432
+ process(value, [...path, key], docRoot);
433
+ }
434
+ }
435
+ process(root, [], null);
436
+ return root;
437
+ }`,
438
+ // zod v4 breaks non-root cycles with a draft-7 definitions block plus
439
+ // #/definitions/* refs, which OpenAPI consumers cannot resolve. Inline
440
+ // each definition at its first use site and point the remaining
441
+ // (recursive) refs structurally at that site, mirroring the v3
442
+ // $refStrategy: 'root' output.
443
+ `function inlineDefinitions(root) {
444
+ const defs = root.definitions;
445
+ if (!defs || typeof defs !== 'object') return root;
446
+ delete root.definitions;
447
+ const firstSite = Object.create(null);
448
+ function walk(node, path) {
449
+ if (!node || typeof node !== 'object') return;
450
+ if (Array.isArray(node)) {
451
+ node.forEach((item, index) => walk(item, [...path, index]));
452
+ return;
453
+ }
454
+ const match =
455
+ typeof node.$ref === 'string' &&
456
+ node.$ref.match(/^#\\/definitions\\/(.+)$/);
457
+ if (match) {
458
+ const name = match[1];
459
+ if (name in firstSite) {
460
+ node.$ref = firstSite[name];
461
+ return;
462
+ }
463
+ firstSite[name] = componentPointer(path);
464
+ delete node.$ref;
465
+ Object.assign(node, defs[name]);
466
+ }
467
+ for (const [key, value] of Object.entries(node)) {
468
+ walk(value, [...path, key]);
469
+ }
470
+ }
471
+ walk(root, []);
472
+ return root;
473
+ }`,
474
+ // Wraps nested conversions: when the embedded document carries refs or a
475
+ // definitions block, tag its root so resolveEmbeddedDocs can rebase them.
476
+ `function convertEmbedded(schema) {
477
+ const { $schema: _ignored, ...doc } = toJsonSchema(schema);
478
+ if (doc.definitions || JSON.stringify(doc).includes('"$ref"')) {
479
+ doc['x-sdkit-embedded-root'] = true;
480
+ }
481
+ return doc;
482
+ }`,
231
483
  `function toJsonSchema(schema) {
232
- return zodToJsonSchema(schema, {
233
- $refStrategy: 'root',
234
- basePath: ['#', 'components', 'schemas'],
235
- target: 'jsonSchema7',
236
- base64Strategy: 'format:binary',
237
- effectStrategy: 'input',
238
- override: (def) => {
239
- if (def.typeName === 'ZodOptional') {
240
- optional = true;
241
- const { $schema, ...result } = toJsonSchema(def.innerType);
242
- if (def.description) { result.description = def.description; }
243
- return result;
484
+ return z.toJSONSchema(schema, {
485
+ target: 'draft-7',
486
+ io: 'input',
487
+ unrepresentable: 'any',
488
+ override(ctx) {
489
+ const def = ctx.zodSchema._zod.def;
490
+ const json = ctx.jsonSchema;
491
+ if (def.type === 'optional') {
492
+ optional = true;
493
+ }
494
+ if (def.type === 'catch') {
495
+ delete json.default;
244
496
  }
245
- if (def.typeName === 'ZodDate') {
246
- return {
247
- type: 'string',
248
- format: 'date-time',
249
- 'x-zod-type': def.coerce ? 'coerce-date' : 'date',
250
- ...(def.description ? { description: def.description } : {}),
497
+ if (def.type === 'date') {
498
+ json.type = 'string';
499
+ json.format = 'date-time';
500
+ json['x-zod-type'] = def.coerce ? 'coerce-date' : 'date';
501
+ }
502
+ if (def.type === 'bigint') {
503
+ json.type = 'integer';
504
+ // z.bigint() carries no format; treat it as int64.
505
+ json.format = ctx.zodSchema._zod.bag.format ?? 'int64';
506
+ if (def.coerce) {
507
+ json['x-zod-type'] = 'coerce-bigint';
508
+ }
509
+ }
510
+ if (def.type === 'set') {
511
+ json.type = 'array';
512
+ json.uniqueItems = true;
513
+ json.items = convertEmbedded(def.valueType);
514
+ }
515
+ if (def.type === 'map') {
516
+ json.type = 'array';
517
+ json.items = {
518
+ type: 'array',
519
+ items: [convertEmbedded(def.keyType), convertEmbedded(def.valueType)],
520
+ minItems: 2,
521
+ maxItems: 2,
251
522
  };
252
523
  }
253
- return ignoreOverride;
524
+ if (def.type === 'string' && json.format === 'base64') {
525
+ json.format = 'binary';
526
+ delete json.pattern;
527
+ delete json.contentEncoding;
528
+ }
529
+ if (
530
+ def.type === 'string' &&
531
+ json.format &&
532
+ json.pattern &&
533
+ !hasExplicitRegexCheck(def)
534
+ ) {
535
+ if (STANDARD_STRING_FORMATS.has(json.format)) {
536
+ delete json.pattern;
537
+ } else if (!MAPPED_NONSTANDARD_FORMATS.has(json.format)) {
538
+ delete json.format;
539
+ }
540
+ }
541
+ if (def.type === 'number') {
542
+ const explicitBound = (kind, value) =>
543
+ (def.checks ?? []).some(
544
+ (check) =>
545
+ check._zod?.def?.check === kind &&
546
+ Number(check._zod.def.value) === value,
547
+ );
548
+ if (
549
+ json.maximum === Number.MAX_SAFE_INTEGER &&
550
+ !explicitBound('less_than', Number.MAX_SAFE_INTEGER)
551
+ ) {
552
+ delete json.maximum;
553
+ }
554
+ if (
555
+ json.minimum === Number.MIN_SAFE_INTEGER &&
556
+ !explicitBound('greater_than', Number.MIN_SAFE_INTEGER)
557
+ ) {
558
+ delete json.minimum;
559
+ }
560
+ }
561
+ if (def.type === 'pipe') {
562
+ const outJson = convertEmbedded(def.out);
563
+ const outKeys = Object.keys(outJson).filter(
564
+ (key) => key !== 'x-sdkit-embedded-root',
565
+ );
566
+ if (outKeys.length > 0) {
567
+ const inJson = { ...json };
568
+ for (const key of Object.keys(json)) delete json[key];
569
+ json.allOf = [inJson, outJson];
570
+ }
571
+ }
572
+ if (def.type === 'tuple' && Array.isArray(json.items)) {
573
+ if (json.minItems === undefined) {
574
+ json.minItems = json.items.length;
575
+ }
576
+ if (
577
+ json.additionalItems === undefined &&
578
+ json.maxItems === undefined
579
+ ) {
580
+ json.maxItems = json.items.length;
581
+ }
582
+ }
254
583
  },
255
584
  });
256
585
  }`,
257
- `const zodSchema = ${removeUnsupportedMethods(schema)};`,
258
- `const { $schema, ...rawResult } = toJsonSchema(zodSchema);
259
- const result = normalizeSchema(rawResult);`,
260
- `const innerDef = unwrapSchemaDef(zodSchema._def);
586
+ `let zodSchema;
587
+ let rawResult;
588
+ try {
589
+ zodSchema = ${removeUnsupportedMethods(schema)};
590
+ maskBigIntDefaults(zodSchema);
591
+ const { $schema, ...converted } = toJsonSchema(zodSchema);
592
+ rawResult = converted;
593
+ } finally {
594
+ // The v3 shims exist only while the analyzed source evaluates and
595
+ // converts (lazy getters can call them mid-conversion); remove them so
596
+ // the shared zod instance is not left with v3 API surface process-wide.
597
+ for (const method of installedShims) delete stringProto[method];
598
+ }
599
+ unmaskBigIntDefaults(rawResult);
600
+ const result = normalizeSchema(
601
+ inlineDefinitions(resolveEmbeddedDocs(rawResult)),
602
+ );`,
603
+ `const innerDef = unwrapSchemaDef(zodSchema._zod.def);
261
604
  if (innerDef?.coerce && !result['x-zod-type']) {
262
- const zodType = 'coerce-' + innerDef.typeName.replace('Zod', '').toLowerCase();
263
- if (!applyZodType(result, zodType, innerDef.typeName)) {
605
+ const zodType = 'coerce-' + innerDef.type;
606
+ if (!applyZodType(result, zodType, innerDef.type)) {
264
607
  result['x-zod-type'] = zodType;
265
608
  }
266
609
  }`,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/zod-jsonschema.ts"],
4
- "sourcesContent": ["export type InjectImport = {\n import: string;\n from: string;\n};\n\nfunction removeUnsupportedMethods(schema: string) {\n return schema\n .replaceAll('.instanceof(File)', '.string().base64()')\n .replaceAll('.instanceof(Blob)', '.string().base64()')\n .replaceAll('.custom<File>()', '.string().base64()')\n .replaceAll('.custom<Blob>()', '.string().base64()');\n}\n\nexport async function evalZod(schema: string, imports: InjectImport[] = []) {\n // https://github.com/nodejs/node/issues/51956\n const lines = [\n `import { createRequire } from \"node:module\";`,\n `const filename = \"${import.meta.url}\";`,\n `const require = createRequire(filename);`,\n `const z = require(\"zod\");`,\n ...imports.map((imp) => `const ${imp.import} = require('${imp.from}');`),\n `const {zodToJsonSchema, ignoreOverride} = require('zod-to-json-schema');`,\n `let optional = false;`,\n `function unwrapSchemaDef(def) {\n while (def) {\n if (\n def.typeName === 'ZodOptional' ||\n def.typeName === 'ZodDefault' ||\n def.typeName === 'ZodNullable' ||\n def.typeName === 'ZodCatch' ||\n def.typeName === 'ZodBranded' ||\n def.typeName === 'ZodReadonly'\n ) {\n def = def.innerType?._def;\n continue;\n }\n if (def.typeName === 'ZodEffects') {\n def = def.schema?._def;\n continue;\n }\n if (def.typeName === 'ZodPipeline') {\n def = def.in?._def;\n continue;\n }\n return def;\n }\n return def;\n }`,\n `function matchesDefType(schema, typeName) {\n if (!schema || typeof schema !== 'object') return false;\n if (typeName === 'ZodNumber') {\n return schema.type === 'number' || schema.type === 'integer';\n }\n if (typeName === 'ZodBigInt') {\n return schema.type === 'integer';\n }\n if (typeName === 'ZodString') {\n return schema.type === 'string';\n }\n if (typeName === 'ZodBoolean') {\n return schema.type === 'boolean';\n }\n if (typeName === 'ZodDate') {\n return schema.type === 'string' && schema.format === 'date-time';\n }\n return typeof schema.type === 'string';\n }`,\n `function applyZodType(schema, zodType, typeName) {\n if (!schema || typeof schema !== 'object') return false;\n if (schema['x-zod-type']) return true;\n if (matchesDefType(schema, typeName)) {\n schema['x-zod-type'] = zodType;\n return true;\n }\n for (const key of ['anyOf', 'oneOf', 'allOf']) {\n if (!Array.isArray(schema[key])) continue;\n const candidates = schema[key].filter(\n (candidate) => candidate && candidate.type !== 'null',\n );\n if (candidates.length === 1 && applyZodType(candidates[0], zodType, typeName)) {\n return true;\n }\n for (const candidate of candidates) {\n if (applyZodType(candidate, zodType, typeName)) {\n return true;\n }\n }\n }\n return false;\n }`,\n `function normalizeDefaultValue(value) {\n return value instanceof Date ? value.toISOString() : value;\n }`,\n `function mergeComparableValue(target, key, value) {\n if (value === undefined) return true;\n const normalized = normalizeDefaultValue(value);\n if (target[key] === undefined) {\n target[key] = normalized;\n return true;\n }\n return Object.is(target[key], normalized);\n }`,\n `function mergeLowerBound(target, key, value) {\n if (value === undefined) return true;\n if (typeof value !== 'number') return false;\n if (target[key] === undefined || value > target[key]) {\n target[key] = value;\n }\n return true;\n }`,\n `function mergeUpperBound(target, key, value) {\n if (value === undefined) return true;\n if (typeof value !== 'number') return false;\n if (target[key] === undefined || value < target[key]) {\n target[key] = value;\n }\n return true;\n }`,\n `function mergeEnumValues(target, values) {\n if (!Array.isArray(values)) return false;\n if (!Array.isArray(target.enum)) {\n target.enum = [...values];\n return true;\n }\n target.enum = target.enum.filter((candidate) =>\n values.some((value) => Object.is(candidate, value)),\n );\n return target.enum.length > 0;\n }`,\n `function isMergeablePrimitiveSchema(schema) {\n return (\n schema &&\n typeof schema === 'object' &&\n !schema.$ref &&\n !schema.anyOf &&\n !schema.oneOf &&\n !schema.allOf &&\n (schema.type === 'string' ||\n schema.type === 'boolean' ||\n schema.type === 'number' ||\n schema.type === 'integer')\n );\n }`,\n `function mergePrimitiveSchemas(schemas) {\n const merged = {};\n for (const schema of schemas) {\n if (!isMergeablePrimitiveSchema(schema)) {\n return null;\n }\n if (merged.type === undefined) {\n merged.type = schema.type;\n } else if (merged.type !== schema.type) {\n const numericPair =\n (merged.type === 'number' && schema.type === 'integer') ||\n (merged.type === 'integer' && schema.type === 'number');\n if (!numericPair) {\n return null;\n }\n merged.type = 'integer';\n }\n\n for (const [key, value] of Object.entries(schema)) {\n if (key === 'type') {\n continue;\n }\n switch (key) {\n case 'minimum':\n case 'exclusiveMinimum':\n case 'minLength':\n case 'minItems':\n case 'minProperties':\n if (!mergeLowerBound(merged, key, value)) {\n return null;\n }\n break;\n case 'maximum':\n case 'exclusiveMaximum':\n case 'maxLength':\n case 'maxItems':\n case 'maxProperties':\n if (!mergeUpperBound(merged, key, value)) {\n return null;\n }\n break;\n case 'enum':\n if (!mergeEnumValues(merged, value)) {\n return null;\n }\n break;\n default:\n if (!mergeComparableValue(merged, key, value)) {\n return null;\n }\n }\n }\n }\n return merged;\n }`,\n `function normalizeSchema(schema) {\n if (!schema || typeof schema !== 'object') {\n return schema;\n }\n\n if (schema.default !== undefined) {\n schema.default = normalizeDefaultValue(schema.default);\n }\n\n if (Array.isArray(schema.items)) {\n schema.items = schema.items.map((item) => normalizeSchema(item));\n } else if (schema.items && typeof schema.items === 'object') {\n schema.items = normalizeSchema(schema.items);\n }\n\n if (schema.properties && typeof schema.properties === 'object') {\n for (const [key, value] of Object.entries(schema.properties)) {\n schema.properties[key] = normalizeSchema(value);\n }\n }\n\n if (\n schema.additionalProperties &&\n typeof schema.additionalProperties === 'object'\n ) {\n schema.additionalProperties = normalizeSchema(schema.additionalProperties);\n }\n\n for (const key of ['anyOf', 'oneOf', 'allOf']) {\n if (!Array.isArray(schema[key])) continue;\n schema[key] = schema[key].map((candidate) => normalizeSchema(candidate));\n }\n\n if (Array.isArray(schema.allOf)) {\n const merged = mergePrimitiveSchemas(schema.allOf);\n if (merged) {\n const { allOf, ...rest } = schema;\n return { ...rest, ...merged };\n }\n }\n\n return schema;\n }`,\n `function toJsonSchema(schema) {\n return zodToJsonSchema(schema, {\n $refStrategy: 'root',\n basePath: ['#', 'components', 'schemas'],\n target: 'jsonSchema7',\n base64Strategy: 'format:binary',\n effectStrategy: 'input',\n override: (def) => {\n if (def.typeName === 'ZodOptional') {\n\t\t\t\t\t\toptional = true;\n\t\t\t\t\t\tconst { $schema, ...result } = toJsonSchema(def.innerType);\n if (def.description) { result.description = def.description; }\n return result;\n }\n if (def.typeName === 'ZodDate') {\n return {\n type: 'string',\n format: 'date-time',\n 'x-zod-type': def.coerce ? 'coerce-date' : 'date',\n ...(def.description ? { description: def.description } : {}),\n };\n }\n return ignoreOverride;\n },\n });\n }`,\n `const zodSchema = ${removeUnsupportedMethods(schema)};`,\n `const { $schema, ...rawResult } = toJsonSchema(zodSchema);\n const result = normalizeSchema(rawResult);`,\n `const innerDef = unwrapSchemaDef(zodSchema._def);\n if (innerDef?.coerce && !result['x-zod-type']) {\n const zodType = 'coerce-' + innerDef.typeName.replace('Zod', '').toLowerCase();\n if (!applyZodType(result, zodType, innerDef.typeName)) {\n result['x-zod-type'] = zodType;\n }\n }`,\n `export default {schema: result, optional}`,\n ];\n\n const base64 = Buffer.from(lines.join('\\n')).toString('base64');\n return import(\n /* @vite-ignore */\n `data:text/javascript;base64,${base64}`\n ).then((mod) => mod.default);\n}\n"],
5
- "mappings": "AAKA,SAAS,yBAAyB,QAAgB;AAChD,SAAO,OACJ,WAAW,qBAAqB,oBAAoB,EACpD,WAAW,qBAAqB,oBAAoB,EACpD,WAAW,mBAAmB,oBAAoB,EAClD,WAAW,mBAAmB,oBAAoB;AACvD;AAEA,eAAsB,QAAQ,QAAgB,UAA0B,CAAC,GAAG;AAE1E,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA,qBAAqB,YAAY,GAAG;AAAA,IACpC;AAAA,IACA;AAAA,IACA,GAAG,QAAQ,IAAI,CAAC,QAAQ,SAAS,IAAI,MAAM,eAAe,IAAI,IAAI,KAAK;AAAA,IACvE;AAAA,IACA;AAAA,IACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAyBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAmBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAuBA;AAAA;AAAA;AAAA,IAGA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAcA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAuDA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA2CA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IA0BA,qBAAqB,yBAAyB,MAAM,CAAC;AAAA,IACrD;AAAA;AAAA,IAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA;AAAA,EACF;AAEA,QAAM,SAAS,OAAO,KAAK,MAAM,KAAK,IAAI,CAAC,EAAE,SAAS,QAAQ;AAC9D,SAAO;AAAA;AAAA,IAEL,+BAA+B,MAAM;AAAA,IACrC,KAAK,CAAC,QAAQ,IAAI,OAAO;AAC7B;",
4
+ "sourcesContent": ["export type InjectImport = {\n import: string;\n from: string;\n property?: string;\n};\n\nfunction removeUnsupportedMethods(schema: string) {\n return schema\n .replaceAll('.instanceof(File)', '.string().base64()')\n .replaceAll('.instanceof(Blob)', '.string().base64()')\n .replaceAll('.custom<File>()', '.string().base64()')\n .replaceAll('.custom<Blob>()', '.string().base64()');\n}\n\nexport async function evalZod(schema: string, imports: InjectImport[] = []) {\n // https://github.com/nodejs/node/issues/51956\n const lines = [\n `import { createRequire } from \"node:module\";`,\n `const filename = \"${import.meta.url}\";`,\n `const require = createRequire(filename);`,\n `const z = require(\"zod\");`,\n // zod 4 removed ZodString.ip/.cidr; restore them for analyzed user source\n // that still uses the v3 spellings.\n `const stringProto = Object.getPrototypeOf(z.string());\n const installedShims = [];\n function withReceiverChecks(receiver, format) {\n return (receiver._zod.def.checks ?? []).length\n ? z.intersection(receiver, format)\n : format;\n }\n if (!stringProto.ip) {\n installedShims.push('ip');\n stringProto.ip = function (options) {\n if (options?.version === 'v4') return withReceiverChecks(this, z.ipv4());\n if (options?.version === 'v6') return withReceiverChecks(this, z.ipv6());\n return withReceiverChecks(this, z.union([z.ipv4(), z.ipv6()]));\n };\n }\n if (!stringProto.cidr) {\n installedShims.push('cidr');\n stringProto.cidr = function (options) {\n if (options?.version === 'v4') return withReceiverChecks(this, z.cidrv4());\n if (options?.version === 'v6') return withReceiverChecks(this, z.cidrv6());\n return withReceiverChecks(this, z.union([z.cidrv4(), z.cidrv6()]));\n };\n }`,\n ...imports.map(\n (imp) =>\n `const ${imp.import} = require(${JSON.stringify(imp.from)})${\n imp.property ? `[${JSON.stringify(imp.property)}]` : ''\n };`,\n ),\n `let optional = false;`,\n `const WRAPPER_TYPES = new Set([\n 'optional',\n 'nullable',\n 'default',\n 'prefault',\n 'catch',\n 'readonly',\n 'nonoptional',\n ]);`,\n `function unwrapSchemaDef(def) {\n while (def) {\n if (WRAPPER_TYPES.has(def.type)) {\n def = def.innerType?._zod?.def;\n continue;\n }\n if (def.type === 'pipe') {\n def = def.in?._zod?.def;\n continue;\n }\n return def;\n }\n return def;\n }`,\n `function matchesDefType(schema, defType) {\n if (!schema || typeof schema !== 'object') return false;\n if (defType === 'number') {\n return schema.type === 'number' || schema.type === 'integer';\n }\n if (defType === 'bigint') {\n return schema.type === 'integer';\n }\n if (defType === 'string') {\n return schema.type === 'string';\n }\n if (defType === 'boolean') {\n return schema.type === 'boolean';\n }\n if (defType === 'date') {\n return schema.type === 'string' && schema.format === 'date-time';\n }\n return typeof schema.type === 'string';\n }`,\n `function applyZodType(schema, zodType, defType) {\n if (!schema || typeof schema !== 'object') return false;\n if (schema['x-zod-type']) return true;\n if (matchesDefType(schema, defType)) {\n schema['x-zod-type'] = zodType;\n return true;\n }\n for (const key of ['anyOf', 'oneOf', 'allOf']) {\n if (!Array.isArray(schema[key])) continue;\n const candidates = schema[key].filter(\n (candidate) => candidate && candidate.type !== 'null',\n );\n if (candidates.length === 1 && applyZodType(candidates[0], zodType, defType)) {\n return true;\n }\n for (const candidate of candidates) {\n if (applyZodType(candidate, zodType, defType)) {\n return true;\n }\n }\n }\n return false;\n }`,\n `const BIGINT_SENTINEL = '__sdkit_bigint__';`,\n // zod v4 JSON-round-trips default values before the override callback\n // runs, which throws on bigint. Mask them as sentinel strings up front\n // and restore after conversion.\n `function maskBigIntDefaults(schema, seen = new Set()) {\n if (!schema || !schema._zod || seen.has(schema)) return;\n seen.add(schema);\n const def = schema._zod.def;\n if (\n (def.type === 'default' || def.type === 'prefault') &&\n typeof def.defaultValue === 'bigint'\n ) {\n Object.defineProperty(def, 'defaultValue', {\n value: BIGINT_SENTINEL + def.defaultValue.toString(),\n configurable: true,\n });\n }\n for (const key of ['innerType', 'in', 'out', 'element', 'valueType', 'keyType', 'left', 'right', 'catchall', 'rest']) {\n if (def[key]) maskBigIntDefaults(def[key], seen);\n }\n if (typeof def.getter === 'function') {\n const inner = def.getter();\n maskBigIntDefaults(inner, seen);\n Object.defineProperty(def, 'getter', {\n value: () => inner,\n configurable: true,\n });\n }\n if (def.shape) {\n for (const value of Object.values(def.shape)) maskBigIntDefaults(value, seen);\n }\n if (Array.isArray(def.options)) {\n for (const option of def.options) maskBigIntDefaults(option, seen);\n }\n if (Array.isArray(def.items)) {\n for (const item of def.items) maskBigIntDefaults(item, seen);\n }\n }`,\n // int64 is a plain number in sdk-it, so masked bigint defaults unmask to\n // JS numbers \u2014 the spec stays plain JSON with no bigint values to trip up\n // JSON.stringify.\n `function unmaskBigIntDefaults(schema) {\n if (!schema || typeof schema !== 'object') return;\n for (const [key, value] of Object.entries(schema)) {\n if (typeof value === 'string' && value.startsWith(BIGINT_SENTINEL)) {\n schema[key] = Number(value.slice(BIGINT_SENTINEL.length));\n } else if (Array.isArray(value)) {\n value.forEach(unmaskBigIntDefaults);\n } else if (value && typeof value === 'object') {\n unmaskBigIntDefaults(value);\n }\n }\n }`,\n `function hasExplicitRegexCheck(def) {\n return (def.checks ?? []).some(\n (check) => check._zod?.def?.format === 'regex',\n );\n }`,\n `const STANDARD_STRING_FORMATS = new Set([\n 'email',\n 'uri',\n 'url',\n 'uuid',\n 'guid',\n 'date-time',\n 'date',\n 'time',\n 'duration',\n 'ipv4',\n 'ipv6',\n 'hostname',\n 'binary',\n ]);`,\n // Nonstandard formats that sdk-it's own converters map to semantic zod\n // validators. Keep both keys: format for our converters, pattern for\n // third-party validators that do not recognize the format.\n `const MAPPED_NONSTANDARD_FORMATS = new Set(['cidrv4', 'cidrv6']);`,\n `function normalizeDefaultValue(value) {\n return value instanceof Date ? value.toISOString() : value;\n }`,\n `function mergeComparableValue(target, key, value) {\n if (value === undefined) return true;\n const normalized = normalizeDefaultValue(value);\n if (target[key] === undefined) {\n target[key] = normalized;\n return true;\n }\n return Object.is(target[key], normalized);\n }`,\n `function mergeLowerBound(target, key, value) {\n if (value === undefined) return true;\n if (typeof value !== 'number') return false;\n if (target[key] === undefined || value > target[key]) {\n target[key] = value;\n }\n return true;\n }`,\n `function mergeUpperBound(target, key, value) {\n if (value === undefined) return true;\n if (typeof value !== 'number') return false;\n if (target[key] === undefined || value < target[key]) {\n target[key] = value;\n }\n return true;\n }`,\n `function mergeEnumValues(target, values) {\n if (!Array.isArray(values)) return false;\n if (!Array.isArray(target.enum)) {\n target.enum = [...values];\n return true;\n }\n target.enum = target.enum.filter((candidate) =>\n values.some((value) => Object.is(candidate, value)),\n );\n return target.enum.length > 0;\n }`,\n `function isMergeablePrimitiveSchema(schema) {\n return (\n schema &&\n typeof schema === 'object' &&\n !schema.$ref &&\n !schema.anyOf &&\n !schema.oneOf &&\n !schema.allOf &&\n (schema.type === 'string' ||\n schema.type === 'boolean' ||\n schema.type === 'number' ||\n schema.type === 'integer')\n );\n }`,\n `function mergePrimitiveSchemas(schemas) {\n const merged = {};\n for (const schema of schemas) {\n if (\n schema &&\n typeof schema === 'object' &&\n Object.keys(schema).length === 0\n ) {\n continue;\n }\n if (!isMergeablePrimitiveSchema(schema)) {\n return null;\n }\n if (merged.type === undefined) {\n merged.type = schema.type;\n } else if (merged.type !== schema.type) {\n const numericPair =\n (merged.type === 'number' && schema.type === 'integer') ||\n (merged.type === 'integer' && schema.type === 'number');\n if (!numericPair) {\n return null;\n }\n merged.type = 'integer';\n }\n\n for (const [key, value] of Object.entries(schema)) {\n if (key === 'type') {\n continue;\n }\n switch (key) {\n case 'minimum':\n case 'exclusiveMinimum':\n case 'minLength':\n case 'minItems':\n case 'minProperties':\n if (!mergeLowerBound(merged, key, value)) {\n return null;\n }\n break;\n case 'maximum':\n case 'exclusiveMaximum':\n case 'maxLength':\n case 'maxItems':\n case 'maxProperties':\n if (!mergeUpperBound(merged, key, value)) {\n return null;\n }\n break;\n case 'enum':\n if (!mergeEnumValues(merged, value)) {\n return null;\n }\n break;\n default:\n if (!mergeComparableValue(merged, key, value)) {\n return null;\n }\n }\n }\n }\n return merged;\n }`,\n `function normalizeSchema(schema, isIntersectionMember = false) {\n if (!schema || typeof schema !== 'object') {\n return schema;\n }\n\n if (schema.default !== undefined) {\n schema.default = normalizeDefaultValue(schema.default);\n }\n\n if (Array.isArray(schema.items)) {\n schema.items = schema.items.map((item) => normalizeSchema(item));\n } else if (schema.items && typeof schema.items === 'object') {\n schema.items = normalizeSchema(schema.items);\n }\n\n if (schema.properties && typeof schema.properties === 'object') {\n for (const [key, value] of Object.entries(schema.properties)) {\n schema.properties[key] = normalizeSchema(value);\n }\n }\n\n if (\n schema.additionalProperties &&\n typeof schema.additionalProperties === 'object'\n ) {\n schema.additionalProperties = normalizeSchema(schema.additionalProperties);\n }\n\n for (const key of ['anyOf', 'oneOf', 'allOf']) {\n if (!Array.isArray(schema[key])) continue;\n schema[key] = schema[key].map((candidate) =>\n normalizeSchema(candidate, key === 'allOf'),\n );\n }\n\n if (Array.isArray(schema.oneOf) && !schema.anyOf) {\n schema.anyOf = schema.oneOf;\n delete schema.oneOf;\n }\n\n if (\n Array.isArray(schema.anyOf) &&\n schema.anyOf.every(\n (member) =>\n member &&\n typeof member === 'object' &&\n Object.keys(member).length === 1 &&\n typeof member.type === 'string',\n )\n ) {\n const { anyOf, ...rest } = schema;\n schema = { ...rest, type: anyOf.map((member) => member.type) };\n }\n\n if (\n schema.type === 'object' &&\n schema.properties &&\n schema.additionalProperties === undefined &&\n !isIntersectionMember\n ) {\n schema.additionalProperties = false;\n }\n\n if (\n schema.type === 'object' &&\n schema.propertyNames &&\n JSON.stringify(schema.propertyNames) === '{\"type\":\"string\"}'\n ) {\n delete schema.propertyNames;\n }\n\n if (\n typeof schema.$ref === 'string' &&\n !schema.$ref.startsWith('#/components/schemas')\n ) {\n schema.$ref =\n schema.$ref === '#'\n ? '#/components/schemas'\n : schema.$ref.replace(/^#\\\\//, '#/components/schemas/');\n }\n\n if (Array.isArray(schema.allOf)) {\n const merged = mergePrimitiveSchemas(schema.allOf);\n if (merged) {\n const { allOf, ...rest } = schema;\n return { ...rest, ...merged };\n }\n if (schema.allOf.length === 1 && Object.keys(schema).length === 1) {\n return schema.allOf[0];\n }\n }\n\n return schema;\n }`,\n `function escapePointerSegment(segment) {\n return String(segment).replaceAll('~', '~0').replaceAll('/', '~1');\n }`,\n `function componentPointer(path) {\n return ['#', 'components', 'schemas', ...path.map(escapePointerSegment)].join('/');\n }`,\n // Nested toJsonSchema passes (set/map values, pipe outputs) return\n // self-contained draft-7 documents whose '#' and '#/definitions/*' refs\n // are relative to the embedded document root, not the outer schema.\n // Resolve them to outer-root structural pointers before the outer\n // definitions/normalize passes rewrite them into dangling refs.\n `function resolveEmbeddedDocs(root) {\n function process(node, path, docRoot) {\n if (!node || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n node.forEach((item, index) => process(item, [...path, index], docRoot));\n return;\n }\n if (node['x-sdkit-embedded-root']) {\n delete node['x-sdkit-embedded-root'];\n const defs = node.definitions ?? Object.create(null);\n delete node.definitions;\n docRoot = { path, defs, firstSite: Object.create(null) };\n }\n if (docRoot && typeof node.$ref === 'string') {\n if (node.$ref === '#') {\n node.$ref = componentPointer(docRoot.path);\n return;\n }\n const match = node.$ref.match(/^#\\\\/definitions\\\\/(.+)$/);\n if (match) {\n const name = match[1];\n if (name in docRoot.firstSite) {\n node.$ref = docRoot.firstSite[name];\n return;\n }\n docRoot.firstSite[name] = componentPointer(path);\n delete node.$ref;\n Object.assign(node, docRoot.defs[name]);\n }\n }\n for (const [key, value] of Object.entries(node)) {\n process(value, [...path, key], docRoot);\n }\n }\n process(root, [], null);\n return root;\n }`,\n // zod v4 breaks non-root cycles with a draft-7 definitions block plus\n // #/definitions/* refs, which OpenAPI consumers cannot resolve. Inline\n // each definition at its first use site and point the remaining\n // (recursive) refs structurally at that site, mirroring the v3\n // $refStrategy: 'root' output.\n `function inlineDefinitions(root) {\n const defs = root.definitions;\n if (!defs || typeof defs !== 'object') return root;\n delete root.definitions;\n const firstSite = Object.create(null);\n function walk(node, path) {\n if (!node || typeof node !== 'object') return;\n if (Array.isArray(node)) {\n node.forEach((item, index) => walk(item, [...path, index]));\n return;\n }\n const match =\n typeof node.$ref === 'string' &&\n node.$ref.match(/^#\\\\/definitions\\\\/(.+)$/);\n if (match) {\n const name = match[1];\n if (name in firstSite) {\n node.$ref = firstSite[name];\n return;\n }\n firstSite[name] = componentPointer(path);\n delete node.$ref;\n Object.assign(node, defs[name]);\n }\n for (const [key, value] of Object.entries(node)) {\n walk(value, [...path, key]);\n }\n }\n walk(root, []);\n return root;\n }`,\n // Wraps nested conversions: when the embedded document carries refs or a\n // definitions block, tag its root so resolveEmbeddedDocs can rebase them.\n `function convertEmbedded(schema) {\n const { $schema: _ignored, ...doc } = toJsonSchema(schema);\n if (doc.definitions || JSON.stringify(doc).includes('\"$ref\"')) {\n doc['x-sdkit-embedded-root'] = true;\n }\n return doc;\n }`,\n `function toJsonSchema(schema) {\n return z.toJSONSchema(schema, {\n target: 'draft-7',\n io: 'input',\n unrepresentable: 'any',\n override(ctx) {\n const def = ctx.zodSchema._zod.def;\n const json = ctx.jsonSchema;\n if (def.type === 'optional') {\n optional = true;\n }\n if (def.type === 'catch') {\n delete json.default;\n }\n if (def.type === 'date') {\n json.type = 'string';\n json.format = 'date-time';\n json['x-zod-type'] = def.coerce ? 'coerce-date' : 'date';\n }\n if (def.type === 'bigint') {\n json.type = 'integer';\n // z.bigint() carries no format; treat it as int64.\n json.format = ctx.zodSchema._zod.bag.format ?? 'int64';\n if (def.coerce) {\n json['x-zod-type'] = 'coerce-bigint';\n }\n }\n if (def.type === 'set') {\n json.type = 'array';\n json.uniqueItems = true;\n json.items = convertEmbedded(def.valueType);\n }\n if (def.type === 'map') {\n json.type = 'array';\n json.items = {\n type: 'array',\n items: [convertEmbedded(def.keyType), convertEmbedded(def.valueType)],\n minItems: 2,\n maxItems: 2,\n };\n }\n if (def.type === 'string' && json.format === 'base64') {\n json.format = 'binary';\n delete json.pattern;\n delete json.contentEncoding;\n }\n if (\n def.type === 'string' &&\n json.format &&\n json.pattern &&\n !hasExplicitRegexCheck(def)\n ) {\n if (STANDARD_STRING_FORMATS.has(json.format)) {\n delete json.pattern;\n } else if (!MAPPED_NONSTANDARD_FORMATS.has(json.format)) {\n delete json.format;\n }\n }\n if (def.type === 'number') {\n const explicitBound = (kind, value) =>\n (def.checks ?? []).some(\n (check) =>\n check._zod?.def?.check === kind &&\n Number(check._zod.def.value) === value,\n );\n if (\n json.maximum === Number.MAX_SAFE_INTEGER &&\n !explicitBound('less_than', Number.MAX_SAFE_INTEGER)\n ) {\n delete json.maximum;\n }\n if (\n json.minimum === Number.MIN_SAFE_INTEGER &&\n !explicitBound('greater_than', Number.MIN_SAFE_INTEGER)\n ) {\n delete json.minimum;\n }\n }\n if (def.type === 'pipe') {\n const outJson = convertEmbedded(def.out);\n const outKeys = Object.keys(outJson).filter(\n (key) => key !== 'x-sdkit-embedded-root',\n );\n if (outKeys.length > 0) {\n const inJson = { ...json };\n for (const key of Object.keys(json)) delete json[key];\n json.allOf = [inJson, outJson];\n }\n }\n if (def.type === 'tuple' && Array.isArray(json.items)) {\n if (json.minItems === undefined) {\n json.minItems = json.items.length;\n }\n if (\n json.additionalItems === undefined &&\n json.maxItems === undefined\n ) {\n json.maxItems = json.items.length;\n }\n }\n },\n });\n }`,\n `let zodSchema;\n let rawResult;\n try {\n zodSchema = ${removeUnsupportedMethods(schema)};\n maskBigIntDefaults(zodSchema);\n const { $schema, ...converted } = toJsonSchema(zodSchema);\n rawResult = converted;\n } finally {\n // The v3 shims exist only while the analyzed source evaluates and\n // converts (lazy getters can call them mid-conversion); remove them so\n // the shared zod instance is not left with v3 API surface process-wide.\n for (const method of installedShims) delete stringProto[method];\n }\n unmaskBigIntDefaults(rawResult);\n const result = normalizeSchema(\n inlineDefinitions(resolveEmbeddedDocs(rawResult)),\n );`,\n `const innerDef = unwrapSchemaDef(zodSchema._zod.def);\n if (innerDef?.coerce && !result['x-zod-type']) {\n const zodType = 'coerce-' + innerDef.type;\n if (!applyZodType(result, zodType, innerDef.type)) {\n result['x-zod-type'] = zodType;\n }\n }`,\n `export default {schema: result, optional}`,\n ];\n\n const base64 = Buffer.from(lines.join('\\n')).toString('base64');\n return import(\n /* @vite-ignore */\n `data:text/javascript;base64,${base64}`\n ).then((mod) => mod.default);\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sdk-it/core",
3
- "version": "0.43.0",
3
+ "version": "0.45.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -29,13 +29,12 @@
29
29
  "dependencies": {
30
30
  "debug": "^4.4.0",
31
31
  "stringcase": "^4.3.1",
32
- "lodash-es": "^4.17.21",
33
- "zod-to-json-schema": "^3.24.6"
32
+ "lodash-es": "^4.17.21"
34
33
  },
35
34
  "peerDependencies": {
36
35
  "typescript": "^5.8.3",
37
36
  "openapi3-ts": "^4.4.0",
38
- "zod": "^3.25.76 || ^4.0.0"
37
+ "zod": "^4.3.0"
39
38
  },
40
39
  "devDependencies": {
41
40
  "@types/debug": "^4.1.12"