schemantic 0.3.0 → 0.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +196 -174
  2. package/LICENSE +21 -21
  3. package/README.md +88 -830
  4. package/dist/cli/index.d.ts +4 -1
  5. package/dist/cli/index.d.ts.map +1 -1
  6. package/dist/cli/index.js +185 -61
  7. package/dist/cli/index.js.map +1 -1
  8. package/dist/core/configuration.d.ts +6 -0
  9. package/dist/core/configuration.d.ts.map +1 -0
  10. package/dist/core/configuration.js +64 -0
  11. package/dist/core/configuration.js.map +1 -0
  12. package/dist/core/index.d.ts +1 -0
  13. package/dist/core/index.d.ts.map +1 -1
  14. package/dist/core/index.js +1 -0
  15. package/dist/core/index.js.map +1 -1
  16. package/dist/core/output-writer.d.ts +7 -0
  17. package/dist/core/output-writer.d.ts.map +1 -0
  18. package/dist/core/output-writer.js +255 -0
  19. package/dist/core/output-writer.js.map +1 -0
  20. package/dist/core/schemantic.d.ts +8 -2
  21. package/dist/core/schemantic.d.ts.map +1 -1
  22. package/dist/core/schemantic.js +196 -71
  23. package/dist/core/schemantic.js.map +1 -1
  24. package/dist/generators/api-client-generator.d.ts +9 -20
  25. package/dist/generators/api-client-generator.d.ts.map +1 -1
  26. package/dist/generators/api-client-generator.js +364 -424
  27. package/dist/generators/api-client-generator.js.map +1 -1
  28. package/dist/generators/base.d.ts.map +1 -1
  29. package/dist/generators/base.js +7 -25
  30. package/dist/generators/base.js.map +1 -1
  31. package/dist/generators/enum-generator.d.ts.map +1 -1
  32. package/dist/generators/enum-generator.js +30 -3
  33. package/dist/generators/enum-generator.js.map +1 -1
  34. package/dist/generators/hook-generator.d.ts +3 -17
  35. package/dist/generators/hook-generator.d.ts.map +1 -1
  36. package/dist/generators/hook-generator.js +51 -445
  37. package/dist/generators/hook-generator.js.map +1 -1
  38. package/dist/generators/naming.d.ts +10 -0
  39. package/dist/generators/naming.d.ts.map +1 -0
  40. package/dist/generators/naming.js +331 -0
  41. package/dist/generators/naming.js.map +1 -0
  42. package/dist/generators/object-generator.d.ts +0 -4
  43. package/dist/generators/object-generator.d.ts.map +1 -1
  44. package/dist/generators/object-generator.js +13 -101
  45. package/dist/generators/object-generator.js.map +1 -1
  46. package/dist/generators/primitive-generator.d.ts +1 -13
  47. package/dist/generators/primitive-generator.d.ts.map +1 -1
  48. package/dist/generators/primitive-generator.js +11 -76
  49. package/dist/generators/primitive-generator.js.map +1 -1
  50. package/dist/generators/projections.d.ts +10 -0
  51. package/dist/generators/projections.d.ts.map +1 -0
  52. package/dist/generators/projections.js +65 -0
  53. package/dist/generators/projections.js.map +1 -0
  54. package/dist/generators/schema-type.d.ts +23 -0
  55. package/dist/generators/schema-type.d.ts.map +1 -0
  56. package/dist/generators/schema-type.js +233 -0
  57. package/dist/generators/schema-type.js.map +1 -0
  58. package/dist/parsers/base.d.ts +2 -0
  59. package/dist/parsers/base.d.ts.map +1 -1
  60. package/dist/parsers/base.js +12 -2
  61. package/dist/parsers/base.js.map +1 -1
  62. package/dist/parsers/factory.d.ts.map +1 -1
  63. package/dist/parsers/factory.js +2 -1
  64. package/dist/parsers/factory.js.map +1 -1
  65. package/dist/parsers/openapi.d.ts +4 -0
  66. package/dist/parsers/openapi.d.ts.map +1 -1
  67. package/dist/parsers/openapi.js +107 -20
  68. package/dist/parsers/openapi.js.map +1 -1
  69. package/dist/plugins/branded-types.d.ts.map +1 -1
  70. package/dist/plugins/branded-types.js +231 -365
  71. package/dist/plugins/branded-types.js.map +1 -1
  72. package/dist/plugins/builtin.d.ts.map +1 -1
  73. package/dist/plugins/builtin.js +6 -141
  74. package/dist/plugins/builtin.js.map +1 -1
  75. package/dist/plugins/index.d.ts +4 -0
  76. package/dist/plugins/index.d.ts.map +1 -1
  77. package/dist/plugins/index.js +17 -0
  78. package/dist/plugins/index.js.map +1 -1
  79. package/dist/plugins/loader.d.ts +5 -1
  80. package/dist/plugins/loader.d.ts.map +1 -1
  81. package/dist/plugins/loader.js +42 -5
  82. package/dist/plugins/loader.js.map +1 -1
  83. package/dist/plugins/manager.d.ts.map +1 -1
  84. package/dist/plugins/manager.js +7 -7
  85. package/dist/plugins/manager.js.map +1 -1
  86. package/dist/plugins/performance-monitoring.d.ts +31 -1
  87. package/dist/plugins/performance-monitoring.d.ts.map +1 -1
  88. package/dist/plugins/performance-monitoring.js +146 -207
  89. package/dist/plugins/performance-monitoring.js.map +1 -1
  90. package/dist/plugins/request-deduplication.d.ts.map +1 -1
  91. package/dist/plugins/request-deduplication.js +151 -586
  92. package/dist/plugins/request-deduplication.js.map +1 -1
  93. package/dist/plugins/zod-validation.d.ts +23 -15
  94. package/dist/plugins/zod-validation.d.ts.map +1 -1
  95. package/dist/plugins/zod-validation.js +390 -268
  96. package/dist/plugins/zod-validation.js.map +1 -1
  97. package/dist/types/core.d.ts +20 -3
  98. package/dist/types/core.d.ts.map +1 -1
  99. package/dist/types/core.js +2 -0
  100. package/dist/types/core.js.map +1 -1
  101. package/dist/types/openapi.d.ts +14 -1
  102. package/dist/types/openapi.d.ts.map +1 -1
  103. package/docs/ADVANCED_PLUGINS_IMPLEMENTATION.md +24 -355
  104. package/docs/ADVANCED_PLUGIN_DEVELOPMENT.md +33 -309
  105. package/docs/API.md +80 -601
  106. package/docs/AUDIT.md +49 -0
  107. package/docs/CONFIGURATION.md +81 -715
  108. package/docs/FASTAPI_TUTORIAL.md +109 -1497
  109. package/docs/NAMING_MIGRATION.md +59 -0
  110. package/docs/PLUGINS.md +49 -687
  111. package/docs/PLUGIN_DEVELOPMENT.md +53 -720
  112. package/docs/REAUDIT.md +399 -0
  113. package/docs/RELEASING.md +88 -0
  114. package/docs/REMEDIATION.md +58 -0
  115. package/docs/superpowers/plans/2026-10-10-audit-remediation.md +113 -0
  116. package/package.json +114 -95
  117. package/dist/__tests__/setup.d.ts +0 -6
  118. package/dist/__tests__/setup.d.ts.map +0 -1
  119. package/dist/__tests__/setup.js +0 -30
  120. package/dist/__tests__/setup.js.map +0 -1
  121. package/dist/__tests__/test-config.d.ts +0 -36
  122. package/dist/__tests__/test-config.d.ts.map +0 -1
  123. package/dist/__tests__/test-config.js +0 -562
  124. package/dist/__tests__/test-config.js.map +0 -1
@@ -18,15 +18,17 @@
18
18
  */
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
20
  exports.zodValidationPlugin = void 0;
21
+ exports.buildZodSchema = buildZodSchema;
21
22
  exports.memoizedValidation = memoizedValidation;
22
23
  exports.getZodValidationPerformanceStats = getZodValidationPerformanceStats;
23
24
  exports.clearZodValidationCaches = clearZodValidationCaches;
24
25
  exports.getValidationCacheStats = getValidationCacheStats;
25
26
  const schema_1 = require("../types/schema");
27
+ const schema_type_1 = require("../generators/schema-type");
26
28
  /**
27
29
  * Schema cache for performance optimization
28
30
  */
29
- const schemaCache = new Map();
31
+ let schemaCaches = new WeakMap();
30
32
  /**
31
33
  * Memoized schema transformation for performance
32
34
  */
@@ -60,21 +62,20 @@ exports.zodValidationPlugin = {
60
62
  const startTime = performance.now();
61
63
  try {
62
64
  const options = getPluginOptions(context);
63
- const schemaKey = generateSchemaKey(schema);
65
+ const sourceKey = generateSchemaKey(schema);
66
+ const schemaKey = sourceKey === undefined ? undefined : JSON.stringify(options) + sourceKey;
64
67
  // Use memoized transformation for performance
65
- if (options.cacheSchemas && memoizedSchemaTransform.has(schemaKey)) {
68
+ if (options.cacheSchemas && schemaKey !== undefined && memoizedSchemaTransform.has(schemaKey)) {
66
69
  return memoizedSchemaTransform.get(schemaKey);
67
70
  }
68
71
  const transformedSchema = transformSchemaForZod(schema, options);
69
- if (options.cacheSchemas) {
72
+ if (options.cacheSchemas && schemaKey !== undefined) {
73
+ if (memoizedSchemaTransform.size >= 1000)
74
+ memoizedSchemaTransform.delete(memoizedSchemaTransform.keys().next().value);
70
75
  memoizedSchemaTransform.set(schemaKey, transformedSchema);
71
76
  }
72
77
  return transformedSchema;
73
78
  }
74
- catch (error) {
75
- console.warn(`Zod validation plugin transformSchema failed:`, error);
76
- return schema;
77
- }
78
79
  finally {
79
80
  const endTime = performance.now();
80
81
  recordPerformanceMetric("transformSchema", endTime - startTime);
@@ -113,7 +114,7 @@ exports.zodValidationPlugin = {
113
114
  generatedType.dependencies.push("zod");
114
115
  // Add branded type exports if enabled
115
116
  if (options.generateBrandedTypes) {
116
- generatedType.exports.push(`Branded${typeName}`);
117
+ generatedType.exports.push(`Branded${typeName}`, `createBranded${typeName}`);
117
118
  }
118
119
  // Add validation exports
119
120
  generatedType.exports.push(`${typeName}Schema`, `validate${typeName}`, `parse${typeName}`);
@@ -121,9 +122,6 @@ exports.zodValidationPlugin = {
121
122
  generatedType.exports.push(`is${typeName}`);
122
123
  }
123
124
  }
124
- catch (error) {
125
- console.warn(`Zod validation plugin afterTypeGeneration failed for ${typeName}:`, error);
126
- }
127
125
  finally {
128
126
  const endTime = performance.now();
129
127
  recordPerformanceMetric("afterTypeGeneration", endTime - startTime);
@@ -136,8 +134,9 @@ exports.zodValidationPlugin = {
136
134
  const startTime = performance.now();
137
135
  try {
138
136
  const options = getPluginOptions(context);
137
+ const validationErrorName = chooseValidationErrorName(generatedClient, context);
139
138
  // Add validation middleware to client
140
- const validationMiddleware = generateValidationMiddleware(generatedClient, context, options);
139
+ const validationMiddleware = generateValidationMiddleware(generatedClient, context, options, validationErrorName);
141
140
  // Add request/response validation helpers
142
141
  const validationHelpers = generateValidationHelpers(options);
143
142
  // Update client content with validation integration
@@ -171,10 +170,7 @@ exports.zodValidationPlugin = {
171
170
  generatedClient.dependencies.push("zod");
172
171
  }
173
172
  // Add validation exports
174
- generatedClient.exports.push("validateRequest", "validateResponse", "ValidationError");
175
- }
176
- catch (error) {
177
- console.warn(`Zod validation plugin afterClientGeneration failed:`, error);
173
+ generatedClient.exports.push("validateRequest", "validateResponse", validationErrorName, "createValidationPipeline", "createLazyValidator");
178
174
  }
179
175
  finally {
180
176
  const endTime = performance.now();
@@ -186,7 +182,7 @@ exports.zodValidationPlugin = {
186
182
  * Get plugin options from generation context
187
183
  */
188
184
  function getPluginOptions(context) {
189
- const pluginConfig = context.config.plugins?.find((p) => p.name === "zod-validation");
185
+ const pluginConfig = context.config.plugins?.find((p) => p.enabled && (p.name === "zod-validation" || p.name === "validation"));
190
186
  return {
191
187
  strictMode: true,
192
188
  cacheSchemas: true,
@@ -201,16 +197,29 @@ function getPluginOptions(context) {
201
197
  * Generate a unique key for schema caching
202
198
  */
203
199
  function generateSchemaKey(schema) {
204
- return JSON.stringify(schema);
200
+ // Circular in-memory schema graphs have no JSON cache identity. Bypass caching
201
+ // instead of inventing a sentinel shared by unrelated recursive graphs.
202
+ try {
203
+ return JSON.stringify(schema);
204
+ }
205
+ catch (error) {
206
+ if (error instanceof TypeError && /circular/i.test(error.message))
207
+ return undefined;
208
+ throw error;
209
+ }
205
210
  }
206
211
  /**
207
212
  * Transform schema to include Zod validation metadata
208
213
  */
209
- function transformSchemaForZod(schema, options) {
214
+ function transformSchemaForZod(schema, options, seen = new WeakMap()) {
210
215
  if (!(0, schema_1.isOpenAPISchemaObject)(schema)) {
211
216
  return schema;
212
217
  }
218
+ const existing = seen.get(schema);
219
+ if (existing)
220
+ return existing;
213
221
  const transformedSchema = { ...schema };
222
+ seen.set(schema, transformedSchema);
214
223
  // Add Zod validation metadata
215
224
  transformedSchema._zodValidation = {
216
225
  strictMode: options.strictMode,
@@ -220,9 +229,9 @@ function transformSchemaForZod(schema, options) {
220
229
  // Process properties recursively
221
230
  if (schema.properties) {
222
231
  transformedSchema.properties = {};
223
- for (const [propName, propSchema] of Object.entries(schema.properties)) {
232
+ for (const [propName, propSchema] of Object.entries(schema.properties ?? {})) {
224
233
  if (typeof propSchema === "object" && propSchema !== null) {
225
- transformedSchema.properties[propName] = transformSchemaForZod(propSchema, options);
234
+ transformedSchema.properties[propName] = transformSchemaForZod(propSchema, options, seen);
226
235
  }
227
236
  else {
228
237
  transformedSchema.properties[propName] = propSchema;
@@ -235,13 +244,18 @@ function transformSchemaForZod(schema, options) {
235
244
  * Generate Zod schema for a type
236
245
  */
237
246
  async function generateZodSchema(typeName, schema, options, context) {
238
- const cacheKey = `${typeName}-${generateSchemaKey(schema)}`;
239
- if (options.cacheSchemas && schemaCache.has(cacheKey)) {
240
- return schemaCache.get(cacheKey);
241
- }
247
+ const sourceKey = generateSchemaKey(schema);
248
+ const cacheKey = sourceKey === undefined ? undefined : JSON.stringify([typeName, context?.config.typePrefix, context?.config.typeSuffix, options, sourceKey]);
249
+ let cache = context ? schemaCaches.get(context) : undefined;
250
+ if (context && !cache) {
251
+ cache = new Map();
252
+ schemaCaches.set(context, cache);
253
+ }
254
+ if (options.cacheSchemas && cacheKey !== undefined && cache?.has(cacheKey))
255
+ return cache.get(cacheKey);
242
256
  const zodSchema = buildZodSchema(schema, options, 0, context);
243
- if (options.cacheSchemas) {
244
- schemaCache.set(cacheKey, zodSchema);
257
+ if (options.cacheSchemas && cacheKey !== undefined) {
258
+ cache?.set(cacheKey, zodSchema);
245
259
  }
246
260
  return zodSchema;
247
261
  }
@@ -249,62 +263,153 @@ async function generateZodSchema(typeName, schema, options, context) {
249
263
  * Build Zod schema from OpenAPI schema
250
264
  */
251
265
  function buildZodSchema(schema, options, depth = 0, context) {
252
- // Handle oneOf/discriminated unions early
253
- if ((0, schema_1.isOpenAPISchemaObject)(schema)) {
254
- const obj = schema;
255
- if (Array.isArray(obj.oneOf) && obj.oneOf.length > 0) {
256
- return buildOneOfSchema(obj, options, context);
266
+ if (depth > 30)
267
+ return "z.unknown()";
268
+ if (typeof schema === "object" && schema !== null && "$ref" in schema && schema.$ref) {
269
+ const ref = schema.$ref;
270
+ const siblings = Object.fromEntries(Object.entries(schema).filter(([key]) => key !== "$ref" && !key.startsWith("_") && !["description", "title", "deprecated", "example"].includes(key)));
271
+ const nullable = siblings.nullable === true;
272
+ delete siblings.nullable;
273
+ const resolved = context?.schemaResolver?.(ref);
274
+ let referenceSchema;
275
+ if (Object.keys(siblings).length && resolved) {
276
+ const inheritedType = (0, schema_1.isOpenAPISchemaObject)(resolved) && ["string", "number", "integer", "array", "boolean"].includes(String(resolved.type)) ? { type: resolved.type } : {};
277
+ referenceSchema = `z.intersection(${buildZodSchema(resolved, { ...options, strictMode: false }, depth + 1, context)}, ${buildZodSchema({ ...inheritedType, ...siblings }, { ...options, strictMode: false }, depth + 1, context)})`;
257
278
  }
279
+ else if (/^#\/components\/schemas\/[^/]+$/.test(ref)) {
280
+ referenceSchema = `z.lazy(() => ${formatTypeNameWithContext((0, schema_type_1.referenceName)(ref), context)}Schema)`;
281
+ }
282
+ else {
283
+ referenceSchema = resolved ? buildZodSchema(resolved, options, depth + 1, context) : "z.unknown()";
284
+ }
285
+ return nullable ? `${referenceSchema}.nullable()` : referenceSchema;
258
286
  }
259
- // Handle schema references first
260
- if (typeof schema === "object" &&
261
- schema !== null &&
262
- "$ref" in schema &&
263
- schema.$ref) {
264
- // Extract type name from reference and generate schema reference
265
- const refPath = schema.$ref;
266
- const typeName = refPath.split("/").pop();
267
- // Try to resolve and inline if it's a union (oneOf)
268
- if (context?.schemaResolver) {
269
- const resolved = context.schemaResolver(refPath);
270
- if (resolved && (0, schema_1.isOpenAPISchemaObject)(resolved)) {
271
- const obj = resolved;
272
- if (Array.isArray(obj.oneOf) && obj.oneOf.length > 0) {
273
- return buildOneOfSchema(obj, options, context);
287
+ if (!(0, schema_1.isOpenAPISchemaObject)(schema))
288
+ return "z.unknown()";
289
+ if (schema["x-schemantic-binary"] === true)
290
+ return "z.instanceof(globalThis.Blob)";
291
+ const build = (part) => buildZodSchema(part, options, depth + 1, context);
292
+ let result;
293
+ if (schema.enum?.length) {
294
+ const literals = schema.enum.map(value => `z.literal(${JSON.stringify(value)})`);
295
+ const literalSchema = literals.length === 1 ? literals[0] : `z.union([${literals.join(", ")}])`;
296
+ const own = { ...schema };
297
+ delete own.enum;
298
+ result = `z.intersection(${literalSchema}, ${build(own)})`;
299
+ }
300
+ else if (schema.const !== undefined) {
301
+ const own = { ...schema };
302
+ delete own.const;
303
+ result = `z.intersection(z.literal(${JSON.stringify(schema.const)}), ${build(own)})`;
304
+ }
305
+ else if (schema.oneOf?.length || schema.anyOf?.length || schema.allOf?.length) {
306
+ // A composition and its sibling constraints apply together. Implicit strict
307
+ // mode must not reject fields contributed by another intersected schema;
308
+ // explicit additionalProperties:false still applies within each branch.
309
+ const compositionOptions = { ...options, strictMode: false };
310
+ const hasSiblingFields = !!schema.properties || !!schema.required?.length || schema.additionalProperties !== undefined;
311
+ const compositionCount = [schema.oneOf, schema.anyOf, schema.allOf].filter(parts => parts?.length).length;
312
+ const intersectsFields = hasSiblingFields || compositionCount > 1;
313
+ const unionOptions = intersectsFields ? compositionOptions : options;
314
+ const buildPart = (part, partOptions = compositionOptions, inlineReference = true) => {
315
+ if (typeof part === "object" && part !== null && "$ref" in part && typeof part.$ref === "string") {
316
+ const resolved = context?.schemaResolver?.(part.$ref);
317
+ if (resolved && inlineReference) {
318
+ const siblings = { ...part };
319
+ delete siblings.$ref;
320
+ const nullable = siblings.nullable === true;
321
+ delete siblings.nullable;
322
+ // Retain the entire reference expression instead of substituting only
323
+ // its target and losing minLength/properties/required beside $ref.
324
+ return buildZodSchema({ allOf: [resolved, siblings], ...(nullable ? { nullable: true } : {}) }, partOptions, depth + 1, context);
274
325
  }
275
326
  }
327
+ return buildZodSchema(part, partOptions, depth + 1, context);
328
+ };
329
+ const parts = [];
330
+ if (schema.oneOf?.length) {
331
+ const variants = schema.oneOf.map(part => buildPart(part, unionOptions, intersectsFields));
332
+ const union = variants.length === 1 ? variants[0] : `z.union([${variants.join(", ")}])`;
333
+ parts.push(`${union}.refine((value: unknown) => [${variants.join(", ")}].filter(variant => variant.safeParse(value).success).length === 1, { message: "Expected exactly one matching schema" })`);
334
+ }
335
+ if (schema.anyOf?.length) {
336
+ const variants = schema.anyOf.map(part => buildPart(part, unionOptions, intersectsFields));
337
+ parts.push(variants.length === 1 ? variants[0] : `z.union([${variants.join(", ")}])`);
276
338
  }
277
- if (typeName) {
278
- const formattedTypeName = formatTypeNameWithContext(typeName, context);
279
- return `${formattedTypeName}Schema`;
339
+ if (schema.allOf?.length)
340
+ parts.push(...schema.allOf.map(part => buildPart(part)));
341
+ const own = { ...schema };
342
+ delete own.oneOf;
343
+ delete own.anyOf;
344
+ delete own.allOf;
345
+ delete own.nullable;
346
+ delete own.not;
347
+ const siblingValidator = buildZodSchema(own, compositionOptions, depth + 1, context);
348
+ if (siblingValidator !== "z.unknown()")
349
+ parts.push(siblingValidator);
350
+ result = parts.reduce((left, right) => `z.intersection(${left}, ${right})`);
351
+ }
352
+ else if (Array.isArray(schema.type)) {
353
+ const parts = schema.type.map(type => build({ ...schema, type, nullable: false }));
354
+ result = parts.length === 1 ? parts[0] : `z.union([${parts.join(", ")}])`;
355
+ }
356
+ else {
357
+ switch (schema.type) {
358
+ case "string":
359
+ result = buildStringSchema(schema, options);
360
+ break;
361
+ case "number":
362
+ case "integer":
363
+ result = buildNumberSchema(schema, options);
364
+ break;
365
+ case "boolean":
366
+ result = "z.boolean()";
367
+ break;
368
+ case "null":
369
+ result = "z.null()";
370
+ break;
371
+ case "array":
372
+ result = `z.array(${schema.items ? build(schema.items) : "z.unknown()"})`;
373
+ if (schema.minItems !== undefined)
374
+ result += `.min(${schema.minItems})`;
375
+ if (schema.maxItems !== undefined)
376
+ result += `.max(${schema.maxItems})`;
377
+ if (schema.uniqueItems)
378
+ result += `.refine((items: unknown[]) => items.every((item, index) => items.findIndex(other => { const canonical = (value: unknown): string => Array.isArray(value) ? '[' + value.map(canonical).join(',') + ']' : value !== null && typeof value === 'object' ? '{' + Object.keys(value).sort().map(key => JSON.stringify(key) + ':' + canonical((value as Record<string, unknown>)[key])).join(',') + '}' : JSON.stringify(value) ?? 'undefined'; return canonical(other) === canonical(item); }) === index), { message: "Array items must be unique" })`;
379
+ break;
380
+ case "object":
381
+ result = buildObjectSchema(schema, options, depth, context);
382
+ break;
383
+ default: result = schema.properties || schema.required || schema.additionalProperties !== undefined
384
+ ? buildObjectSchema(schema, options, depth, context) : buildUntypedConstraintSchema(schema, options, depth, context);
280
385
  }
281
386
  }
282
- if (!(0, schema_1.isOpenAPISchemaObject)(schema)) {
283
- return "z.unknown()";
387
+ if (schema.not)
388
+ result += `.refine((value: unknown) => !(${build(schema.not)}).safeParse(value).success, { message: "Value matches prohibited schema" })`;
389
+ return schema.nullable ? `${result}.nullable()` : result;
390
+ }
391
+ /** Validate applicable keywords even when a sibling schema does not declare type. */
392
+ function buildUntypedConstraintSchema(schema, options, depth, context) {
393
+ let result = "z.unknown()";
394
+ const check = (applies, validator) => {
395
+ result += `.refine((value: unknown) => !(${applies}) || (${validator}).safeParse(value).success)`;
396
+ };
397
+ if (schema.minLength !== undefined || schema.maxLength !== undefined || schema.pattern || schema.format) {
398
+ check("typeof value === 'string'", buildStringSchema({ ...schema, type: "string" }, options));
284
399
  }
285
- // Prevent infinite recursion
286
- if (depth > 10) {
287
- return "z.unknown()";
400
+ if (schema.minimum !== undefined || schema.maximum !== undefined || schema.exclusiveMinimum !== undefined || schema.exclusiveMaximum !== undefined || schema.multipleOf !== undefined) {
401
+ check("typeof value === 'number'", buildNumberSchema({ ...schema, type: "number" }, options));
288
402
  }
289
- switch (schema.type) {
290
- case "string":
291
- return buildStringSchema(schema, options);
292
- case "number":
293
- case "integer":
294
- return buildNumberSchema(schema, options);
295
- case "boolean":
296
- return "z.boolean()";
297
- case "array": {
298
- const itemSchema = schema.items
299
- ? buildZodSchema(schema.items, options, depth + 1, context)
300
- : "z.unknown()";
301
- return `z.array(${itemSchema})`;
302
- }
303
- case "object":
304
- return buildObjectSchema(schema, options, depth, context);
305
- default:
306
- return "z.unknown()";
403
+ if (schema.minProperties !== undefined || schema.maxProperties !== undefined) {
404
+ check("typeof value === 'object' && value !== null && !Array.isArray(value)", appendObjectPropertyConstraints("z.record(z.string(), z.unknown())", schema));
405
+ }
406
+ if (schema.items || schema.minItems !== undefined || schema.maxItems !== undefined || schema.uniqueItems) {
407
+ const arraySchema = { ...schema, type: "array" };
408
+ delete arraySchema.not;
409
+ delete arraySchema.nullable;
410
+ check("Array.isArray(value)", buildZodSchema(arraySchema, options, depth + 1, context));
307
411
  }
412
+ return result;
308
413
  }
309
414
  /**
310
415
  * Helper function to format type names consistently
@@ -314,13 +419,7 @@ function buildZodSchema(schema, options, depth = 0, context) {
314
419
  * Format a type name using optional generation context to apply prefix/suffix.
315
420
  */
316
421
  function formatTypeNameWithContext(name, context) {
317
- const tokens = name.split(/[-_\s]+/).filter(Boolean);
318
- const pascalCase = tokens
319
- .map((word) => (word ? word.charAt(0).toUpperCase() + word.slice(1) : ""))
320
- .join("");
321
- const prefix = context?.config?.typePrefix ?? "";
322
- const suffix = context?.config?.typeSuffix ?? "";
323
- return `${prefix || ""}${pascalCase}${suffix || ""}`;
422
+ return (0, schema_type_1.typeName)(name, context?.config.typePrefix, context?.config.typeSuffix);
324
423
  }
325
424
  /**
326
425
  * Build Zod string schema with validation rules
@@ -334,7 +433,7 @@ function buildStringSchema(schema, _options) {
334
433
  zodSchema += `.max(${schema.maxLength})`;
335
434
  }
336
435
  if (schema.pattern) {
337
- zodSchema += `.regex(/${schema.pattern}/)`;
436
+ zodSchema += `.regex(new RegExp(${JSON.stringify(schema.pattern)}))`;
338
437
  }
339
438
  if (schema.format) {
340
439
  switch (schema.format) {
@@ -368,7 +467,7 @@ function buildStringSchema(schema, _options) {
368
467
  if (_options.customErrorMessages && schema.description) {
369
468
  const customMessage = _options.customErrorMessages[schema.description];
370
469
  if (customMessage) {
371
- zodSchema += `.describe("${customMessage}")`;
470
+ zodSchema += `.describe(${JSON.stringify(customMessage)})`;
372
471
  }
373
472
  }
374
473
  return zodSchema;
@@ -384,6 +483,16 @@ function buildNumberSchema(schema, _options) {
384
483
  if (schema.maximum !== undefined) {
385
484
  zodSchema += `.max(${schema.maximum})`;
386
485
  }
486
+ if (schema.exclusiveMinimum !== undefined) {
487
+ const bound = typeof schema.exclusiveMinimum === "number" ? schema.exclusiveMinimum : schema.exclusiveMinimum ? schema.minimum : undefined;
488
+ if (bound !== undefined)
489
+ zodSchema += `.gt(${bound})`;
490
+ }
491
+ if (schema.exclusiveMaximum !== undefined) {
492
+ const bound = typeof schema.exclusiveMaximum === "number" ? schema.exclusiveMaximum : schema.exclusiveMaximum ? schema.maximum : undefined;
493
+ if (bound !== undefined)
494
+ zodSchema += `.lt(${bound})`;
495
+ }
387
496
  if (schema.multipleOf !== undefined) {
388
497
  zodSchema += `.multipleOf(${schema.multipleOf})`;
389
498
  }
@@ -393,161 +502,156 @@ function buildNumberSchema(schema, _options) {
393
502
  * Build Zod object schema
394
503
  */
395
504
  function buildObjectSchema(schema, options, depth, context) {
396
- if (!schema.properties) {
397
- return "z.object({})";
505
+ if (!schema.properties && !schema.required?.length) {
506
+ const values = typeof schema.additionalProperties === "object"
507
+ ? buildZodSchema(schema.additionalProperties, options, depth + 1, context) : "z.unknown()";
508
+ const base = schema.additionalProperties === false ? "z.object({}).strict()" : `z.record(z.string(), ${values})`;
509
+ return appendObjectPropertyConstraints(base, schema);
398
510
  }
399
511
  const properties = [];
400
512
  const required = schema.required || [];
401
- const transformPairs = [];
402
- const wantsCamel = context?.config?.namingConvention === "camelCase";
403
- // Helper: convert prop name to camelCase similar to generator
404
- const toCamel = (str) => {
405
- if (!str)
406
- return str;
407
- // sanitize: collapse non-alnum into underscore, trim
408
- const sanitized = str
409
- .replace(/[^A-Za-z0-9]+/g, "_")
410
- .replace(/^_+|_+$/g, "")
411
- .replace(/_+/g, "_");
412
- const parts = sanitized.split(/_+/).filter(Boolean);
413
- if (parts.length === 0)
414
- return "";
415
- const head = parts[0] ?? "";
416
- const tail = parts.slice(1);
417
- const camelHead = head ? head.charAt(0).toLowerCase() + head.slice(1) : "";
418
- const camelTail = tail.map((p) => p ? p.charAt(0).toUpperCase() + p.slice(1) : "");
419
- return camelHead + camelTail.join("");
420
- };
421
- for (const [propName, propSchema] of Object.entries(schema.properties)) {
513
+ for (const [propName, propSchema] of Object.entries(schema.properties ?? {})) {
422
514
  if (typeof propSchema === "object" && propSchema !== null) {
423
515
  let propZodSchema = buildZodSchema(propSchema, options, depth + 1, context);
424
516
  if (!required.includes(propName)) {
425
517
  propZodSchema += ".optional()";
426
518
  }
427
- properties.push(` ${propName}: ${propZodSchema}`);
428
- if (wantsCamel) {
429
- const camel = toCamel(propName);
430
- if (camel !== propName) {
431
- transformPairs.push(` ${camel}: val[${JSON.stringify(propName)}]`);
432
- }
433
- }
519
+ properties.push(` ${(0, schema_type_1.propertyKey)(propName)}: ${propZodSchema}`);
434
520
  }
435
521
  }
436
- const strictMode = options.strictMode ? ".strict()" : "";
522
+ for (const name of required)
523
+ if (!schema.properties?.[name])
524
+ properties.push(` ${(0, schema_type_1.propertyKey)(name)}: z.unknown().refine((value: unknown) => value !== undefined, { message: "Required property" })`);
525
+ const strictMode = schema.additionalProperties === true ? ".passthrough()"
526
+ : typeof schema.additionalProperties === "object" ? `.catchall(${buildZodSchema(schema.additionalProperties, options, depth + 1, context)})`
527
+ : options.strictMode || schema.additionalProperties === false ? ".strict()" : ".passthrough()";
437
528
  const base = `z.object({\n${properties.join(",\n")}\n})${strictMode}`;
438
- if (wantsCamel && transformPairs.length > 0) {
439
- // Type the transform parameter to avoid implicit any in generated code
440
- return `${base}.transform((val: Record<string, unknown>) => ({\n${transformPairs.join(",\n")}\n}))`;
441
- }
442
- // Default: return the base schema when no transform is required
443
- return base;
529
+ return appendObjectPropertyConstraints(base, schema);
530
+ }
531
+ function appendObjectPropertyConstraints(base, schema) {
532
+ // Zod intersections may reconcile strict-object unrecognized-key issues against
533
+ // sibling schemas. OpenAPI's explicit additionalProperties:false applies to
534
+ // this schema's own declared properties, so check the original input first.
535
+ const allowedKeys = JSON.stringify(Object.keys(schema.properties ?? {}));
536
+ let result = schema.additionalProperties === false
537
+ ? `z.unknown().refine((value: unknown) => value === null || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).every(key => ${allowedKeys}.includes(key)), { message: "Unexpected object property" }).pipe(${base})`
538
+ : base;
539
+ if (schema.minProperties !== undefined)
540
+ result += `.refine((value: Record<string, unknown>) => Object.keys(value).length >= ${schema.minProperties}, { message: "Too few properties" })`;
541
+ if (schema.maxProperties !== undefined)
542
+ result += `.refine((value: Record<string, unknown>) => Object.keys(value).length <= ${schema.maxProperties}, { message: "Too many properties" })`;
543
+ return result;
444
544
  }
445
545
  /**
446
546
  * Build Zod union/discriminatedUnion schema for oneOf
447
547
  */
448
- function buildOneOfSchema(schema, options, context) {
449
- const variants = (schema.oneOf || []).map((variant) => {
450
- if (typeof variant === "object" &&
451
- variant !== null &&
452
- "$ref" in variant) {
453
- const refPath = variant["$ref"];
454
- const name = refPath.split("/").pop() || "Unknown";
455
- return `${formatTypeNameWithContext(name, context)}Schema`;
456
- }
457
- return buildZodSchema(variant, options, 0, context);
458
- });
459
- const discriminator = schema.discriminator?.propertyName;
460
- if (discriminator) {
461
- return `z.discriminatedUnion(${JSON.stringify(discriminator)}, [${variants.join(", ")}])`;
462
- }
463
- return `z.union([${variants.join(", ")}])`;
464
- }
465
548
  /**
466
549
  * Generate branded type for compile-time safety
467
550
  */
468
551
  function generateBrandedType(typeName, _generatedType) {
469
- return `
470
- /**
471
- * Branded type for ${typeName} with compile-time guarantees
472
- */
473
- export type Branded${typeName} = ${typeName} & { __brand: '${typeName}' };
474
-
475
- /**
476
- * Create a branded ${typeName} instance
477
- */
478
- export function createBranded${typeName}(data: ${typeName}): Branded${typeName} {
479
- return data as Branded${typeName};
552
+ return `
553
+ /**
554
+ * Branded type for ${typeName} with compile-time guarantees
555
+ */
556
+ export type Branded${typeName} = ${typeName} & { __brand: '${typeName}' };
557
+
558
+ /**
559
+ * Create a branded ${typeName} instance
560
+ */
561
+ export function createBranded${typeName}(data: ${typeName}): Branded${typeName} {
562
+ return data as Branded${typeName};
480
563
  }`;
481
564
  }
482
565
  /**
483
566
  * Generate runtime type guard
484
567
  */
485
568
  function generateTypeGuard(typeName, _zodSchema) {
486
- return `
487
- /**
488
- * Runtime type guard for ${typeName}
489
- */
490
- export function is${typeName}(value: unknown): value is ${typeName} {
491
- return ${typeName}Schema.safeParse(value).success;
569
+ return `
570
+ /**
571
+ * Runtime type guard for ${typeName}
572
+ */
573
+ export function is${typeName}(value: unknown): value is ${typeName} {
574
+ return ${typeName}Schema.safeParse(value).success;
492
575
  }`;
493
576
  }
494
577
  /**
495
578
  * Generate Zod imports based on options
496
579
  */
497
- function generateZodImports(options) {
580
+ function generateZodImports(_options) {
498
581
  const imports = ["z"];
499
- if (options.generateBrandedTypes) {
500
- imports.push("ZodType");
501
- }
502
582
  return `import { ${imports.join(", ")} } from 'zod';`;
503
583
  }
504
584
  /**
505
585
  * Generate validation module for a type
506
586
  */
507
587
  function generateValidationModule(typeName, zodSchema, brandedType, typeGuard, _options) {
508
- return `
509
- /**
510
- * Zod validation schema for ${typeName}
511
- */
512
- export const ${typeName}Schema = ${zodSchema};
513
-
514
- /**
515
- * Validate ${typeName} data with detailed error reporting
516
- */
517
- export function validate${typeName}(data: unknown): { success: true; data: ${typeName} } | { success: false; errors: string[] } {
518
- const result = ${typeName}Schema.safeParse(data);
519
-
520
- if (!result.success) {
521
- const issues = result.error?.issues ?? [];
522
- console.warn('Response validation failed:', issues);
523
- // Return a structured failure result so consumers can handle errors without requiring a runtime ValidationError class
524
- return {
525
- success: false,
526
- errors: issues.length > 0
527
- ? issues.map((iss: z.core.$ZodIssue) => (iss.path && iss.path.length > 0 ? iss.path.join('.') + ': ' + iss.message : '(root): ' + iss.message))
528
- : []
529
- };
530
- }
531
-
532
- return { success: true, data: result.data as ${typeName} };
533
- }
534
-
535
- /**
536
- * Parse ${typeName} data with exception on validation failure
537
- */
538
- export function parse${typeName}(data: unknown): ${typeName} {
539
- return ${typeName}Schema.parse(data) as ${typeName};
540
- }
588
+ return `
589
+ /**
590
+ * Zod validation schema for ${typeName}
591
+ */
592
+ export const ${typeName}Schema: z.ZodType = ${zodSchema};
593
+
594
+ /**
595
+ * Validate ${typeName} data with detailed error reporting
596
+ */
597
+ export function validate${typeName}(data: unknown): { success: true; data: ${typeName} } | { success: false; errors: string[] } {
598
+ const result = ${typeName}Schema.safeParse(data);
599
+
600
+ if (!result.success) {
601
+ const issues = result.error?.issues ?? [];
602
+ console.warn('Response validation failed:', issues);
603
+ // Return a structured failure result so consumers can handle errors without requiring a runtime ValidationError class
604
+ return {
605
+ success: false,
606
+ errors: issues.length > 0
607
+ ? issues.map((iss: z.core.$ZodIssue) => (iss.path && iss.path.length > 0 ? iss.path.join('.') + ': ' + iss.message : '(root): ' + iss.message))
608
+ : []
609
+ };
610
+ }
611
+
612
+ return { success: true, data: result.data as ${typeName} };
613
+ }
614
+
615
+ /**
616
+ * Parse ${typeName} data with exception on validation failure
617
+ */
618
+ export function parse${typeName}(data: unknown): ${typeName} {
619
+ return ${typeName}Schema.parse(data) as ${typeName};
620
+ }
541
621
  ${brandedType}${typeGuard}`;
542
622
  }
623
+ /** Runtime helper names must be distinct across type/client exports and imported bindings. */
624
+ function chooseValidationErrorName(client, context) {
625
+ const claimed = new Set(client.exports);
626
+ for (const type of context.generatedTypes.values()) {
627
+ claimed.add(type.name);
628
+ for (const name of type.exports)
629
+ claimed.add(name);
630
+ }
631
+ for (const generatedClient of context.generatedClients.values()) {
632
+ claimed.add(generatedClient.name);
633
+ for (const name of generatedClient.exports)
634
+ claimed.add(name);
635
+ }
636
+ // Cover imported aliases and local bindings supplied by custom client generators.
637
+ for (const identifier of client.content.match(/\b[A-Za-z_$][\w$]*\b/g) ?? [])
638
+ claimed.add(identifier);
639
+ if (!claimed.has("ValidationError"))
640
+ return "ValidationError";
641
+ let name = "ResponseValidationError";
642
+ let suffix = 0;
643
+ while (claimed.has(name))
644
+ name = `ResponseValidationError${++suffix}`;
645
+ return name;
646
+ }
543
647
  /**
544
648
  * Generate validation middleware for API client
545
649
  */
546
- function generateValidationMiddleware(_generatedClient, _context, options) {
650
+ function generateValidationMiddleware(_generatedClient, _context, options, errorName) {
547
651
  // Ensure z/ZodType import is present in client content
548
652
  if (!_generatedClient.content.includes("from 'zod'")) {
549
653
  _generatedClient.content =
550
- `import { z, ZodType } from 'zod';\n` + _generatedClient.content;
654
+ `import { z } from 'zod';\nimport type { ZodType } from 'zod';\n` + _generatedClient.content;
551
655
  }
552
656
  else if (!_generatedClient.content.includes("ZodType")) {
553
657
  // Append ZodType to existing import if only z is imported
@@ -562,83 +666,83 @@ function generateValidationMiddleware(_generatedClient, _context, options) {
562
666
  });
563
667
  }
564
668
  const strictModeCode = options.strictMode
565
- ? "throw new ValidationError(result.error?.issues ?? [], data);"
566
- : "// In non-strict mode, return data as-is";
567
- return `
568
- /**
569
- * Validation middleware for API requests and responses
570
- */
571
- export class ValidationError extends Error {
572
- public issues: readonly z.core.$ZodIssue[];
573
- constructor(issues: readonly z.core.$ZodIssue[], public data: unknown) {
574
- const message = issues
575
- .map((iss) => (iss.path && iss.path.length > 0 ? iss.path.join('.') + ': ' + iss.message : '(root): ' + iss.message))
576
- .join('; ');
577
- super('Validation failed: ' + message);
578
- this.name = 'ValidationError';
579
- this.issues = issues;
580
- Object.setPrototypeOf(this, ValidationError.prototype);
581
- }
582
- }
583
-
584
- /**
585
- * Validate request data before sending
586
- */
587
- export function validateRequest<T>(data: unknown, schema: ZodType<T>): T {
588
- const result = schema.safeParse(data);
589
-
590
- if (!result.success) {
591
- const issues = result.error?.issues ?? [];
592
- // Use Zod v4 core issue type
593
- throw new ValidationError(issues as readonly z.core.$ZodIssue[], data);
594
- }
595
-
596
- return result.data;
597
- }
598
-
599
- /**
600
- * Validate response data after receiving
601
- */
602
- export function validateResponse<T>(data: unknown, schema: ZodType<T>): T {
603
- const result = schema.safeParse(data);
604
-
605
- if (!result.success) {
606
- const issues = result.error?.issues ?? [];
607
- console.warn('Response validation failed:', issues);
608
- ${strictModeCode}
609
- }
610
-
611
- return result.data;
669
+ ? `throw new ${errorName}(result.error?.issues ?? [], data);`
670
+ : "return data as T;";
671
+ return `
672
+ /**
673
+ * Validation middleware for API requests and responses
674
+ */
675
+ export class ${errorName} extends Error {
676
+ public issues: readonly z.core.$ZodIssue[];
677
+ constructor(issues: readonly z.core.$ZodIssue[], public data: unknown) {
678
+ const message = issues
679
+ .map((iss) => (iss.path && iss.path.length > 0 ? iss.path.join('.') + ': ' + iss.message : '(root): ' + iss.message))
680
+ .join('; ');
681
+ super('Validation failed: ' + message);
682
+ this.name = '${errorName}';
683
+ this.issues = issues;
684
+ Object.setPrototypeOf(this, ${errorName}.prototype);
685
+ }
686
+ }
687
+
688
+ /**
689
+ * Validate request data before sending
690
+ */
691
+ export function validateRequest<T>(data: unknown, schema: ZodType<T>): T {
692
+ const result = schema.safeParse(data);
693
+
694
+ if (!result.success) {
695
+ const issues = result.error?.issues ?? [];
696
+ // Use Zod v4 core issue type
697
+ throw new ${errorName}(issues as readonly z.core.$ZodIssue[], data);
698
+ }
699
+
700
+ return result.data;
701
+ }
702
+
703
+ /**
704
+ * Validate response data after receiving
705
+ */
706
+ export function validateResponse<T>(data: unknown, schema: ZodType<T>): T {
707
+ const result = schema.safeParse(data);
708
+
709
+ if (!result.success) {
710
+ const issues = result.error?.issues ?? [];
711
+ console.warn('Response validation failed:', issues);
712
+ ${strictModeCode}
713
+ }
714
+
715
+ return result.data;
612
716
  }`;
613
717
  }
614
718
  /**
615
719
  * Generate validation helper functions
616
720
  */
617
721
  function generateValidationHelpers(_options) {
618
- return `
619
- /**
620
- * Utility functions for validation operations
621
- */
622
-
623
- /**
624
- * Create a validation pipeline for multiple schemas
625
- */
626
- export function createValidationPipeline<T>(...schemas: ZodType<unknown>[]): ZodType<T> {
627
- return schemas.reduce((acc, schema) => acc.pipe(schema)) as ZodType<T>;
628
- }
629
-
630
- /**
631
- * Lazy validation for performance optimization
632
- */
633
- export function createLazyValidator<T>(schemaFactory: () => ZodType<T>) {
634
- let schema: ZodType<T> | null = null;
635
-
636
- return (data: unknown): T => {
637
- if (!schema) {
638
- schema = schemaFactory();
639
- }
640
- return schema.parse(data);
641
- };
722
+ return `
723
+ /**
724
+ * Utility functions for validation operations
725
+ */
726
+
727
+ /**
728
+ * Create a validation pipeline for multiple schemas
729
+ */
730
+ export function createValidationPipeline<T>(...schemas: ZodType<unknown>[]): ZodType<T> {
731
+ return schemas.reduce((acc, schema) => acc.pipe(schema)) as ZodType<T>;
732
+ }
733
+
734
+ /**
735
+ * Lazy validation for performance optimization
736
+ */
737
+ export function createLazyValidator<T>(schemaFactory: () => ZodType<T>) {
738
+ let schema: ZodType<T> | null = null;
739
+
740
+ return (data: unknown): T => {
741
+ if (!schema) {
742
+ schema = schemaFactory();
743
+ }
744
+ return schema.parse(data);
745
+ };
642
746
  }`;
643
747
  }
644
748
  /**
@@ -720,16 +824,34 @@ class BoundedValidationCache {
720
824
  }
721
825
  }
722
826
  const validationCache = new BoundedValidationCache();
827
+ const validationSchemaIds = new WeakMap();
828
+ let nextValidationSchemaId = 0;
723
829
  function memoizedValidation(data, schema, cacheKey, schemaVersion = "1.0") {
724
- if (!cacheKey) {
830
+ // Cache primitive inputs/results only. Caller-owned objects and stateful references
831
+ // must be validated on every call rather than reusing mutable results.
832
+ if (!cacheKey || (data !== null && typeof data === "object") || typeof data === "function" || typeof data === "symbol") {
833
+ return schema.parse(data);
834
+ }
835
+ let schemaId = validationSchemaIds.get(schema);
836
+ if (schemaId === undefined) {
837
+ schemaId = ++nextValidationSchemaId;
838
+ validationSchemaIds.set(schema, schemaId);
839
+ }
840
+ let identity;
841
+ try {
842
+ identity = JSON.stringify([cacheKey, schemaId, schemaVersion, typeof data, typeof data === "number" && Object.is(data, -0) ? "-0" : String(data)]);
843
+ }
844
+ catch {
725
845
  return schema.parse(data);
726
846
  }
727
- const cached = validationCache.get(cacheKey);
847
+ const cached = validationCache.get(identity);
728
848
  if (cached && cached.schemaVersion === schemaVersion) {
729
849
  return cached.result;
730
850
  }
731
851
  const result = schema.parse(data);
732
- validationCache.set(cacheKey, {
852
+ if ((result !== null && typeof result === "object") || typeof result === "function" || typeof result === "symbol")
853
+ return result;
854
+ validationCache.set(identity, {
733
855
  result,
734
856
  timestamp: Date.now(),
735
857
  schemaVersion,
@@ -757,7 +879,7 @@ function getZodValidationPerformanceStats() {
757
879
  * Clear performance metrics and caches
758
880
  */
759
881
  function clearZodValidationCaches() {
760
- schemaCache.clear();
882
+ schemaCaches = new WeakMap();
761
883
  memoizedSchemaTransform.clear();
762
884
  performanceMetrics.clear();
763
885
  validationCache.clear();