@cerios/openapi-to-zod 1.3.1 → 1.4.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.
- package/dist/cli.js +49 -51
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +51 -53
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +49 -51
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +49 -51
- package/dist/index.mjs.map +1 -1
- package/dist/internal.d.mts +159 -52
- package/dist/internal.d.ts +159 -52
- package/dist/internal.js +1508 -242
- package/dist/internal.js.map +1 -1
- package/dist/internal.mjs +1506 -240
- package/dist/internal.mjs.map +1 -1
- package/package.json +1 -1
package/dist/internal.mjs
CHANGED
|
@@ -135,6 +135,1502 @@ function getBatchExitCode(summary) {
|
|
|
135
135
|
return summary.failed > 0 ? 1 : 0;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
// src/utils/lru-cache.ts
|
|
139
|
+
var LRUCache = class {
|
|
140
|
+
constructor(maxSize) {
|
|
141
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
142
|
+
this.maxSize = maxSize;
|
|
143
|
+
}
|
|
144
|
+
get capacity() {
|
|
145
|
+
return this.maxSize;
|
|
146
|
+
}
|
|
147
|
+
get(key) {
|
|
148
|
+
if (!this.cache.has(key)) return void 0;
|
|
149
|
+
const value = this.cache.get(key);
|
|
150
|
+
if (value === void 0) return void 0;
|
|
151
|
+
this.cache.delete(key);
|
|
152
|
+
this.cache.set(key, value);
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
155
|
+
set(key, value) {
|
|
156
|
+
if (this.cache.has(key)) {
|
|
157
|
+
this.cache.delete(key);
|
|
158
|
+
} else if (this.cache.size >= this.maxSize) {
|
|
159
|
+
const firstKey = this.cache.keys().next().value;
|
|
160
|
+
if (firstKey !== void 0) {
|
|
161
|
+
this.cache.delete(firstKey);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
this.cache.set(key, value);
|
|
165
|
+
}
|
|
166
|
+
has(key) {
|
|
167
|
+
return this.cache.has(key);
|
|
168
|
+
}
|
|
169
|
+
clear() {
|
|
170
|
+
this.cache.clear();
|
|
171
|
+
}
|
|
172
|
+
size() {
|
|
173
|
+
return this.cache.size;
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
// src/utils/name-utils.ts
|
|
178
|
+
function sanitizeIdentifier(str) {
|
|
179
|
+
return str.replace(/[^a-zA-Z0-9._\-\s]+/g, "_");
|
|
180
|
+
}
|
|
181
|
+
function toCamelCase(str, options) {
|
|
182
|
+
const sanitized = sanitizeIdentifier(str);
|
|
183
|
+
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
184
|
+
let name;
|
|
185
|
+
if (words.length === 0) {
|
|
186
|
+
name = str.charAt(0).toLowerCase() + str.slice(1);
|
|
187
|
+
} else if (words.length === 1) {
|
|
188
|
+
name = words[0].charAt(0).toLowerCase() + words[0].slice(1);
|
|
189
|
+
} else {
|
|
190
|
+
name = words[0].charAt(0).toLowerCase() + words[0].slice(1) + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
191
|
+
}
|
|
192
|
+
if (options == null ? void 0 : options.prefix) {
|
|
193
|
+
const prefix = options.prefix.charAt(0).toLowerCase() + options.prefix.slice(1);
|
|
194
|
+
name = prefix + name.charAt(0).toUpperCase() + name.slice(1);
|
|
195
|
+
}
|
|
196
|
+
if (options == null ? void 0 : options.suffix) {
|
|
197
|
+
const suffix = options.suffix.charAt(0).toUpperCase() + options.suffix.slice(1);
|
|
198
|
+
name = name + suffix;
|
|
199
|
+
}
|
|
200
|
+
return name;
|
|
201
|
+
}
|
|
202
|
+
function toPascalCase(str) {
|
|
203
|
+
const stringValue = String(str);
|
|
204
|
+
const isAlreadyValidCase = /^[a-zA-Z][a-zA-Z0-9]*$/.test(stringValue);
|
|
205
|
+
if (isAlreadyValidCase) {
|
|
206
|
+
return stringValue.charAt(0).toUpperCase() + stringValue.slice(1);
|
|
207
|
+
}
|
|
208
|
+
const sanitized = sanitizeIdentifier(stringValue);
|
|
209
|
+
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
210
|
+
let result;
|
|
211
|
+
if (words.length === 0) {
|
|
212
|
+
result = "Value";
|
|
213
|
+
} else {
|
|
214
|
+
result = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
215
|
+
}
|
|
216
|
+
if (/^\d/.test(result)) {
|
|
217
|
+
result = `N${result}`;
|
|
218
|
+
}
|
|
219
|
+
if (!result || /^_+$/.test(result)) {
|
|
220
|
+
return "Value";
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
}
|
|
224
|
+
function resolveRef(ref) {
|
|
225
|
+
const parts = ref.split("/");
|
|
226
|
+
return parts[parts.length - 1];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/utils/pattern-utils.ts
|
|
230
|
+
import { minimatch } from "minimatch";
|
|
231
|
+
function isValidGlobPattern(pattern) {
|
|
232
|
+
try {
|
|
233
|
+
new minimatch.Minimatch(pattern);
|
|
234
|
+
return true;
|
|
235
|
+
} catch {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
function isGlobPattern(pattern) {
|
|
240
|
+
return /[*?[\]{}!]/.test(pattern);
|
|
241
|
+
}
|
|
242
|
+
function stripPrefix(input, pattern, ensureLeadingChar) {
|
|
243
|
+
if (!pattern) {
|
|
244
|
+
return input;
|
|
245
|
+
}
|
|
246
|
+
if (isGlobPattern(pattern) && !isValidGlobPattern(pattern)) {
|
|
247
|
+
console.warn(`\u26A0\uFE0F Invalid glob pattern "${pattern}": Pattern is malformed`);
|
|
248
|
+
return input;
|
|
249
|
+
}
|
|
250
|
+
if (isGlobPattern(pattern)) {
|
|
251
|
+
let longestMatch = -1;
|
|
252
|
+
for (let i = 1; i <= input.length; i++) {
|
|
253
|
+
const testPrefix = input.substring(0, i);
|
|
254
|
+
if (minimatch(testPrefix, pattern)) {
|
|
255
|
+
longestMatch = i;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (longestMatch > 0) {
|
|
259
|
+
const stripped = input.substring(longestMatch);
|
|
260
|
+
if (ensureLeadingChar) {
|
|
261
|
+
if (stripped === "") {
|
|
262
|
+
return ensureLeadingChar;
|
|
263
|
+
}
|
|
264
|
+
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
265
|
+
return `${ensureLeadingChar}${stripped}`;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return stripped === "" && !ensureLeadingChar ? input : stripped;
|
|
269
|
+
}
|
|
270
|
+
return input;
|
|
271
|
+
}
|
|
272
|
+
if (input.startsWith(pattern)) {
|
|
273
|
+
const stripped = input.substring(pattern.length);
|
|
274
|
+
if (ensureLeadingChar) {
|
|
275
|
+
if (stripped === "") {
|
|
276
|
+
return ensureLeadingChar;
|
|
277
|
+
}
|
|
278
|
+
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
279
|
+
return `${ensureLeadingChar}${stripped}`;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return stripped;
|
|
283
|
+
}
|
|
284
|
+
return input;
|
|
285
|
+
}
|
|
286
|
+
function stripPathPrefix(path, pattern) {
|
|
287
|
+
if (!pattern) {
|
|
288
|
+
return path;
|
|
289
|
+
}
|
|
290
|
+
if (!isGlobPattern(pattern)) {
|
|
291
|
+
let normalizedPattern = pattern.trim();
|
|
292
|
+
if (!normalizedPattern.startsWith("/")) {
|
|
293
|
+
normalizedPattern = `/${normalizedPattern}`;
|
|
294
|
+
}
|
|
295
|
+
if (normalizedPattern.endsWith("/") && normalizedPattern !== "/") {
|
|
296
|
+
normalizedPattern = normalizedPattern.slice(0, -1);
|
|
297
|
+
}
|
|
298
|
+
return stripPrefix(path, normalizedPattern, "/");
|
|
299
|
+
}
|
|
300
|
+
return stripPrefix(path, pattern, "/");
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// src/utils/string-utils.ts
|
|
304
|
+
function escapeDescription(str) {
|
|
305
|
+
return str.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n");
|
|
306
|
+
}
|
|
307
|
+
function escapePattern(str) {
|
|
308
|
+
return str.replace(/(?<!\\)\//g, "\\/");
|
|
309
|
+
}
|
|
310
|
+
function escapeJSDoc(str) {
|
|
311
|
+
return str.replace(/\*\//g, "*\\/");
|
|
312
|
+
}
|
|
313
|
+
function wrapNullable(validation, isNullable2) {
|
|
314
|
+
return isNullable2 ? `${validation}.nullable()` : validation;
|
|
315
|
+
}
|
|
316
|
+
function isNullable(schema, defaultNullable = false) {
|
|
317
|
+
if (schema.nullable === true) {
|
|
318
|
+
return true;
|
|
319
|
+
}
|
|
320
|
+
if (schema.nullable === false) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
if (Array.isArray(schema.type)) {
|
|
324
|
+
return schema.type.includes("null");
|
|
325
|
+
}
|
|
326
|
+
return defaultNullable;
|
|
327
|
+
}
|
|
328
|
+
function getPrimaryType(schema) {
|
|
329
|
+
if (Array.isArray(schema.type)) {
|
|
330
|
+
const nonNullType = schema.type.find((t) => t !== "null");
|
|
331
|
+
return nonNullType;
|
|
332
|
+
}
|
|
333
|
+
return schema.type;
|
|
334
|
+
}
|
|
335
|
+
function hasMultipleTypes(schema) {
|
|
336
|
+
if (Array.isArray(schema.type)) {
|
|
337
|
+
const nonNullTypes = schema.type.filter((t) => t !== "null");
|
|
338
|
+
return nonNullTypes.length > 1;
|
|
339
|
+
}
|
|
340
|
+
return false;
|
|
341
|
+
}
|
|
342
|
+
function addDescription(validation, description, useDescribe) {
|
|
343
|
+
if (!description || !useDescribe) return validation;
|
|
344
|
+
const escapedDesc = escapeDescription(description);
|
|
345
|
+
return `${validation}.describe("${escapedDesc}")`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// src/validators/array-validator.ts
|
|
349
|
+
function generateArrayValidation(schema, context) {
|
|
350
|
+
var _a;
|
|
351
|
+
let validation;
|
|
352
|
+
if (schema.prefixItems && schema.prefixItems.length > 0) {
|
|
353
|
+
const tupleItems = schema.prefixItems.map((item) => context.generatePropertySchema(item, context.currentSchema));
|
|
354
|
+
validation = `z.tuple([${tupleItems.join(", ")}])`;
|
|
355
|
+
if (schema.items) {
|
|
356
|
+
const restSchema = context.generatePropertySchema(schema.items, context.currentSchema);
|
|
357
|
+
validation += `.rest(${restSchema})`;
|
|
358
|
+
} else if (schema.unevaluatedItems && typeof schema.unevaluatedItems === "object") {
|
|
359
|
+
const restSchema = context.generatePropertySchema(schema.unevaluatedItems, context.currentSchema);
|
|
360
|
+
validation += `.rest(${restSchema})`;
|
|
361
|
+
}
|
|
362
|
+
} else if (schema.items) {
|
|
363
|
+
const itemSchema = context.generatePropertySchema(schema.items, context.currentSchema);
|
|
364
|
+
validation = `z.array(${itemSchema})`;
|
|
365
|
+
if (schema.minItems !== void 0) {
|
|
366
|
+
validation += `.min(${schema.minItems})`;
|
|
367
|
+
}
|
|
368
|
+
if (schema.maxItems !== void 0) {
|
|
369
|
+
validation += `.max(${schema.maxItems})`;
|
|
370
|
+
}
|
|
371
|
+
if (schema.uniqueItems === true) {
|
|
372
|
+
validation += `.refine((items) => new Set(items).size === items.length, { message: "Array items must be unique" })`;
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
validation = "z.array(z.unknown())";
|
|
376
|
+
}
|
|
377
|
+
if (schema.contains) {
|
|
378
|
+
const containsSchema = context.generatePropertySchema(schema.contains, context.currentSchema);
|
|
379
|
+
const minCount = (_a = schema.minContains) != null ? _a : 1;
|
|
380
|
+
const maxCount = schema.maxContains;
|
|
381
|
+
if (maxCount !== void 0) {
|
|
382
|
+
validation += `.refine((arr) => { const matches = arr.filter(item => ${containsSchema}.safeParse(item).success); return matches.length >= ${minCount} && matches.length <= ${maxCount}; }, { message: "Array must contain between ${minCount} and ${maxCount} items matching the schema" })`;
|
|
383
|
+
} else {
|
|
384
|
+
validation += `.refine((arr) => arr.filter(item => ${containsSchema}.safeParse(item).success).length >= ${minCount}, { message: "Array must contain at least ${minCount} item(s) matching the schema" })`;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
if (schema.unevaluatedItems === false && schema.prefixItems && schema.prefixItems.length > 0 && !schema.items) {
|
|
388
|
+
const prefixCount = schema.prefixItems.length;
|
|
389
|
+
validation += `.refine((arr) => arr.length <= ${prefixCount}, { message: "No unevaluated items allowed beyond prefix items" })`;
|
|
390
|
+
}
|
|
391
|
+
return addDescription(validation, schema.description, context.useDescribe);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// src/validators/composition-validator.ts
|
|
395
|
+
function isDiscriminatorRequired(schemas, discriminator, context) {
|
|
396
|
+
const invalidSchemas = [];
|
|
397
|
+
for (const schema of schemas) {
|
|
398
|
+
const resolved = resolveSchema(schema, context);
|
|
399
|
+
const required = resolved.required || [];
|
|
400
|
+
if (!required.includes(discriminator)) {
|
|
401
|
+
const schemaName = schema.$ref ? schema.$ref.split("/").pop() || "inline" : "inline";
|
|
402
|
+
invalidSchemas.push(schemaName);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
valid: invalidSchemas.length === 0,
|
|
407
|
+
invalidSchemas
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function generateUnion(schemas, discriminator, isNullable2, context, options, currentSchema) {
|
|
411
|
+
if (schemas.length === 0) {
|
|
412
|
+
console.warn(
|
|
413
|
+
"[openapi-to-zod] Warning: Empty oneOf/anyOf array encountered. This is likely a malformed OpenAPI spec. Generating z.never() as fallback."
|
|
414
|
+
);
|
|
415
|
+
return wrapNullable(
|
|
416
|
+
'z.never().describe("Empty oneOf/anyOf in OpenAPI spec - no valid schema defined")',
|
|
417
|
+
isNullable2
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
if (schemas.length === 1) {
|
|
421
|
+
let singleSchema = context.generatePropertySchema(schemas[0], currentSchema, false, true);
|
|
422
|
+
if ((options == null ? void 0 : options.passthrough) && !singleSchema.includes(".catchall(")) {
|
|
423
|
+
singleSchema = `${singleSchema}.catchall(z.unknown())`;
|
|
424
|
+
}
|
|
425
|
+
return wrapNullable(singleSchema, isNullable2);
|
|
426
|
+
}
|
|
427
|
+
if (discriminator) {
|
|
428
|
+
let resolvedSchemas = schemas;
|
|
429
|
+
if ((options == null ? void 0 : options.discriminatorMapping) && context.resolveDiscriminatorMapping) {
|
|
430
|
+
resolvedSchemas = context.resolveDiscriminatorMapping(options.discriminatorMapping, schemas);
|
|
431
|
+
}
|
|
432
|
+
const discriminatorCheck = isDiscriminatorRequired(resolvedSchemas, discriminator, context);
|
|
433
|
+
if (!discriminatorCheck.valid) {
|
|
434
|
+
console.warn(
|
|
435
|
+
`[openapi-to-zod] Warning: Discriminator "${discriminator}" is not required in schemas: ${discriminatorCheck.invalidSchemas.join(", ")}. Falling back to z.union() instead of z.discriminatedUnion().`
|
|
436
|
+
);
|
|
437
|
+
let schemaStrings3 = resolvedSchemas.map((s) => context.generatePropertySchema(s, currentSchema, false, true));
|
|
438
|
+
if (options == null ? void 0 : options.passthrough) {
|
|
439
|
+
schemaStrings3 = schemaStrings3.map((s) => s.includes(".catchall(") ? s : `${s}.catchall(z.unknown())`);
|
|
440
|
+
}
|
|
441
|
+
const fallbackDescription = `Discriminator "${discriminator}" is optional in some schemas (${discriminatorCheck.invalidSchemas.join(", ")}), using z.union() instead of z.discriminatedUnion()`;
|
|
442
|
+
const union3 = `z.union([${schemaStrings3.join(", ")}]).describe("${fallbackDescription}")`;
|
|
443
|
+
return wrapNullable(union3, isNullable2);
|
|
444
|
+
}
|
|
445
|
+
let schemaStrings2 = resolvedSchemas.map((s) => context.generatePropertySchema(s, currentSchema, false, true));
|
|
446
|
+
if (options == null ? void 0 : options.passthrough) {
|
|
447
|
+
schemaStrings2 = schemaStrings2.map((s) => s.includes(".catchall(") ? s : `${s}.catchall(z.unknown())`);
|
|
448
|
+
}
|
|
449
|
+
const union2 = `z.discriminatedUnion("${discriminator}", [${schemaStrings2.join(", ")}])`;
|
|
450
|
+
return wrapNullable(union2, isNullable2);
|
|
451
|
+
}
|
|
452
|
+
let schemaStrings = schemas.map((s) => context.generatePropertySchema(s, currentSchema, false, true));
|
|
453
|
+
if (options == null ? void 0 : options.passthrough) {
|
|
454
|
+
schemaStrings = schemaStrings.map((s) => s.includes(".catchall(") ? s : `${s}.catchall(z.unknown())`);
|
|
455
|
+
}
|
|
456
|
+
const union = `z.union([${schemaStrings.join(", ")}])`;
|
|
457
|
+
return wrapNullable(union, isNullable2);
|
|
458
|
+
}
|
|
459
|
+
function resolveSchema(schema, context) {
|
|
460
|
+
if (schema.$ref && context.resolveSchemaRef) {
|
|
461
|
+
const resolved = context.resolveSchemaRef(schema.$ref);
|
|
462
|
+
if (resolved) {
|
|
463
|
+
return resolved;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
return schema;
|
|
467
|
+
}
|
|
468
|
+
function collectProperties(schema, context) {
|
|
469
|
+
const resolved = resolveSchema(schema, context);
|
|
470
|
+
const props = /* @__PURE__ */ new Map();
|
|
471
|
+
const sourceName = schema.$ref ? schema.$ref.split("/").pop() || "unknown" : "inline";
|
|
472
|
+
if (resolved.properties) {
|
|
473
|
+
for (const [key, value] of Object.entries(resolved.properties)) {
|
|
474
|
+
props.set(key, { schema: value, source: sourceName });
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (resolved.allOf) {
|
|
478
|
+
for (const subSchema of resolved.allOf) {
|
|
479
|
+
const subProps = collectProperties(subSchema, context);
|
|
480
|
+
for (const [key, value] of subProps) {
|
|
481
|
+
if (!props.has(key)) {
|
|
482
|
+
props.set(key, value);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
return props;
|
|
488
|
+
}
|
|
489
|
+
function schemasMatch(a, b) {
|
|
490
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
491
|
+
}
|
|
492
|
+
function detectConflictingProperties(schemas, context) {
|
|
493
|
+
const conflicts = [];
|
|
494
|
+
const propertyMap = /* @__PURE__ */ new Map();
|
|
495
|
+
for (const schema of schemas) {
|
|
496
|
+
const schemaProps = collectProperties(schema, context);
|
|
497
|
+
for (const [propName, propInfo] of schemaProps) {
|
|
498
|
+
const existing = propertyMap.get(propName);
|
|
499
|
+
if (existing) {
|
|
500
|
+
if (!schemasMatch(existing.schema, propInfo.schema)) {
|
|
501
|
+
conflicts.push(
|
|
502
|
+
`Property "${propName}" has conflicting definitions in ${existing.source} and ${propInfo.source}`
|
|
503
|
+
);
|
|
504
|
+
}
|
|
505
|
+
} else {
|
|
506
|
+
propertyMap.set(propName, propInfo);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
return conflicts;
|
|
511
|
+
}
|
|
512
|
+
function generateAllOf(schemas, isNullable2, context, currentSchema) {
|
|
513
|
+
if (schemas.length === 1) {
|
|
514
|
+
const singleSchema = context.generatePropertySchema(schemas[0], currentSchema, false, true);
|
|
515
|
+
return wrapNullable(singleSchema, isNullable2);
|
|
516
|
+
}
|
|
517
|
+
const conflicts = detectConflictingProperties(schemas, context);
|
|
518
|
+
let conflictDescription = "";
|
|
519
|
+
if (conflicts.length > 0) {
|
|
520
|
+
for (const conflict of conflicts) {
|
|
521
|
+
console.warn(`[openapi-to-zod] Warning: allOf composition conflict - ${conflict}`);
|
|
522
|
+
}
|
|
523
|
+
conflictDescription = `allOf property conflicts detected: ${conflicts.join("; ")}`;
|
|
524
|
+
}
|
|
525
|
+
const allObjects = schemas.every((s) => s.type === "object" || s.properties || s.$ref || s.allOf);
|
|
526
|
+
let result;
|
|
527
|
+
if (allObjects) {
|
|
528
|
+
let merged = context.generatePropertySchema(schemas[0], currentSchema, false, true);
|
|
529
|
+
for (let i = 1; i < schemas.length; i++) {
|
|
530
|
+
const schema = schemas[i];
|
|
531
|
+
if (schema.$ref) {
|
|
532
|
+
const refSchema = context.generatePropertySchema(schema, currentSchema, false, true);
|
|
533
|
+
merged = `${merged}.extend(${refSchema}.shape)`;
|
|
534
|
+
} else if (context.generateInlineObjectShape && (schema.properties || schema.type === "object")) {
|
|
535
|
+
const inlineShape = context.generateInlineObjectShape(schema, currentSchema);
|
|
536
|
+
merged = `${merged}.extend(${inlineShape})`;
|
|
537
|
+
} else {
|
|
538
|
+
const schemaString = context.generatePropertySchema(schema, currentSchema, false, true);
|
|
539
|
+
merged = `${merged}.extend(${schemaString}.shape)`;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
result = merged;
|
|
543
|
+
} else {
|
|
544
|
+
const schemaStrings = schemas.map((s) => context.generatePropertySchema(s, currentSchema, false, true));
|
|
545
|
+
let merged = schemaStrings[0];
|
|
546
|
+
for (let i = 1; i < schemaStrings.length; i++) {
|
|
547
|
+
merged = `${merged}.and(${schemaStrings[i]})`;
|
|
548
|
+
}
|
|
549
|
+
result = merged;
|
|
550
|
+
}
|
|
551
|
+
if (conflictDescription) {
|
|
552
|
+
result = `${result}.describe("${conflictDescription}")`;
|
|
553
|
+
}
|
|
554
|
+
return wrapNullable(result, isNullable2);
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
// src/validators/number-validator.ts
|
|
558
|
+
function generateNumberValidation(schema, isInt, useDescribe) {
|
|
559
|
+
let validation = isInt ? "z.number().int()" : "z.number()";
|
|
560
|
+
if (schema.minimum !== void 0) {
|
|
561
|
+
const isExclusive = schema.exclusiveMinimum === true;
|
|
562
|
+
validation += isExclusive ? `.gt(${schema.minimum})` : `.gte(${schema.minimum})`;
|
|
563
|
+
} else if (typeof schema.exclusiveMinimum === "number") {
|
|
564
|
+
validation += `.gt(${schema.exclusiveMinimum})`;
|
|
565
|
+
}
|
|
566
|
+
if (schema.maximum !== void 0) {
|
|
567
|
+
const isExclusive = schema.exclusiveMaximum === true;
|
|
568
|
+
validation += isExclusive ? `.lt(${schema.maximum})` : `.lte(${schema.maximum})`;
|
|
569
|
+
} else if (typeof schema.exclusiveMaximum === "number") {
|
|
570
|
+
validation += `.lt(${schema.exclusiveMaximum})`;
|
|
571
|
+
}
|
|
572
|
+
if (schema.multipleOf !== void 0) {
|
|
573
|
+
validation += `.multipleOf(${schema.multipleOf})`;
|
|
574
|
+
}
|
|
575
|
+
return addDescription(validation, schema.description, useDescribe);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// src/generators/jsdoc-generator.ts
|
|
579
|
+
function generateJSDoc(schema, name, options = { includeDescriptions: true }) {
|
|
580
|
+
if (!schema || typeof schema !== "object") {
|
|
581
|
+
return "";
|
|
582
|
+
}
|
|
583
|
+
if (!options.includeDescriptions) {
|
|
584
|
+
if (schema.deprecated) {
|
|
585
|
+
return "/** @deprecated */\n";
|
|
586
|
+
}
|
|
587
|
+
return "";
|
|
588
|
+
}
|
|
589
|
+
if (!schema.description && !schema.title && !schema.deprecated && !schema.examples && schema.example === void 0) {
|
|
590
|
+
return "";
|
|
591
|
+
}
|
|
592
|
+
const parts = [];
|
|
593
|
+
if (schema.title && typeof schema.title === "string" && (!name || schema.title !== name)) {
|
|
594
|
+
const sanitizedTitle = escapeJSDoc(schema.title).replace(/@/g, "\\@");
|
|
595
|
+
parts.push(sanitizedTitle);
|
|
596
|
+
}
|
|
597
|
+
if (schema.description && typeof schema.description === "string") {
|
|
598
|
+
const sanitizedDesc = escapeJSDoc(schema.description).replace(/@/g, "\\@").replace(/\*\//g, "*\\/");
|
|
599
|
+
parts.push(sanitizedDesc);
|
|
600
|
+
}
|
|
601
|
+
if (schema.examples && Array.isArray(schema.examples) && schema.examples.length > 0) {
|
|
602
|
+
try {
|
|
603
|
+
const examplesStr = schema.examples.map((ex) => JSON.stringify(ex)).join(", ");
|
|
604
|
+
parts.push(`@example ${examplesStr}`);
|
|
605
|
+
} catch (error) {
|
|
606
|
+
console.warn("Warning: Could not serialize schema examples", error);
|
|
607
|
+
}
|
|
608
|
+
} else if (schema.example !== void 0) {
|
|
609
|
+
try {
|
|
610
|
+
parts.push(`@example ${JSON.stringify(schema.example)}`);
|
|
611
|
+
} catch (error) {
|
|
612
|
+
console.warn("Warning: Could not serialize schema example", error);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
if (schema.deprecated) {
|
|
616
|
+
parts.push("@deprecated");
|
|
617
|
+
}
|
|
618
|
+
if (parts.length === 0) {
|
|
619
|
+
return "";
|
|
620
|
+
}
|
|
621
|
+
const fullComment = parts.join(" ");
|
|
622
|
+
return `/** ${fullComment} */
|
|
623
|
+
`;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// src/validators/conditional-validator.ts
|
|
627
|
+
function generatePropertyAccess(propName) {
|
|
628
|
+
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
629
|
+
return validIdentifier.test(propName) ? `obj.${propName}` : `obj["${propName}"]`;
|
|
630
|
+
}
|
|
631
|
+
function generateDependencies(schema, generatePropertySchema, currentSchema) {
|
|
632
|
+
if (!schema.dependencies) {
|
|
633
|
+
return "";
|
|
634
|
+
}
|
|
635
|
+
let result = "";
|
|
636
|
+
for (const [prop, dependency] of Object.entries(schema.dependencies)) {
|
|
637
|
+
if (Array.isArray(dependency)) {
|
|
638
|
+
if (dependency.length === 0) {
|
|
639
|
+
continue;
|
|
640
|
+
}
|
|
641
|
+
const propAccess = generatePropertyAccess(prop);
|
|
642
|
+
const checkLogic = dependency.map((p) => {
|
|
643
|
+
const pAccess = generatePropertyAccess(p);
|
|
644
|
+
return `if (${pAccess} === undefined) missing.push('${p}');`;
|
|
645
|
+
}).join("\n ");
|
|
646
|
+
result += `.superRefine((obj, ctx) => {
|
|
647
|
+
if (${propAccess} === undefined) return;
|
|
648
|
+
const missing: string[] = [];
|
|
649
|
+
${checkLogic}
|
|
650
|
+
if (missing.length > 0) {
|
|
651
|
+
ctx.addIssue({
|
|
652
|
+
code: "custom",
|
|
653
|
+
message: \`When '${prop}' is present, the following properties are required: \${missing.join(', ')}\`,
|
|
654
|
+
path: []
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
})`;
|
|
658
|
+
} else if (generatePropertySchema) {
|
|
659
|
+
const depSchema = { ...dependency, type: dependency.type || "object" };
|
|
660
|
+
const depSchemaValidation = generatePropertySchema(depSchema, currentSchema);
|
|
661
|
+
const propAccess = generatePropertyAccess(prop);
|
|
662
|
+
result += `.superRefine((obj, ctx) => {
|
|
663
|
+
if (${propAccess} === undefined) return;
|
|
664
|
+
const validation = ${depSchemaValidation}.safeParse(obj);
|
|
665
|
+
if (!validation.success) {
|
|
666
|
+
const errors = validation.error.issues.map(i => \` - \${i.path.join('.')}: \${i.message}\`).join('\\n');
|
|
667
|
+
ctx.addIssue({
|
|
668
|
+
code: "custom",
|
|
669
|
+
message: \`When '${prop}' is present, object must satisfy additional constraints:\\n\${errors}\`,
|
|
670
|
+
path: []
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
})`;
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return result;
|
|
677
|
+
}
|
|
678
|
+
function generateConditionalCheck(schema) {
|
|
679
|
+
const conditions = [];
|
|
680
|
+
if (schema.properties) {
|
|
681
|
+
for (const [prop, propSchema] of Object.entries(schema.properties)) {
|
|
682
|
+
const propAccess = generatePropertyAccess(prop);
|
|
683
|
+
if (propSchema.type) {
|
|
684
|
+
conditions.push(`typeof ${propAccess} === "${propSchema.type}"`);
|
|
685
|
+
}
|
|
686
|
+
if (propSchema.const !== void 0) {
|
|
687
|
+
const value = typeof propSchema.const === "string" ? `"${propSchema.const}"` : propSchema.const;
|
|
688
|
+
conditions.push(`${propAccess} === ${value}`);
|
|
689
|
+
}
|
|
690
|
+
if (propSchema.minimum !== void 0) {
|
|
691
|
+
conditions.push(`${propAccess} >= ${propSchema.minimum}`);
|
|
692
|
+
}
|
|
693
|
+
if (propSchema.maximum !== void 0) {
|
|
694
|
+
conditions.push(`${propAccess} <= ${propSchema.maximum}`);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
if (schema.required) {
|
|
699
|
+
for (const prop of schema.required) {
|
|
700
|
+
conditions.push(`${generatePropertyAccess(prop)} !== undefined`);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return conditions.length > 0 ? conditions.join(" && ") : "true";
|
|
704
|
+
}
|
|
705
|
+
function generateConditionalValidation(schema) {
|
|
706
|
+
const checks = [];
|
|
707
|
+
if (schema.required) {
|
|
708
|
+
for (const prop of schema.required) {
|
|
709
|
+
checks.push(`${generatePropertyAccess(prop)} !== undefined`);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
if (schema.properties) {
|
|
713
|
+
for (const [prop, propSchema] of Object.entries(schema.properties)) {
|
|
714
|
+
const propAccess = generatePropertyAccess(prop);
|
|
715
|
+
if (propSchema.minimum !== void 0) {
|
|
716
|
+
checks.push(`${propAccess} === undefined || ${propAccess} >= ${propSchema.minimum}`);
|
|
717
|
+
}
|
|
718
|
+
if (propSchema.maximum !== void 0) {
|
|
719
|
+
checks.push(`${propAccess} === undefined || ${propAccess} <= ${propSchema.maximum}`);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return checks.length > 0 ? checks.join(" && ") : "true";
|
|
724
|
+
}
|
|
725
|
+
function generateIfThenElse(schema) {
|
|
726
|
+
if (!schema.if || !schema.then && !schema.else) {
|
|
727
|
+
return "";
|
|
728
|
+
}
|
|
729
|
+
const ifCondition = generateConditionalCheck(schema.if);
|
|
730
|
+
if (schema.then && schema.else) {
|
|
731
|
+
const thenValidation = generateConditionalValidation(schema.then);
|
|
732
|
+
const elseValidation2 = generateConditionalValidation(schema.else);
|
|
733
|
+
const thenRequiredProps = schema.then.required || [];
|
|
734
|
+
const elseRequiredProps2 = schema.else.required || [];
|
|
735
|
+
return `.superRefine((obj, ctx) => {
|
|
736
|
+
const ifConditionMet = ${ifCondition};
|
|
737
|
+
if (ifConditionMet) {
|
|
738
|
+
// Then branch
|
|
739
|
+
const thenValid = ${thenValidation};
|
|
740
|
+
if (!thenValid) {
|
|
741
|
+
${thenRequiredProps.length > 0 ? `
|
|
742
|
+
const missingThenProps = ${JSON.stringify(thenRequiredProps)}.filter(p => obj[p] === undefined);
|
|
743
|
+
const message = missingThenProps.length > 0
|
|
744
|
+
? \`When condition is met, required properties are missing: \${missingThenProps.join(', ')}\`
|
|
745
|
+
: "When condition is met, validation constraints failed";
|
|
746
|
+
` : `
|
|
747
|
+
const message = "When condition is met, validation constraints failed";
|
|
748
|
+
`}
|
|
749
|
+
ctx.addIssue({
|
|
750
|
+
code: "custom",
|
|
751
|
+
message: message,
|
|
752
|
+
path: []
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
} else {
|
|
756
|
+
// Else branch
|
|
757
|
+
const elseValid = ${elseValidation2};
|
|
758
|
+
if (!elseValid) {
|
|
759
|
+
${elseRequiredProps2.length > 0 ? `
|
|
760
|
+
const missingElseProps = ${JSON.stringify(elseRequiredProps2)}.filter(p => obj[p] === undefined);
|
|
761
|
+
const message = missingElseProps.length > 0
|
|
762
|
+
? \`When condition is not met, required properties are missing: \${missingElseProps.join(', ')}\`
|
|
763
|
+
: "When condition is not met, validation constraints failed";
|
|
764
|
+
` : `
|
|
765
|
+
const message = "When condition is not met, validation constraints failed";
|
|
766
|
+
`}
|
|
767
|
+
ctx.addIssue({
|
|
768
|
+
code: "custom",
|
|
769
|
+
message: message,
|
|
770
|
+
path: []
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
})`;
|
|
775
|
+
}
|
|
776
|
+
if (schema.then) {
|
|
777
|
+
const thenValidation = generateConditionalValidation(schema.then);
|
|
778
|
+
const thenRequiredProps = schema.then.required || [];
|
|
779
|
+
return `.superRefine((obj, ctx) => {
|
|
780
|
+
const ifConditionMet = ${ifCondition};
|
|
781
|
+
if (ifConditionMet) {
|
|
782
|
+
const thenValid = ${thenValidation};
|
|
783
|
+
if (!thenValid) {
|
|
784
|
+
${thenRequiredProps.length > 0 ? `
|
|
785
|
+
const missingProps = ${JSON.stringify(thenRequiredProps)}.filter(p => obj[p] === undefined);
|
|
786
|
+
const message = missingProps.length > 0
|
|
787
|
+
? \`When condition is met, required properties are missing: \${missingProps.join(', ')}\`
|
|
788
|
+
: "When condition is met, validation constraints failed";
|
|
789
|
+
` : `
|
|
790
|
+
const message = "When condition is met, validation constraints failed";
|
|
791
|
+
`}
|
|
792
|
+
ctx.addIssue({
|
|
793
|
+
code: "custom",
|
|
794
|
+
message: message,
|
|
795
|
+
path: []
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
})`;
|
|
800
|
+
}
|
|
801
|
+
if (!schema.else) return "";
|
|
802
|
+
const elseValidation = generateConditionalValidation(schema.else);
|
|
803
|
+
const elseRequiredProps = schema.else.required || [];
|
|
804
|
+
return `.superRefine((obj, ctx) => {
|
|
805
|
+
const ifConditionMet = ${ifCondition};
|
|
806
|
+
if (!ifConditionMet) {
|
|
807
|
+
const elseValid = ${elseValidation};
|
|
808
|
+
if (!elseValid) {
|
|
809
|
+
${elseRequiredProps.length > 0 ? `
|
|
810
|
+
const missingProps = ${JSON.stringify(elseRequiredProps)}.filter(p => obj[p] === undefined);
|
|
811
|
+
const message = missingProps.length > 0
|
|
812
|
+
? \`When condition is not met, required properties are missing: \${missingProps.join(', ')}\`
|
|
813
|
+
: "When condition is not met, validation constraints failed";
|
|
814
|
+
` : `
|
|
815
|
+
const message = "When condition is not met, validation constraints failed";
|
|
816
|
+
`}
|
|
817
|
+
ctx.addIssue({
|
|
818
|
+
code: "custom",
|
|
819
|
+
message: message,
|
|
820
|
+
path: []
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
})`;
|
|
825
|
+
}
|
|
826
|
+
function generateDependentRequired(schema) {
|
|
827
|
+
if (!schema.dependentRequired) {
|
|
828
|
+
return "";
|
|
829
|
+
}
|
|
830
|
+
let result = "";
|
|
831
|
+
for (const [prop, requiredProps] of Object.entries(schema.dependentRequired)) {
|
|
832
|
+
if (requiredProps.length === 0) {
|
|
833
|
+
continue;
|
|
834
|
+
}
|
|
835
|
+
const propAccess = generatePropertyAccess(prop);
|
|
836
|
+
const checkLogic = requiredProps.map((rp) => {
|
|
837
|
+
const rpAccess = generatePropertyAccess(rp);
|
|
838
|
+
return `if (${rpAccess} === undefined) missing.push('${rp}');`;
|
|
839
|
+
}).join("\n ");
|
|
840
|
+
result += `.superRefine((obj, ctx) => {
|
|
841
|
+
if (${propAccess} === undefined) return;
|
|
842
|
+
const missing: string[] = [];
|
|
843
|
+
${checkLogic}
|
|
844
|
+
if (missing.length > 0) {
|
|
845
|
+
ctx.addIssue({
|
|
846
|
+
code: "custom",
|
|
847
|
+
message: \`When '${prop}' is present, the following properties are required: \${missing.join(', ')}\`,
|
|
848
|
+
path: []
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
})`;
|
|
852
|
+
}
|
|
853
|
+
return result;
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// src/validators/object-validator.ts
|
|
857
|
+
function needsQuoting(propName) {
|
|
858
|
+
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
859
|
+
return !validIdentifier.test(propName);
|
|
860
|
+
}
|
|
861
|
+
function generatePropertyAccess2(propName) {
|
|
862
|
+
return needsQuoting(propName) ? `obj["${propName}"]` : `obj.${propName}`;
|
|
863
|
+
}
|
|
864
|
+
function generateObjectSchema(schema, context, currentSchema) {
|
|
865
|
+
const required = new Set(schema.required || []);
|
|
866
|
+
const properties = [];
|
|
867
|
+
if (schema.properties) {
|
|
868
|
+
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
869
|
+
if (!context.shouldIncludeProperty(propSchema)) {
|
|
870
|
+
continue;
|
|
871
|
+
}
|
|
872
|
+
const isRequired = required.has(propName);
|
|
873
|
+
const zodSchema = context.generatePropertySchema(propSchema, currentSchema);
|
|
874
|
+
const quotedPropName = needsQuoting(propName) ? `"${propName}"` : propName;
|
|
875
|
+
let propertyDef = ` ${quotedPropName}: ${zodSchema}`;
|
|
876
|
+
if (!isRequired) {
|
|
877
|
+
propertyDef += ".optional()";
|
|
878
|
+
}
|
|
879
|
+
const jsdoc = generateJSDoc(propSchema, propName, { includeDescriptions: context.includeDescriptions });
|
|
880
|
+
if (jsdoc) {
|
|
881
|
+
properties.push(`${jsdoc.trimEnd()}
|
|
882
|
+
${propertyDef}`);
|
|
883
|
+
} else {
|
|
884
|
+
properties.push(propertyDef);
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
let objectMethod;
|
|
889
|
+
if (schema.additionalProperties === false) {
|
|
890
|
+
objectMethod = "z.strictObject";
|
|
891
|
+
} else {
|
|
892
|
+
switch (context.mode) {
|
|
893
|
+
case "strict":
|
|
894
|
+
objectMethod = "z.strictObject";
|
|
895
|
+
break;
|
|
896
|
+
case "loose":
|
|
897
|
+
objectMethod = "z.looseObject";
|
|
898
|
+
break;
|
|
899
|
+
default:
|
|
900
|
+
objectMethod = "z.object";
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
let objectDef = `${objectMethod}({
|
|
904
|
+
${properties.join(",\n")}
|
|
905
|
+
})`;
|
|
906
|
+
if (schema.additionalProperties !== void 0) {
|
|
907
|
+
if (typeof schema.additionalProperties === "object") {
|
|
908
|
+
const additionalSchema = context.generatePropertySchema(schema.additionalProperties, currentSchema);
|
|
909
|
+
objectDef += `.catchall(${additionalSchema})`;
|
|
910
|
+
} else if (schema.additionalProperties === true) {
|
|
911
|
+
objectDef += ".catchall(z.unknown())";
|
|
912
|
+
}
|
|
913
|
+
} else if (schema.patternProperties) {
|
|
914
|
+
objectDef += ".catchall(z.unknown())";
|
|
915
|
+
}
|
|
916
|
+
if (schema.minProperties !== void 0 || schema.maxProperties !== void 0) {
|
|
917
|
+
const conditions = [];
|
|
918
|
+
if (schema.minProperties !== void 0) {
|
|
919
|
+
conditions.push(`Object.keys(obj).length >= ${schema.minProperties}`);
|
|
920
|
+
}
|
|
921
|
+
if (schema.maxProperties !== void 0) {
|
|
922
|
+
conditions.push(`Object.keys(obj).length <= ${schema.maxProperties}`);
|
|
923
|
+
}
|
|
924
|
+
const condition = conditions.join(" && ");
|
|
925
|
+
let message = "Object ";
|
|
926
|
+
if (schema.minProperties !== void 0 && schema.maxProperties !== void 0) {
|
|
927
|
+
message += `must have between ${schema.minProperties} and ${schema.maxProperties} properties`;
|
|
928
|
+
} else if (schema.minProperties !== void 0) {
|
|
929
|
+
message += `must have at least ${schema.minProperties} ${schema.minProperties === 1 ? "property" : "properties"}`;
|
|
930
|
+
} else {
|
|
931
|
+
message += `must have at most ${schema.maxProperties} ${schema.maxProperties === 1 ? "property" : "properties"}`;
|
|
932
|
+
}
|
|
933
|
+
objectDef += `.refine((obj) => ${condition}, { message: "${message}" })`;
|
|
934
|
+
}
|
|
935
|
+
const definedProps = new Set(Object.keys(schema.properties || {}));
|
|
936
|
+
const undefinedRequired = (schema.required || []).filter((prop) => !definedProps.has(prop));
|
|
937
|
+
if (undefinedRequired.length > 0) {
|
|
938
|
+
if (!objectDef.includes(".catchall(")) {
|
|
939
|
+
objectDef += ".catchall(z.unknown())";
|
|
940
|
+
}
|
|
941
|
+
const requiredChecks = undefinedRequired.map((prop) => `${generatePropertyAccess2(prop)} !== undefined`).join(" && ");
|
|
942
|
+
const propList = undefinedRequired.join(", ");
|
|
943
|
+
objectDef += `.refine((obj) => ${requiredChecks}, { message: "Missing required fields: ${propList}" })`;
|
|
944
|
+
}
|
|
945
|
+
if (schema.patternProperties) {
|
|
946
|
+
const definedProps2 = Object.keys(schema.properties || {});
|
|
947
|
+
const definedPropsSet = `new Set(${JSON.stringify(definedProps2)})`;
|
|
948
|
+
const patterns = Object.entries(schema.patternProperties);
|
|
949
|
+
const patternSchemas = patterns.map(([pattern, patternSchema]) => ({
|
|
950
|
+
pattern,
|
|
951
|
+
escapedPattern: pattern.replace(/\\/g, "\\\\").replace(/'/g, "\\'"),
|
|
952
|
+
zodSchema: context.generatePropertySchema(patternSchema, currentSchema)
|
|
953
|
+
}));
|
|
954
|
+
objectDef += `.superRefine((obj, ctx) => {
|
|
955
|
+
const definedPropsSet = ${definedPropsSet};
|
|
956
|
+
const patterns = ${JSON.stringify(patternSchemas.map((p) => ({ pattern: p.escapedPattern })))};
|
|
957
|
+
const schemas = [${patternSchemas.map((p) => p.zodSchema).join(", ")}];
|
|
958
|
+
const regexps = patterns.map(p => new RegExp(p.pattern));
|
|
959
|
+
|
|
960
|
+
// Check all object keys
|
|
961
|
+
for (const key of Object.keys(obj)) {
|
|
962
|
+
// Skip properties that are explicitly defined
|
|
963
|
+
if (definedPropsSet.has(key)) continue;
|
|
964
|
+
|
|
965
|
+
// Find first matching pattern (first-match-wins priority)
|
|
966
|
+
for (let i = 0; i < regexps.length; i++) {
|
|
967
|
+
if (regexps[i].test(key)) {
|
|
968
|
+
const validation = schemas[i].safeParse(obj[key]);
|
|
969
|
+
if (!validation.success) {
|
|
970
|
+
// Add detailed error messages with property name and pattern
|
|
971
|
+
for (const issue of validation.error.issues) {
|
|
972
|
+
ctx.addIssue({
|
|
973
|
+
...issue,
|
|
974
|
+
path: [key, ...issue.path],
|
|
975
|
+
message: \`Property '\${key}' (pattern '\${patterns[i].pattern}'): \${issue.message}\`
|
|
976
|
+
});
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
break; // First match wins, stop checking other patterns
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
})`;
|
|
984
|
+
}
|
|
985
|
+
if (schema.propertyNames) {
|
|
986
|
+
const hasPattern = schema.propertyNames.pattern !== void 0;
|
|
987
|
+
const hasMinLength = schema.propertyNames.minLength !== void 0;
|
|
988
|
+
const hasMaxLength = schema.propertyNames.maxLength !== void 0;
|
|
989
|
+
if (hasPattern || hasMinLength || hasMaxLength) {
|
|
990
|
+
const escapedPattern = hasPattern && schema.propertyNames.pattern ? schema.propertyNames.pattern.replace(/\\/g, "\\\\").replace(/'/g, "\\'") : null;
|
|
991
|
+
const minLen = schema.propertyNames.minLength;
|
|
992
|
+
const maxLen = schema.propertyNames.maxLength;
|
|
993
|
+
objectDef += `.superRefine((obj, ctx) => {
|
|
994
|
+
${escapedPattern ? `const pattern = /${escapedPattern}/;` : ""}
|
|
995
|
+
|
|
996
|
+
for (const key of Object.keys(obj)) {
|
|
997
|
+
const failures: string[] = [];
|
|
998
|
+
|
|
999
|
+
${hasPattern ? `
|
|
1000
|
+
if (!pattern.test(key)) {
|
|
1001
|
+
failures.push("must match pattern '${schema.propertyNames.pattern}'");
|
|
1002
|
+
}
|
|
1003
|
+
` : ""}
|
|
1004
|
+
|
|
1005
|
+
${hasMinLength ? `
|
|
1006
|
+
if (key.length < ${minLen}) {
|
|
1007
|
+
failures.push("must be at least ${minLen} characters");
|
|
1008
|
+
}
|
|
1009
|
+
` : ""}
|
|
1010
|
+
|
|
1011
|
+
${hasMaxLength ? `
|
|
1012
|
+
if (key.length > ${maxLen}) {
|
|
1013
|
+
failures.push("must be at most ${maxLen} characters");
|
|
1014
|
+
}
|
|
1015
|
+
` : ""}
|
|
1016
|
+
|
|
1017
|
+
if (failures.length > 0) {
|
|
1018
|
+
ctx.addIssue({
|
|
1019
|
+
code: "custom",
|
|
1020
|
+
message: \`Property name '\${key}' \${failures.join(", ")}\`,
|
|
1021
|
+
path: [key]
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
})`;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
objectDef += generateDependencies(schema, context.generatePropertySchema, currentSchema);
|
|
1029
|
+
objectDef += generateDependentRequired(schema);
|
|
1030
|
+
objectDef += generateIfThenElse(schema);
|
|
1031
|
+
return objectDef;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// src/validators/string-validator.ts
|
|
1035
|
+
var DEFAULT_FORMAT_MAP = {
|
|
1036
|
+
uuid: "z.uuid()",
|
|
1037
|
+
email: "z.email()",
|
|
1038
|
+
uri: "z.url()",
|
|
1039
|
+
url: "z.url()",
|
|
1040
|
+
"uri-reference": 'z.string().refine((val) => !/\\s/.test(val), { message: "Must be a valid URI reference" })',
|
|
1041
|
+
hostname: 'z.string().refine((val) => /^(?=.{1,253}$)(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)*(?!-)[A-Za-z0-9-]{1,63}(?<!-)$/.test(val), { message: "Must be a valid hostname" })',
|
|
1042
|
+
byte: "z.base64()",
|
|
1043
|
+
binary: "z.string()",
|
|
1044
|
+
date: "z.iso.date()",
|
|
1045
|
+
time: "z.iso.time()",
|
|
1046
|
+
duration: 'z.string().refine((val) => /^P(?:(?:\\d+Y)?(?:\\d+M)?(?:\\d+D)?(?:T(?:\\d+H)?(?:\\d+M)?(?:\\d+(?:\\.\\d+)?S)?)?|\\d+W)$/.test(val) && !/^PT?$/.test(val), { message: "Must be a valid ISO 8601 duration" })',
|
|
1047
|
+
ipv4: "z.ipv4()",
|
|
1048
|
+
ipv6: "z.ipv6()",
|
|
1049
|
+
emoji: "z.emoji()",
|
|
1050
|
+
base64: "z.base64()",
|
|
1051
|
+
base64url: "z.base64url()",
|
|
1052
|
+
nanoid: "z.nanoid()",
|
|
1053
|
+
cuid: "z.cuid()",
|
|
1054
|
+
cuid2: "z.cuid2()",
|
|
1055
|
+
ulid: "z.ulid()",
|
|
1056
|
+
cidr: "z.cidrv4()",
|
|
1057
|
+
// Default to v4
|
|
1058
|
+
cidrv4: "z.cidrv4()",
|
|
1059
|
+
cidrv6: "z.cidrv6()",
|
|
1060
|
+
"json-pointer": 'z.string().refine((val) => val === "" || /^(\\/([^~/]|~0|~1)+)+$/.test(val), { message: "Must be a valid JSON Pointer (RFC 6901)" })',
|
|
1061
|
+
"relative-json-pointer": 'z.string().refine((val) => /^(0|[1-9]\\d*)(#|(\\/([^~/]|~0|~1)+)*)$/.test(val), { message: "Must be a valid relative JSON Pointer" })'
|
|
1062
|
+
};
|
|
1063
|
+
function buildDateTimeValidation(pattern) {
|
|
1064
|
+
if (!pattern) {
|
|
1065
|
+
return "z.iso.datetime()";
|
|
1066
|
+
}
|
|
1067
|
+
const patternStr = pattern instanceof RegExp ? pattern.source : pattern;
|
|
1068
|
+
if (patternStr === "") {
|
|
1069
|
+
return "z.iso.datetime()";
|
|
1070
|
+
}
|
|
1071
|
+
try {
|
|
1072
|
+
new RegExp(patternStr);
|
|
1073
|
+
} catch (error) {
|
|
1074
|
+
throw new Error(
|
|
1075
|
+
`Invalid regular expression pattern for customDateTimeFormatRegex: ${patternStr}. ${error instanceof Error ? error.message : "Pattern is malformed"}`
|
|
1076
|
+
);
|
|
1077
|
+
}
|
|
1078
|
+
const escapedPattern = escapePattern(patternStr);
|
|
1079
|
+
return `z.string().regex(/${escapedPattern}/)`;
|
|
1080
|
+
}
|
|
1081
|
+
function generateStringValidation(schema, useDescribe, context) {
|
|
1082
|
+
let validation;
|
|
1083
|
+
const format = schema.format || "";
|
|
1084
|
+
if (format === "date-time") {
|
|
1085
|
+
validation = context.dateTimeValidation;
|
|
1086
|
+
} else {
|
|
1087
|
+
validation = DEFAULT_FORMAT_MAP[format] || "z.string()";
|
|
1088
|
+
}
|
|
1089
|
+
if (schema.minLength !== void 0) {
|
|
1090
|
+
validation += `.min(${schema.minLength})`;
|
|
1091
|
+
}
|
|
1092
|
+
if (schema.maxLength !== void 0) {
|
|
1093
|
+
validation += `.max(${schema.maxLength})`;
|
|
1094
|
+
}
|
|
1095
|
+
if (schema.pattern) {
|
|
1096
|
+
let escapedPattern = context.patternCache.get(schema.pattern);
|
|
1097
|
+
if (escapedPattern === void 0) {
|
|
1098
|
+
escapedPattern = escapePattern(schema.pattern);
|
|
1099
|
+
context.patternCache.set(schema.pattern, escapedPattern);
|
|
1100
|
+
}
|
|
1101
|
+
validation += `.regex(/${escapedPattern}/)`;
|
|
1102
|
+
}
|
|
1103
|
+
if (schema.contentEncoding && !schema.format) {
|
|
1104
|
+
switch (schema.contentEncoding) {
|
|
1105
|
+
case "base64":
|
|
1106
|
+
validation = "z.base64()";
|
|
1107
|
+
break;
|
|
1108
|
+
case "base64url":
|
|
1109
|
+
validation = "z.base64url()";
|
|
1110
|
+
break;
|
|
1111
|
+
case "quoted-printable":
|
|
1112
|
+
validation = 'z.string().refine((val) => /^[\\x20-\\x7E\\r\\n=]*$/.test(val), { message: "Must be valid quoted-printable encoding" })';
|
|
1113
|
+
break;
|
|
1114
|
+
case "7bit":
|
|
1115
|
+
case "8bit":
|
|
1116
|
+
case "binary":
|
|
1117
|
+
validation = "z.string()";
|
|
1118
|
+
break;
|
|
1119
|
+
default:
|
|
1120
|
+
validation = `z.string().describe("Content encoding: ${schema.contentEncoding}")`;
|
|
1121
|
+
}
|
|
1122
|
+
if (schema.minLength !== void 0) {
|
|
1123
|
+
validation += `.min(${schema.minLength})`;
|
|
1124
|
+
}
|
|
1125
|
+
if (schema.maxLength !== void 0) {
|
|
1126
|
+
validation += `.max(${schema.maxLength})`;
|
|
1127
|
+
}
|
|
1128
|
+
if (schema.pattern) {
|
|
1129
|
+
let escapedPattern = context.patternCache.get(schema.pattern);
|
|
1130
|
+
if (escapedPattern === void 0) {
|
|
1131
|
+
escapedPattern = escapePattern(schema.pattern);
|
|
1132
|
+
context.patternCache.set(schema.pattern, escapedPattern);
|
|
1133
|
+
}
|
|
1134
|
+
validation += `.regex(/${escapedPattern}/)`;
|
|
1135
|
+
}
|
|
1136
|
+
} else if (schema.contentMediaType) {
|
|
1137
|
+
const mediaType = schema.contentMediaType;
|
|
1138
|
+
if (mediaType === "application/json") {
|
|
1139
|
+
validation += `.refine((val) => { try { JSON.parse(val); return true; } catch { return false; } }, { message: "Must be valid JSON" })`;
|
|
1140
|
+
} else if (mediaType === "application/xml" || mediaType === "text/xml") {
|
|
1141
|
+
validation += `.refine((val) => { try { if (typeof DOMParser !== "undefined") { const parser = new DOMParser(); const doc = parser.parseFromString(val, "text/xml"); return !doc.querySelector("parsererror"); } return /^\\s*<[^>]+>/.test(val); } catch { return false; } }, { message: "Must be valid XML" })`;
|
|
1142
|
+
} else if (mediaType === "application/yaml" || mediaType === "application/x-yaml" || mediaType === "text/yaml") {
|
|
1143
|
+
validation += `.refine((val) => { try { return val.trim().length > 0 && !/^[[{]/.test(val.trim()); } catch { return false; } }, { message: "Must be valid YAML" })`;
|
|
1144
|
+
} else if (mediaType === "text/html") {
|
|
1145
|
+
validation += `.refine((val) => /<[^>]+>/.test(val), { message: "Must contain HTML tags" })`;
|
|
1146
|
+
} else if (mediaType === "text/plain") {
|
|
1147
|
+
validation += `.refine(() => true, { message: "Plain text content" })`;
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
return addDescription(validation, schema.description, useDescribe);
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// src/generators/property-generator.ts
|
|
1154
|
+
var _PropertyGenerator = class _PropertyGenerator {
|
|
1155
|
+
constructor(context) {
|
|
1156
|
+
// Performance optimization: Memoize filtered property results
|
|
1157
|
+
this.filteredPropsCache = /* @__PURE__ */ new Map();
|
|
1158
|
+
// Performance optimization: LRU cache for generated schemas
|
|
1159
|
+
this.schemaCache = new LRUCache(500);
|
|
1160
|
+
this.context = context;
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Check if a property should be included based on schemaType and readOnly/writeOnly flags
|
|
1164
|
+
*/
|
|
1165
|
+
shouldIncludeProperty(schema) {
|
|
1166
|
+
const rule = _PropertyGenerator.INCLUSION_RULES[this.context.schemaType];
|
|
1167
|
+
return rule(schema);
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* Recursively filter any schema type (helper for composition schemas)
|
|
1171
|
+
*/
|
|
1172
|
+
filterSchemaRecursive(schema) {
|
|
1173
|
+
if (schema.$ref) {
|
|
1174
|
+
return schema;
|
|
1175
|
+
}
|
|
1176
|
+
if (schema.properties) {
|
|
1177
|
+
return this.filterNestedProperties(schema);
|
|
1178
|
+
}
|
|
1179
|
+
if (schema.type === "array" && schema.items && typeof schema.items === "object" && schema.items.properties) {
|
|
1180
|
+
return {
|
|
1181
|
+
...schema,
|
|
1182
|
+
items: this.filterNestedProperties(schema.items)
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
return schema;
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Recursively filter properties in nested objects based on readOnly/writeOnly
|
|
1189
|
+
* Performance optimized with memoization
|
|
1190
|
+
*/
|
|
1191
|
+
filterNestedProperties(schema) {
|
|
1192
|
+
var _a, _b;
|
|
1193
|
+
const propKeys = schema.properties ? Object.keys(schema.properties).sort().join(",") : "";
|
|
1194
|
+
const cacheKey = `${this.context.schemaType}:${schema.type || "unknown"}:${propKeys}:${((_a = schema.required) == null ? void 0 : _a.join(",")) || ""}`;
|
|
1195
|
+
const cached = this.filteredPropsCache.get(cacheKey);
|
|
1196
|
+
if (cached) {
|
|
1197
|
+
return cached;
|
|
1198
|
+
}
|
|
1199
|
+
if (!schema.properties) {
|
|
1200
|
+
return schema;
|
|
1201
|
+
}
|
|
1202
|
+
const filteredProperties = {};
|
|
1203
|
+
const filteredRequired = [];
|
|
1204
|
+
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
1205
|
+
if (!this.shouldIncludeProperty(propSchema)) {
|
|
1206
|
+
continue;
|
|
1207
|
+
}
|
|
1208
|
+
let filteredPropSchema = propSchema;
|
|
1209
|
+
if (propSchema.type === "object" && propSchema.properties) {
|
|
1210
|
+
filteredPropSchema = this.filterNestedProperties(propSchema);
|
|
1211
|
+
} else if (propSchema.type === "array" && propSchema.items && typeof propSchema.items === "object" && propSchema.items.properties) {
|
|
1212
|
+
filteredPropSchema = {
|
|
1213
|
+
...propSchema,
|
|
1214
|
+
items: this.filterNestedProperties(propSchema.items)
|
|
1215
|
+
};
|
|
1216
|
+
} else if (propSchema.allOf || propSchema.oneOf || propSchema.anyOf) {
|
|
1217
|
+
if (propSchema.allOf) {
|
|
1218
|
+
filteredPropSchema = {
|
|
1219
|
+
...propSchema,
|
|
1220
|
+
allOf: propSchema.allOf.map((s) => this.filterSchemaRecursive(s))
|
|
1221
|
+
};
|
|
1222
|
+
} else if (propSchema.oneOf) {
|
|
1223
|
+
filteredPropSchema = {
|
|
1224
|
+
...propSchema,
|
|
1225
|
+
oneOf: propSchema.oneOf.map((s) => this.filterSchemaRecursive(s))
|
|
1226
|
+
};
|
|
1227
|
+
} else if (propSchema.anyOf) {
|
|
1228
|
+
filteredPropSchema = {
|
|
1229
|
+
...propSchema,
|
|
1230
|
+
anyOf: propSchema.anyOf.map((s) => this.filterSchemaRecursive(s))
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
filteredProperties[propName] = filteredPropSchema;
|
|
1235
|
+
if ((_b = schema.required) == null ? void 0 : _b.includes(propName)) {
|
|
1236
|
+
filteredRequired.push(propName);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
const result = {
|
|
1240
|
+
...schema,
|
|
1241
|
+
properties: filteredProperties,
|
|
1242
|
+
required: filteredRequired.length > 0 ? filteredRequired : void 0
|
|
1243
|
+
};
|
|
1244
|
+
this.filteredPropsCache.set(cacheKey, result);
|
|
1245
|
+
return result;
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Resolve discriminator mapping to actual schema references
|
|
1249
|
+
*/
|
|
1250
|
+
resolveDiscriminatorMapping(mapping, schemas) {
|
|
1251
|
+
const mappedSchemas = [];
|
|
1252
|
+
for (const [_, schemaRef] of Object.entries(mapping)) {
|
|
1253
|
+
const matchingSchema = schemas.find((s) => {
|
|
1254
|
+
if (s.$ref) {
|
|
1255
|
+
return s.$ref === schemaRef || s.$ref.endsWith(schemaRef);
|
|
1256
|
+
}
|
|
1257
|
+
return false;
|
|
1258
|
+
});
|
|
1259
|
+
if (matchingSchema) {
|
|
1260
|
+
mappedSchemas.push(matchingSchema);
|
|
1261
|
+
} else {
|
|
1262
|
+
mappedSchemas.push({ $ref: schemaRef });
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
for (const schema of schemas) {
|
|
1266
|
+
if (!mappedSchemas.includes(schema)) {
|
|
1267
|
+
mappedSchemas.push(schema);
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
return mappedSchemas;
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Resolve a $ref string to the actual schema
|
|
1274
|
+
*/
|
|
1275
|
+
resolveSchemaRef(ref) {
|
|
1276
|
+
var _a, _b;
|
|
1277
|
+
const schemaName = ref.split("/").pop();
|
|
1278
|
+
if (!schemaName) return void 0;
|
|
1279
|
+
return (_b = (_a = this.context.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[schemaName];
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Resolve a schema name through any aliases to get the actual schema name
|
|
1283
|
+
* If the schema is an alias (allOf with single $ref), return the target name
|
|
1284
|
+
*/
|
|
1285
|
+
resolveSchemaAlias(schemaName) {
|
|
1286
|
+
var _a, _b;
|
|
1287
|
+
const schema = (_b = (_a = this.context.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[schemaName];
|
|
1288
|
+
if (!schema) return schemaName;
|
|
1289
|
+
if (schema.allOf && schema.allOf.length === 1 && schema.allOf[0].$ref && !schema.properties && !schema.oneOf && !schema.anyOf) {
|
|
1290
|
+
const targetName = resolveRef(schema.allOf[0].$ref);
|
|
1291
|
+
return this.resolveSchemaAlias(targetName);
|
|
1292
|
+
}
|
|
1293
|
+
return schemaName;
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Check if this is a circular dependency through aliases
|
|
1297
|
+
*/
|
|
1298
|
+
isCircularThroughAlias(fromSchema, toSchema) {
|
|
1299
|
+
var _a, _b;
|
|
1300
|
+
const toSchemaSpec = (_b = (_a = this.context.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[toSchema];
|
|
1301
|
+
if (!toSchemaSpec) return false;
|
|
1302
|
+
if (toSchemaSpec.allOf && toSchemaSpec.allOf.length === 1 && toSchemaSpec.allOf[0].$ref) {
|
|
1303
|
+
const aliasTarget = resolveRef(toSchemaSpec.allOf[0].$ref);
|
|
1304
|
+
return aliasTarget === fromSchema;
|
|
1305
|
+
}
|
|
1306
|
+
return false;
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* Generate union for multiple types (OpenAPI 3.1)
|
|
1310
|
+
*/
|
|
1311
|
+
generateMultiTypeUnion(schema, currentSchema) {
|
|
1312
|
+
if (!Array.isArray(schema.type)) {
|
|
1313
|
+
return "z.unknown()";
|
|
1314
|
+
}
|
|
1315
|
+
const nonNullTypes = schema.type.filter((t) => t !== "null");
|
|
1316
|
+
const schemas = nonNullTypes.map((type) => {
|
|
1317
|
+
const typeSchema = { ...schema, type };
|
|
1318
|
+
return this.generatePropertySchema(typeSchema, currentSchema);
|
|
1319
|
+
});
|
|
1320
|
+
return `z.union([${schemas.join(", ")}])`;
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Apply unevaluatedProperties validation to a schema
|
|
1324
|
+
*/
|
|
1325
|
+
applyUnevaluatedProperties(baseSchema, schema) {
|
|
1326
|
+
const evaluatedProps = /* @__PURE__ */ new Set();
|
|
1327
|
+
if (schema.properties) {
|
|
1328
|
+
for (const propName of Object.keys(schema.properties)) {
|
|
1329
|
+
evaluatedProps.add(propName);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
const collectPropsFromComposition = (schemas) => {
|
|
1333
|
+
var _a, _b;
|
|
1334
|
+
if (!schemas) return;
|
|
1335
|
+
for (const subSchema of schemas) {
|
|
1336
|
+
if (subSchema.properties) {
|
|
1337
|
+
for (const propName of Object.keys(subSchema.properties)) {
|
|
1338
|
+
evaluatedProps.add(propName);
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
if (subSchema.$ref) {
|
|
1342
|
+
const refSchema = (_b = (_a = this.context.spec.components) == null ? void 0 : _a.schemas) == null ? void 0 : _b[subSchema.$ref.split("/").pop() || ""];
|
|
1343
|
+
if (refSchema == null ? void 0 : refSchema.properties) {
|
|
1344
|
+
for (const propName of Object.keys(refSchema.properties)) {
|
|
1345
|
+
evaluatedProps.add(propName);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
collectPropsFromComposition(schema.allOf);
|
|
1352
|
+
collectPropsFromComposition(schema.oneOf);
|
|
1353
|
+
collectPropsFromComposition(schema.anyOf);
|
|
1354
|
+
const evaluatedPropsSet = `new Set(${JSON.stringify([...evaluatedProps])})`;
|
|
1355
|
+
let schemaWithCatchall = baseSchema;
|
|
1356
|
+
if (baseSchema.includes(".union([") || baseSchema.includes(".discriminatedUnion(")) {
|
|
1357
|
+
schemaWithCatchall = baseSchema;
|
|
1358
|
+
} else if (baseSchema.includes(".extend(")) {
|
|
1359
|
+
schemaWithCatchall = `${baseSchema}.catchall(z.unknown())`;
|
|
1360
|
+
}
|
|
1361
|
+
if (schema.unevaluatedProperties === false) {
|
|
1362
|
+
return `${schemaWithCatchall}.refine((obj) => Object.keys(obj).every(key => ${evaluatedPropsSet}.has(key)), { message: "No unevaluated properties allowed" })`;
|
|
1363
|
+
} else if (typeof schema.unevaluatedProperties === "object") {
|
|
1364
|
+
const unevalSchema = this.generatePropertySchema(schema.unevaluatedProperties);
|
|
1365
|
+
return `${schemaWithCatchall}.refine((obj) => Object.keys(obj).filter(key => !${evaluatedPropsSet}.has(key)).every(key => ${unevalSchema}.safeParse(obj[key]).success), { message: "Unevaluated properties must match the schema" })`;
|
|
1366
|
+
}
|
|
1367
|
+
return baseSchema;
|
|
1368
|
+
}
|
|
1369
|
+
/**
|
|
1370
|
+
* Generate Zod schema for a property
|
|
1371
|
+
* @param schema - The OpenAPI schema to generate
|
|
1372
|
+
* @param currentSchema - The name of the current schema being processed (for circular ref detection)
|
|
1373
|
+
* @param isTopLevel - Whether this is a top-level schema definition
|
|
1374
|
+
* @param suppressDefaultNullable - When true, don't apply defaultNullable (used when outer schema has explicit nullable: false)
|
|
1375
|
+
*/
|
|
1376
|
+
generatePropertySchema(schema, currentSchema, isTopLevel = false, suppressDefaultNullable = false) {
|
|
1377
|
+
var _a, _b, _c, _d, _e;
|
|
1378
|
+
const isCacheable = !schema.$ref && !schema.allOf && !schema.oneOf && !schema.anyOf && !currentSchema;
|
|
1379
|
+
if (isCacheable) {
|
|
1380
|
+
const cacheKey = JSON.stringify({
|
|
1381
|
+
schema,
|
|
1382
|
+
type: this.context.schemaType,
|
|
1383
|
+
mode: this.context.mode,
|
|
1384
|
+
suppressDefaultNullable
|
|
1385
|
+
});
|
|
1386
|
+
const cached = this.schemaCache.get(cacheKey);
|
|
1387
|
+
if (cached) {
|
|
1388
|
+
return cached;
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
if ((this.context.schemaType === "request" || this.context.schemaType === "response") && schema.properties) {
|
|
1392
|
+
schema = this.filterNestedProperties(schema);
|
|
1393
|
+
}
|
|
1394
|
+
const isEnum = !!schema.enum;
|
|
1395
|
+
const isConst = schema.const !== void 0;
|
|
1396
|
+
const shouldApplyDefaultNullable = !isTopLevel && !isEnum && !isConst && !suppressDefaultNullable;
|
|
1397
|
+
const effectiveDefaultNullable = shouldApplyDefaultNullable ? this.context.defaultNullable : false;
|
|
1398
|
+
const nullable = isNullable(schema, effectiveDefaultNullable);
|
|
1399
|
+
if (hasMultipleTypes(schema)) {
|
|
1400
|
+
const union = this.generateMultiTypeUnion(schema, currentSchema);
|
|
1401
|
+
return wrapNullable(union, nullable);
|
|
1402
|
+
}
|
|
1403
|
+
if (schema.$ref) {
|
|
1404
|
+
const refName = resolveRef(schema.$ref);
|
|
1405
|
+
const resolvedRefName = this.resolveSchemaAlias(refName);
|
|
1406
|
+
if (currentSchema && refName !== currentSchema && !isTopLevel) {
|
|
1407
|
+
if (!this.context.schemaDependencies.has(currentSchema)) {
|
|
1408
|
+
this.context.schemaDependencies.set(currentSchema, /* @__PURE__ */ new Set());
|
|
1409
|
+
}
|
|
1410
|
+
(_a = this.context.schemaDependencies.get(currentSchema)) == null ? void 0 : _a.add(refName);
|
|
1411
|
+
}
|
|
1412
|
+
const strippedRefName = stripPrefix(resolvedRefName, this.context.stripSchemaPrefix);
|
|
1413
|
+
const schemaName = `${toCamelCase(strippedRefName, this.context.namingOptions)}Schema`;
|
|
1414
|
+
if (currentSchema && (refName === currentSchema || this.isCircularThroughAlias(currentSchema, refName))) {
|
|
1415
|
+
const lazySchema = `z.lazy((): z.ZodTypeAny => ${schemaName})`;
|
|
1416
|
+
return wrapNullable(lazySchema, nullable);
|
|
1417
|
+
}
|
|
1418
|
+
return wrapNullable(schemaName, nullable);
|
|
1419
|
+
}
|
|
1420
|
+
if (schema.const !== void 0) {
|
|
1421
|
+
const literalValue = typeof schema.const === "string" ? `"${schema.const}"` : schema.const;
|
|
1422
|
+
const zodLiteral = `z.literal(${literalValue})`;
|
|
1423
|
+
return wrapNullable(zodLiteral, nullable);
|
|
1424
|
+
}
|
|
1425
|
+
if (schema.enum) {
|
|
1426
|
+
const allBooleans = schema.enum.every((v) => typeof v === "boolean");
|
|
1427
|
+
if (allBooleans) {
|
|
1428
|
+
const zodBoolean = "z.boolean()";
|
|
1429
|
+
return wrapNullable(zodBoolean, nullable);
|
|
1430
|
+
}
|
|
1431
|
+
const allStrings = schema.enum.every((v) => typeof v === "string");
|
|
1432
|
+
if (allStrings) {
|
|
1433
|
+
const enumValues = schema.enum.map((v) => `"${v}"`).join(", ");
|
|
1434
|
+
const zodEnum = `z.enum([${enumValues}])`;
|
|
1435
|
+
return wrapNullable(zodEnum, nullable);
|
|
1436
|
+
}
|
|
1437
|
+
const literalValues = schema.enum.map((v) => {
|
|
1438
|
+
if (typeof v === "string") {
|
|
1439
|
+
return `z.literal("${v}")`;
|
|
1440
|
+
}
|
|
1441
|
+
return `z.literal(${v})`;
|
|
1442
|
+
}).join(", ");
|
|
1443
|
+
const zodUnion = `z.union([${literalValues}])`;
|
|
1444
|
+
return wrapNullable(zodUnion, nullable);
|
|
1445
|
+
}
|
|
1446
|
+
if (schema.allOf) {
|
|
1447
|
+
const compositionNullable = isNullable(schema, false);
|
|
1448
|
+
let composition = generateAllOf(
|
|
1449
|
+
schema.allOf,
|
|
1450
|
+
compositionNullable,
|
|
1451
|
+
{
|
|
1452
|
+
generatePropertySchema: this.generatePropertySchema.bind(this),
|
|
1453
|
+
generateInlineObjectShape: this.generateInlineObjectShape.bind(this),
|
|
1454
|
+
resolveSchemaRef: this.resolveSchemaRef.bind(this)
|
|
1455
|
+
},
|
|
1456
|
+
currentSchema
|
|
1457
|
+
);
|
|
1458
|
+
if (schema.unevaluatedProperties !== void 0) {
|
|
1459
|
+
composition = this.applyUnevaluatedProperties(composition, schema);
|
|
1460
|
+
}
|
|
1461
|
+
return composition;
|
|
1462
|
+
}
|
|
1463
|
+
if (schema.oneOf) {
|
|
1464
|
+
const compositionNullable = isNullable(schema, false);
|
|
1465
|
+
const needsPassthrough = schema.unevaluatedProperties !== void 0;
|
|
1466
|
+
let composition = generateUnion(
|
|
1467
|
+
schema.oneOf,
|
|
1468
|
+
(_b = schema.discriminator) == null ? void 0 : _b.propertyName,
|
|
1469
|
+
compositionNullable,
|
|
1470
|
+
{
|
|
1471
|
+
generatePropertySchema: this.generatePropertySchema.bind(this),
|
|
1472
|
+
resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this),
|
|
1473
|
+
resolveSchemaRef: this.resolveSchemaRef.bind(this)
|
|
1474
|
+
},
|
|
1475
|
+
{
|
|
1476
|
+
passthrough: needsPassthrough,
|
|
1477
|
+
discriminatorMapping: (_c = schema.discriminator) == null ? void 0 : _c.mapping
|
|
1478
|
+
},
|
|
1479
|
+
currentSchema
|
|
1480
|
+
);
|
|
1481
|
+
if (schema.unevaluatedProperties !== void 0) {
|
|
1482
|
+
composition = this.applyUnevaluatedProperties(composition, schema);
|
|
1483
|
+
}
|
|
1484
|
+
return composition;
|
|
1485
|
+
}
|
|
1486
|
+
if (schema.anyOf) {
|
|
1487
|
+
const compositionNullable = isNullable(schema, false);
|
|
1488
|
+
const needsPassthrough = schema.unevaluatedProperties !== void 0;
|
|
1489
|
+
let composition = generateUnion(
|
|
1490
|
+
schema.anyOf,
|
|
1491
|
+
(_d = schema.discriminator) == null ? void 0 : _d.propertyName,
|
|
1492
|
+
compositionNullable,
|
|
1493
|
+
{
|
|
1494
|
+
generatePropertySchema: this.generatePropertySchema.bind(this),
|
|
1495
|
+
resolveDiscriminatorMapping: this.resolveDiscriminatorMapping.bind(this),
|
|
1496
|
+
resolveSchemaRef: this.resolveSchemaRef.bind(this)
|
|
1497
|
+
},
|
|
1498
|
+
{
|
|
1499
|
+
passthrough: needsPassthrough,
|
|
1500
|
+
discriminatorMapping: (_e = schema.discriminator) == null ? void 0 : _e.mapping
|
|
1501
|
+
},
|
|
1502
|
+
currentSchema
|
|
1503
|
+
);
|
|
1504
|
+
if (schema.unevaluatedProperties !== void 0) {
|
|
1505
|
+
composition = this.applyUnevaluatedProperties(composition, schema);
|
|
1506
|
+
}
|
|
1507
|
+
return composition;
|
|
1508
|
+
}
|
|
1509
|
+
if (schema.not) {
|
|
1510
|
+
const notSchema = this.generatePropertySchema(schema.not, currentSchema);
|
|
1511
|
+
let baseValidation;
|
|
1512
|
+
if (schema.type || schema.properties || schema.items) {
|
|
1513
|
+
const { not: _, ...baseSchema } = schema;
|
|
1514
|
+
baseValidation = this.generatePropertySchema(baseSchema, currentSchema);
|
|
1515
|
+
} else {
|
|
1516
|
+
baseValidation = "z.unknown()";
|
|
1517
|
+
}
|
|
1518
|
+
const refined = `${baseValidation}.refine((val) => !${notSchema}.safeParse(val).success, { message: "Value must not match the excluded schema" })`;
|
|
1519
|
+
return wrapNullable(refined, nullable);
|
|
1520
|
+
}
|
|
1521
|
+
let validation = "";
|
|
1522
|
+
const primaryType = getPrimaryType(schema);
|
|
1523
|
+
switch (primaryType) {
|
|
1524
|
+
case "string":
|
|
1525
|
+
validation = generateStringValidation(schema, this.context.useDescribe, {
|
|
1526
|
+
dateTimeValidation: this.context.dateTimeValidation,
|
|
1527
|
+
patternCache: this.context.patternCache
|
|
1528
|
+
});
|
|
1529
|
+
break;
|
|
1530
|
+
case "number":
|
|
1531
|
+
validation = generateNumberValidation(schema, false, this.context.useDescribe);
|
|
1532
|
+
break;
|
|
1533
|
+
case "integer":
|
|
1534
|
+
validation = generateNumberValidation(schema, true, this.context.useDescribe);
|
|
1535
|
+
break;
|
|
1536
|
+
case "boolean":
|
|
1537
|
+
validation = "z.boolean()";
|
|
1538
|
+
validation = addDescription(validation, schema.description, this.context.useDescribe);
|
|
1539
|
+
break;
|
|
1540
|
+
case "array":
|
|
1541
|
+
validation = generateArrayValidation(schema, {
|
|
1542
|
+
generatePropertySchema: this.generatePropertySchema.bind(this),
|
|
1543
|
+
useDescribe: this.context.useDescribe,
|
|
1544
|
+
currentSchema
|
|
1545
|
+
});
|
|
1546
|
+
break;
|
|
1547
|
+
case "object":
|
|
1548
|
+
if (schema.properties || schema.required || schema.minProperties !== void 0 || schema.maxProperties !== void 0 || schema.patternProperties || schema.propertyNames) {
|
|
1549
|
+
validation = generateObjectSchema(
|
|
1550
|
+
schema,
|
|
1551
|
+
{
|
|
1552
|
+
generatePropertySchema: this.generatePropertySchema.bind(this),
|
|
1553
|
+
shouldIncludeProperty: this.shouldIncludeProperty.bind(this),
|
|
1554
|
+
mode: this.context.mode,
|
|
1555
|
+
includeDescriptions: this.context.includeDescriptions,
|
|
1556
|
+
useDescribe: this.context.useDescribe
|
|
1557
|
+
},
|
|
1558
|
+
currentSchema
|
|
1559
|
+
);
|
|
1560
|
+
validation = addDescription(validation, schema.description, this.context.useDescribe);
|
|
1561
|
+
} else {
|
|
1562
|
+
switch (this.context.emptyObjectBehavior) {
|
|
1563
|
+
case "strict":
|
|
1564
|
+
validation = "z.strictObject({})";
|
|
1565
|
+
break;
|
|
1566
|
+
case "loose":
|
|
1567
|
+
validation = "z.looseObject({})";
|
|
1568
|
+
break;
|
|
1569
|
+
default:
|
|
1570
|
+
validation = "z.record(z.string(), z.unknown())";
|
|
1571
|
+
break;
|
|
1572
|
+
}
|
|
1573
|
+
validation = addDescription(validation, schema.description, this.context.useDescribe);
|
|
1574
|
+
}
|
|
1575
|
+
break;
|
|
1576
|
+
default:
|
|
1577
|
+
validation = "z.unknown()";
|
|
1578
|
+
validation = addDescription(validation, schema.description, this.context.useDescribe);
|
|
1579
|
+
}
|
|
1580
|
+
const result = wrapNullable(validation, nullable);
|
|
1581
|
+
if (isCacheable) {
|
|
1582
|
+
const cacheKey = JSON.stringify({ schema, type: this.context.schemaType, mode: this.context.mode });
|
|
1583
|
+
this.schemaCache.set(cacheKey, result);
|
|
1584
|
+
}
|
|
1585
|
+
return result;
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* Generate inline object shape for use with .extend()
|
|
1589
|
+
* Returns just the shape object literal: { prop1: z.string(), prop2: z.number() }
|
|
1590
|
+
*
|
|
1591
|
+
* This method is specifically for allOf compositions where we need to pass
|
|
1592
|
+
* the shape directly to .extend() instead of using z.object({...}).shape.
|
|
1593
|
+
* This avoids the .nullable().shape bug when inline objects have nullable: true.
|
|
1594
|
+
*
|
|
1595
|
+
* According to Zod docs (https://zod.dev/api?id=extend):
|
|
1596
|
+
* - .extend() accepts an object of shape definitions
|
|
1597
|
+
* - e.g., baseSchema.extend({ prop: z.string() })
|
|
1598
|
+
*/
|
|
1599
|
+
generateInlineObjectShape(schema, currentSchema) {
|
|
1600
|
+
const required = new Set(schema.required || []);
|
|
1601
|
+
const properties = [];
|
|
1602
|
+
if (schema.properties) {
|
|
1603
|
+
for (const [propName, propSchema] of Object.entries(schema.properties)) {
|
|
1604
|
+
if (!this.shouldIncludeProperty(propSchema)) {
|
|
1605
|
+
continue;
|
|
1606
|
+
}
|
|
1607
|
+
const isRequired = required.has(propName);
|
|
1608
|
+
const zodSchema = this.generatePropertySchema(propSchema, currentSchema);
|
|
1609
|
+
const validIdentifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
|
|
1610
|
+
const quotedPropName = validIdentifier.test(propName) ? propName : `"${propName}"`;
|
|
1611
|
+
let propertyDef = `${quotedPropName}: ${zodSchema}`;
|
|
1612
|
+
if (!isRequired) {
|
|
1613
|
+
propertyDef += ".optional()";
|
|
1614
|
+
}
|
|
1615
|
+
properties.push(propertyDef);
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
if (properties.length === 0) {
|
|
1619
|
+
return "{}";
|
|
1620
|
+
}
|
|
1621
|
+
return `{
|
|
1622
|
+
${properties.map((p) => ` ${p}`).join(",\n")}
|
|
1623
|
+
}`;
|
|
1624
|
+
}
|
|
1625
|
+
};
|
|
1626
|
+
// Performance optimization: Lookup table for faster inclusion checks
|
|
1627
|
+
_PropertyGenerator.INCLUSION_RULES = {
|
|
1628
|
+
request: (schema) => !schema.readOnly,
|
|
1629
|
+
response: (schema) => !schema.writeOnly,
|
|
1630
|
+
all: () => true
|
|
1631
|
+
};
|
|
1632
|
+
var PropertyGenerator = _PropertyGenerator;
|
|
1633
|
+
|
|
138
1634
|
// src/utils/config-schemas.ts
|
|
139
1635
|
import { z } from "zod";
|
|
140
1636
|
var RequestResponseOptionsSchema = z.strictObject({
|
|
@@ -221,95 +1717,8 @@ function getResponseParseMethod(contentType, fallback = "text") {
|
|
|
221
1717
|
return { method: fallback, isUnknown: true };
|
|
222
1718
|
}
|
|
223
1719
|
|
|
224
|
-
// src/utils/lru-cache.ts
|
|
225
|
-
var LRUCache = class {
|
|
226
|
-
constructor(maxSize) {
|
|
227
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
228
|
-
this.maxSize = maxSize;
|
|
229
|
-
}
|
|
230
|
-
get capacity() {
|
|
231
|
-
return this.maxSize;
|
|
232
|
-
}
|
|
233
|
-
get(key) {
|
|
234
|
-
if (!this.cache.has(key)) return void 0;
|
|
235
|
-
const value = this.cache.get(key);
|
|
236
|
-
if (value === void 0) return void 0;
|
|
237
|
-
this.cache.delete(key);
|
|
238
|
-
this.cache.set(key, value);
|
|
239
|
-
return value;
|
|
240
|
-
}
|
|
241
|
-
set(key, value) {
|
|
242
|
-
if (this.cache.has(key)) {
|
|
243
|
-
this.cache.delete(key);
|
|
244
|
-
} else if (this.cache.size >= this.maxSize) {
|
|
245
|
-
const firstKey = this.cache.keys().next().value;
|
|
246
|
-
if (firstKey !== void 0) {
|
|
247
|
-
this.cache.delete(firstKey);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
this.cache.set(key, value);
|
|
251
|
-
}
|
|
252
|
-
has(key) {
|
|
253
|
-
return this.cache.has(key);
|
|
254
|
-
}
|
|
255
|
-
clear() {
|
|
256
|
-
this.cache.clear();
|
|
257
|
-
}
|
|
258
|
-
size() {
|
|
259
|
-
return this.cache.size;
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
// src/utils/name-utils.ts
|
|
264
|
-
function sanitizeIdentifier(str) {
|
|
265
|
-
return str.replace(/[^a-zA-Z0-9._\-\s]+/g, "_");
|
|
266
|
-
}
|
|
267
|
-
function toCamelCase(str, options) {
|
|
268
|
-
const sanitized = sanitizeIdentifier(str);
|
|
269
|
-
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
270
|
-
let name;
|
|
271
|
-
if (words.length === 0) {
|
|
272
|
-
name = str.charAt(0).toLowerCase() + str.slice(1);
|
|
273
|
-
} else if (words.length === 1) {
|
|
274
|
-
name = words[0].charAt(0).toLowerCase() + words[0].slice(1);
|
|
275
|
-
} else {
|
|
276
|
-
name = words[0].charAt(0).toLowerCase() + words[0].slice(1) + words.slice(1).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
277
|
-
}
|
|
278
|
-
if (options == null ? void 0 : options.prefix) {
|
|
279
|
-
const prefix = options.prefix.charAt(0).toLowerCase() + options.prefix.slice(1);
|
|
280
|
-
name = prefix + name.charAt(0).toUpperCase() + name.slice(1);
|
|
281
|
-
}
|
|
282
|
-
if (options == null ? void 0 : options.suffix) {
|
|
283
|
-
const suffix = options.suffix.charAt(0).toUpperCase() + options.suffix.slice(1);
|
|
284
|
-
name = name + suffix;
|
|
285
|
-
}
|
|
286
|
-
return name;
|
|
287
|
-
}
|
|
288
|
-
function toPascalCase(str) {
|
|
289
|
-
const stringValue = String(str);
|
|
290
|
-
const isAlreadyValidCase = /^[a-zA-Z][a-zA-Z0-9]*$/.test(stringValue);
|
|
291
|
-
if (isAlreadyValidCase) {
|
|
292
|
-
return stringValue.charAt(0).toUpperCase() + stringValue.slice(1);
|
|
293
|
-
}
|
|
294
|
-
const sanitized = sanitizeIdentifier(stringValue);
|
|
295
|
-
const words = sanitized.split(/[.\-_\s]+/).filter((word) => word.length > 0);
|
|
296
|
-
let result;
|
|
297
|
-
if (words.length === 0) {
|
|
298
|
-
result = "Value";
|
|
299
|
-
} else {
|
|
300
|
-
result = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
301
|
-
}
|
|
302
|
-
if (/^\d/.test(result)) {
|
|
303
|
-
result = `N${result}`;
|
|
304
|
-
}
|
|
305
|
-
if (!result || /^_+$/.test(result)) {
|
|
306
|
-
return "Value";
|
|
307
|
-
}
|
|
308
|
-
return result;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
1720
|
// src/utils/operation-filters.ts
|
|
312
|
-
import { minimatch } from "minimatch";
|
|
1721
|
+
import { minimatch as minimatch2 } from "minimatch";
|
|
313
1722
|
function createFilterStatistics() {
|
|
314
1723
|
return {
|
|
315
1724
|
totalOperations: 0,
|
|
@@ -328,7 +1737,7 @@ function matchesAnyPattern(value, patterns) {
|
|
|
328
1737
|
if (!value) {
|
|
329
1738
|
return false;
|
|
330
1739
|
}
|
|
331
|
-
return patterns.some((pattern) =>
|
|
1740
|
+
return patterns.some((pattern) => minimatch2(value, pattern));
|
|
332
1741
|
}
|
|
333
1742
|
function containsAny(arr, values) {
|
|
334
1743
|
if (!values || values.length === 0) {
|
|
@@ -442,82 +1851,8 @@ function formatFilterStatistics(stats) {
|
|
|
442
1851
|
return lines.join("\n");
|
|
443
1852
|
}
|
|
444
1853
|
|
|
445
|
-
// src/utils/pattern-utils.ts
|
|
446
|
-
import { minimatch as minimatch2 } from "minimatch";
|
|
447
|
-
function isValidGlobPattern(pattern) {
|
|
448
|
-
try {
|
|
449
|
-
new minimatch2.Minimatch(pattern);
|
|
450
|
-
return true;
|
|
451
|
-
} catch {
|
|
452
|
-
return false;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
function isGlobPattern(pattern) {
|
|
456
|
-
return /[*?[\]{}!]/.test(pattern);
|
|
457
|
-
}
|
|
458
|
-
function stripPrefix(input, pattern, ensureLeadingChar) {
|
|
459
|
-
if (!pattern) {
|
|
460
|
-
return input;
|
|
461
|
-
}
|
|
462
|
-
if (isGlobPattern(pattern) && !isValidGlobPattern(pattern)) {
|
|
463
|
-
console.warn(`\u26A0\uFE0F Invalid glob pattern "${pattern}": Pattern is malformed`);
|
|
464
|
-
return input;
|
|
465
|
-
}
|
|
466
|
-
if (isGlobPattern(pattern)) {
|
|
467
|
-
let longestMatch = -1;
|
|
468
|
-
for (let i = 1; i <= input.length; i++) {
|
|
469
|
-
const testPrefix = input.substring(0, i);
|
|
470
|
-
if (minimatch2(testPrefix, pattern)) {
|
|
471
|
-
longestMatch = i;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
if (longestMatch > 0) {
|
|
475
|
-
const stripped = input.substring(longestMatch);
|
|
476
|
-
if (ensureLeadingChar) {
|
|
477
|
-
if (stripped === "") {
|
|
478
|
-
return ensureLeadingChar;
|
|
479
|
-
}
|
|
480
|
-
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
481
|
-
return `${ensureLeadingChar}${stripped}`;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
return stripped === "" && !ensureLeadingChar ? input : stripped;
|
|
485
|
-
}
|
|
486
|
-
return input;
|
|
487
|
-
}
|
|
488
|
-
if (input.startsWith(pattern)) {
|
|
489
|
-
const stripped = input.substring(pattern.length);
|
|
490
|
-
if (ensureLeadingChar) {
|
|
491
|
-
if (stripped === "") {
|
|
492
|
-
return ensureLeadingChar;
|
|
493
|
-
}
|
|
494
|
-
if (!stripped.startsWith(ensureLeadingChar)) {
|
|
495
|
-
return `${ensureLeadingChar}${stripped}`;
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
return stripped;
|
|
499
|
-
}
|
|
500
|
-
return input;
|
|
501
|
-
}
|
|
502
|
-
function stripPathPrefix(path, pattern) {
|
|
503
|
-
if (!pattern) {
|
|
504
|
-
return path;
|
|
505
|
-
}
|
|
506
|
-
if (!isGlobPattern(pattern)) {
|
|
507
|
-
let normalizedPattern = pattern.trim();
|
|
508
|
-
if (!normalizedPattern.startsWith("/")) {
|
|
509
|
-
normalizedPattern = `/${normalizedPattern}`;
|
|
510
|
-
}
|
|
511
|
-
if (normalizedPattern.endsWith("/") && normalizedPattern !== "/") {
|
|
512
|
-
normalizedPattern = normalizedPattern.slice(0, -1);
|
|
513
|
-
}
|
|
514
|
-
return stripPrefix(path, normalizedPattern, "/");
|
|
515
|
-
}
|
|
516
|
-
return stripPrefix(path, pattern, "/");
|
|
517
|
-
}
|
|
518
|
-
|
|
519
1854
|
// src/utils/ref-resolver.ts
|
|
520
|
-
function
|
|
1855
|
+
function resolveRef2(obj, spec, maxDepth = 10) {
|
|
521
1856
|
var _a, _b, _c, _d;
|
|
522
1857
|
if (!obj || typeof obj !== "object" || maxDepth <= 0) return obj;
|
|
523
1858
|
if (!obj.$ref) return obj;
|
|
@@ -542,20 +1877,20 @@ function resolveRef(obj, spec, maxDepth = 10) {
|
|
|
542
1877
|
}
|
|
543
1878
|
if (resolved) {
|
|
544
1879
|
if (resolved.$ref) {
|
|
545
|
-
return
|
|
1880
|
+
return resolveRef2(resolved, spec, maxDepth - 1);
|
|
546
1881
|
}
|
|
547
1882
|
return resolved;
|
|
548
1883
|
}
|
|
549
1884
|
return obj;
|
|
550
1885
|
}
|
|
551
1886
|
function resolveParameterRef(param, spec) {
|
|
552
|
-
return
|
|
1887
|
+
return resolveRef2(param, spec);
|
|
553
1888
|
}
|
|
554
1889
|
function resolveRequestBodyRef(requestBody, spec) {
|
|
555
|
-
return
|
|
1890
|
+
return resolveRef2(requestBody, spec);
|
|
556
1891
|
}
|
|
557
1892
|
function resolveResponseRef(response, spec) {
|
|
558
|
-
return
|
|
1893
|
+
return resolveRef2(response, spec);
|
|
559
1894
|
}
|
|
560
1895
|
function mergeParameters(pathParams, operationParams, spec) {
|
|
561
1896
|
const resolvedPathParams = (pathParams || []).map((p) => resolveParameterRef(p, spec));
|
|
@@ -575,14 +1910,6 @@ function mergeParameters(pathParams, operationParams, spec) {
|
|
|
575
1910
|
return merged;
|
|
576
1911
|
}
|
|
577
1912
|
|
|
578
|
-
// src/utils/string-utils.ts
|
|
579
|
-
function escapePattern(str) {
|
|
580
|
-
return str.replace(/\//g, "\\/");
|
|
581
|
-
}
|
|
582
|
-
function escapeJSDoc(str) {
|
|
583
|
-
return str.replace(/\*\//g, "*\\/");
|
|
584
|
-
}
|
|
585
|
-
|
|
586
1913
|
// src/utils/typescript-loader.ts
|
|
587
1914
|
function createTypeScriptLoader() {
|
|
588
1915
|
return async (filepath) => {
|
|
@@ -616,72 +1943,12 @@ function createTypeScriptLoader() {
|
|
|
616
1943
|
}
|
|
617
1944
|
};
|
|
618
1945
|
}
|
|
619
|
-
|
|
620
|
-
// src/validators/string-validator.ts
|
|
621
|
-
var PATTERN_CACHE = new LRUCache(1e3);
|
|
622
|
-
var DEFAULT_FORMAT_MAP = {
|
|
623
|
-
uuid: "z.uuid()",
|
|
624
|
-
email: "z.email()",
|
|
625
|
-
uri: "z.url()",
|
|
626
|
-
url: "z.url()",
|
|
627
|
-
"uri-reference": 'z.string().refine((val) => !/\\s/.test(val), { message: "Must be a valid URI reference" })',
|
|
628
|
-
hostname: 'z.string().refine((val) => /^(?=.{1,253}$)(?:(?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)*(?!-)[A-Za-z0-9-]{1,63}(?<!-)$/.test(val), { message: "Must be a valid hostname" })',
|
|
629
|
-
byte: "z.base64()",
|
|
630
|
-
binary: "z.string()",
|
|
631
|
-
date: "z.iso.date()",
|
|
632
|
-
time: "z.iso.time()",
|
|
633
|
-
duration: 'z.string().refine((val) => /^P(?:(?:\\d+Y)?(?:\\d+M)?(?:\\d+D)?(?:T(?:\\d+H)?(?:\\d+M)?(?:\\d+(?:\\.\\d+)?S)?)?|\\d+W)$/.test(val) && !/^PT?$/.test(val), { message: "Must be a valid ISO 8601 duration" })',
|
|
634
|
-
ipv4: "z.ipv4()",
|
|
635
|
-
ipv6: "z.ipv6()",
|
|
636
|
-
emoji: "z.emoji()",
|
|
637
|
-
base64: "z.base64()",
|
|
638
|
-
base64url: "z.base64url()",
|
|
639
|
-
nanoid: "z.nanoid()",
|
|
640
|
-
cuid: "z.cuid()",
|
|
641
|
-
cuid2: "z.cuid2()",
|
|
642
|
-
ulid: "z.ulid()",
|
|
643
|
-
cidr: "z.cidrv4()",
|
|
644
|
-
// Default to v4
|
|
645
|
-
cidrv4: "z.cidrv4()",
|
|
646
|
-
cidrv6: "z.cidrv6()",
|
|
647
|
-
"json-pointer": 'z.string().refine((val) => val === "" || /^(\\/([^~/]|~0|~1)+)+$/.test(val), { message: "Must be a valid JSON Pointer (RFC 6901)" })',
|
|
648
|
-
"relative-json-pointer": 'z.string().refine((val) => /^(0|[1-9]\\d*)(#|(\\/([^~/]|~0|~1)+)*)$/.test(val), { message: "Must be a valid relative JSON Pointer" })'
|
|
649
|
-
};
|
|
650
|
-
var FORMAT_MAP = {
|
|
651
|
-
...DEFAULT_FORMAT_MAP,
|
|
652
|
-
"date-time": "z.iso.datetime()"
|
|
653
|
-
};
|
|
654
|
-
function configureDateTimeFormat(pattern) {
|
|
655
|
-
if (!pattern) {
|
|
656
|
-
FORMAT_MAP["date-time"] = "z.iso.datetime()";
|
|
657
|
-
return;
|
|
658
|
-
}
|
|
659
|
-
const patternStr = pattern instanceof RegExp ? pattern.source : pattern;
|
|
660
|
-
if (patternStr === "") {
|
|
661
|
-
FORMAT_MAP["date-time"] = "z.iso.datetime()";
|
|
662
|
-
return;
|
|
663
|
-
}
|
|
664
|
-
try {
|
|
665
|
-
new RegExp(patternStr);
|
|
666
|
-
} catch (error) {
|
|
667
|
-
throw new Error(
|
|
668
|
-
`Invalid regular expression pattern for customDateTimeFormatRegex: ${patternStr}. ${error instanceof Error ? error.message : "Pattern is malformed"}`
|
|
669
|
-
);
|
|
670
|
-
}
|
|
671
|
-
const escapedPattern = escapePattern(patternStr);
|
|
672
|
-
FORMAT_MAP["date-time"] = `z.string().regex(/${escapedPattern}/)`;
|
|
673
|
-
}
|
|
674
|
-
function resetFormatMap() {
|
|
675
|
-
FORMAT_MAP = {
|
|
676
|
-
...DEFAULT_FORMAT_MAP,
|
|
677
|
-
"date-time": "z.iso.datetime()"
|
|
678
|
-
};
|
|
679
|
-
}
|
|
680
1946
|
export {
|
|
681
1947
|
LRUCache,
|
|
682
1948
|
OperationFiltersSchema,
|
|
1949
|
+
PropertyGenerator,
|
|
683
1950
|
RequestResponseOptionsSchema,
|
|
684
|
-
|
|
1951
|
+
buildDateTimeValidation,
|
|
685
1952
|
createFilterStatistics,
|
|
686
1953
|
createTypeScriptLoader,
|
|
687
1954
|
escapeJSDoc,
|
|
@@ -691,9 +1958,8 @@ export {
|
|
|
691
1958
|
getBatchExitCode,
|
|
692
1959
|
getResponseParseMethod,
|
|
693
1960
|
mergeParameters,
|
|
694
|
-
resetFormatMap,
|
|
695
1961
|
resolveParameterRef,
|
|
696
|
-
resolveRef,
|
|
1962
|
+
resolveRef2 as resolveRef,
|
|
697
1963
|
resolveRequestBodyRef,
|
|
698
1964
|
resolveResponseRef,
|
|
699
1965
|
shouldIncludeOperation,
|