@alt-stack/zod-openapi 1.1.2 → 1.2.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/index.js CHANGED
@@ -108,6 +108,87 @@ function topologicalSortSchemas(schemas) {
108
108
  return sorted;
109
109
  }
110
110
 
111
+ // src/schema-dedup.ts
112
+ function sortObjectDeep(obj) {
113
+ if (obj === null || typeof obj !== "object") return obj;
114
+ if (Array.isArray(obj)) return obj.map(sortObjectDeep);
115
+ const sorted = {};
116
+ const keys = Object.keys(obj).sort();
117
+ for (const key of keys) {
118
+ sorted[key] = sortObjectDeep(obj[key]);
119
+ }
120
+ return sorted;
121
+ }
122
+ function getSchemaFingerprint(schema) {
123
+ return JSON.stringify(sortObjectDeep(schema));
124
+ }
125
+ function createSchemaRegistry() {
126
+ return {
127
+ fingerprintToName: /* @__PURE__ */ new Map(),
128
+ nameToFingerprint: /* @__PURE__ */ new Map()
129
+ };
130
+ }
131
+ function registerSchema(registry, name, schema) {
132
+ const fingerprint = getSchemaFingerprint(schema);
133
+ const existing = registry.fingerprintToName.get(fingerprint);
134
+ if (existing) {
135
+ return { isNew: false, canonicalName: existing };
136
+ }
137
+ registry.fingerprintToName.set(fingerprint, name);
138
+ registry.nameToFingerprint.set(name, fingerprint);
139
+ return { isNew: true, canonicalName: name };
140
+ }
141
+ function preRegisterSchema(registry, name, fingerprint) {
142
+ registry.fingerprintToName.set(fingerprint, name);
143
+ registry.nameToFingerprint.set(name, fingerprint);
144
+ }
145
+ function extractErrorCode(schema) {
146
+ const properties = schema?.["properties"];
147
+ const errorObj = properties?.["error"];
148
+ const errorProps = errorObj?.["properties"];
149
+ const codeSchema = errorProps?.["code"];
150
+ const codeEnum = codeSchema?.["enum"];
151
+ if (Array.isArray(codeEnum) && codeEnum.length === 1) {
152
+ return codeEnum[0];
153
+ }
154
+ return null;
155
+ }
156
+ function errorCodeToPascalCase(code) {
157
+ return code.split("_").map((part) => part.charAt(0) + part.slice(1).toLowerCase()).join("");
158
+ }
159
+ function generateCommonErrorSchemaName(errorCode) {
160
+ return `${errorCodeToPascalCase(errorCode)}ErrorSchema`;
161
+ }
162
+ function findCommonSchemas(schemas, minCount = 2) {
163
+ const fingerprints = /* @__PURE__ */ new Map();
164
+ for (const { name, schema } of schemas) {
165
+ const fingerprint = getSchemaFingerprint(schema);
166
+ const existing = fingerprints.get(fingerprint);
167
+ if (existing) {
168
+ existing.names.push(name);
169
+ } else {
170
+ fingerprints.set(fingerprint, {
171
+ schema,
172
+ names: [name],
173
+ errorCode: extractErrorCode(schema)
174
+ });
175
+ }
176
+ }
177
+ const commonSchemas = [];
178
+ for (const [fingerprint, data] of fingerprints) {
179
+ if (data.names.length >= minCount) {
180
+ const name = data.errorCode ? generateCommonErrorSchemaName(data.errorCode) : data.names[0];
181
+ commonSchemas.push({
182
+ name,
183
+ schema: data.schema,
184
+ fingerprint,
185
+ count: data.names.length
186
+ });
187
+ }
188
+ }
189
+ return commonSchemas.sort((a, b) => b.count - a.count);
190
+ }
191
+
111
192
  // src/types/boolean.ts
112
193
  function convertOpenAPIBooleanToZod(_) {
113
194
  return "z.boolean()";
@@ -588,11 +669,150 @@ function generateRouteSchemaNames(route) {
588
669
  return result;
589
670
  }
590
671
 
591
- // src/to-typescript.ts
672
+ // src/interface-generator.ts
592
673
  var validIdentifierRegex2 = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
593
674
  function quotePropertyName2(name) {
594
675
  return validIdentifierRegex2.test(name) ? name : `'${name}'`;
595
676
  }
677
+ function schemaToTypeString(schema) {
678
+ if (!schema || typeof schema !== "object") return "unknown";
679
+ if (schema["$ref"] && typeof schema["$ref"] === "string") {
680
+ const match = schema["$ref"].match(
681
+ /#\/components\/schemas\/(.+)/
682
+ );
683
+ let result2 = "unknown";
684
+ if (match && match[1]) {
685
+ result2 = decodeURIComponent(match[1]);
686
+ }
687
+ if (schema["nullable"] === true) {
688
+ result2 = `(${result2} | null)`;
689
+ }
690
+ return result2;
691
+ }
692
+ let result = "unknown";
693
+ if ("oneOf" in schema && Array.isArray(schema["oneOf"])) {
694
+ const unionMembers = schema["oneOf"].map(
695
+ (s) => schemaToTypeString(s)
696
+ );
697
+ result = unionMembers.length > 1 ? `(${unionMembers.join(" | ")})` : unionMembers[0] ?? "unknown";
698
+ } else if ("allOf" in schema && Array.isArray(schema["allOf"])) {
699
+ const intersectionMembers = schema["allOf"].map(
700
+ (s) => schemaToTypeString(s)
701
+ );
702
+ result = intersectionMembers.length > 1 ? `(${intersectionMembers.join(" & ")})` : intersectionMembers[0] ?? "unknown";
703
+ } else if ("anyOf" in schema && Array.isArray(schema["anyOf"])) {
704
+ const unionMembers = schema["anyOf"].map(
705
+ (s) => schemaToTypeString(s)
706
+ );
707
+ result = unionMembers.length > 1 ? `(${unionMembers.join(" | ")})` : unionMembers[0] ?? "unknown";
708
+ } else {
709
+ switch (schema["type"]) {
710
+ case "string":
711
+ if (schema["enum"] && Array.isArray(schema["enum"])) {
712
+ result = schema["enum"].map((v) => JSON.stringify(v)).join(" | ");
713
+ } else {
714
+ result = "string";
715
+ }
716
+ break;
717
+ case "number":
718
+ case "integer":
719
+ if (schema["enum"] && Array.isArray(schema["enum"])) {
720
+ result = schema["enum"].map((v) => String(v)).join(" | ");
721
+ } else {
722
+ result = "number";
723
+ }
724
+ break;
725
+ case "boolean":
726
+ result = "boolean";
727
+ break;
728
+ case "null":
729
+ result = "null";
730
+ break;
731
+ case "array":
732
+ if (schema["items"]) {
733
+ const itemType = schemaToTypeString(schema["items"]);
734
+ result = `Array<${itemType}>`;
735
+ } else {
736
+ result = "unknown[]";
737
+ }
738
+ break;
739
+ case "object":
740
+ result = objectSchemaToTypeString(schema);
741
+ break;
742
+ default:
743
+ if (schema["properties"]) {
744
+ result = objectSchemaToTypeString(schema);
745
+ } else if (schema["enum"] && Array.isArray(schema["enum"])) {
746
+ result = schema["enum"].map((v) => JSON.stringify(v)).join(" | ");
747
+ } else {
748
+ result = "unknown";
749
+ }
750
+ break;
751
+ }
752
+ }
753
+ if (schema["nullable"] === true) {
754
+ result = `(${result} | null)`;
755
+ }
756
+ return result;
757
+ }
758
+ function objectSchemaToTypeString(schema) {
759
+ const properties = schema["properties"];
760
+ const required = new Set(schema["required"] ?? []);
761
+ const additionalProperties = schema["additionalProperties"];
762
+ if (!properties && !additionalProperties) {
763
+ return "Record<string, unknown>";
764
+ }
765
+ const propertyStrings = [];
766
+ if (properties) {
767
+ for (const [propName, propSchema] of Object.entries(properties)) {
768
+ const isRequired = required.has(propName);
769
+ const propType = schemaToTypeString(propSchema);
770
+ const quotedName = quotePropertyName2(propName);
771
+ propertyStrings.push(
772
+ `${quotedName}${isRequired ? "" : "?"}: ${propType}`
773
+ );
774
+ }
775
+ }
776
+ if (additionalProperties === true) {
777
+ propertyStrings.push("[key: string]: unknown");
778
+ } else if (typeof additionalProperties === "object" && additionalProperties !== null) {
779
+ const additionalType = schemaToTypeString(additionalProperties);
780
+ propertyStrings.push(`[key: string]: ${additionalType}`);
781
+ }
782
+ return `{ ${propertyStrings.join("; ")} }`;
783
+ }
784
+ function generateInterface(name, schema) {
785
+ const properties = schema["properties"];
786
+ const required = new Set(schema["required"] ?? []);
787
+ if (schema["type"] !== "object" && !properties) {
788
+ return `export type ${name} = ${schemaToTypeString(schema)};`;
789
+ }
790
+ const lines = [];
791
+ lines.push(`export interface ${name} {`);
792
+ if (properties) {
793
+ for (const [propName, propSchema] of Object.entries(properties)) {
794
+ const isRequired = required.has(propName);
795
+ const propType = schemaToTypeString(propSchema);
796
+ const quotedName = quotePropertyName2(propName);
797
+ lines.push(` ${quotedName}${isRequired ? "" : "?"}: ${propType};`);
798
+ }
799
+ }
800
+ const additionalProperties = schema["additionalProperties"];
801
+ if (additionalProperties === true) {
802
+ lines.push(" [key: string]: unknown;");
803
+ } else if (typeof additionalProperties === "object" && additionalProperties !== null) {
804
+ const additionalType = schemaToTypeString(additionalProperties);
805
+ lines.push(` [key: string]: ${additionalType};`);
806
+ }
807
+ lines.push("}");
808
+ return lines.join("\n");
809
+ }
810
+
811
+ // src/to-typescript.ts
812
+ var validIdentifierRegex3 = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
813
+ function quotePropertyName3(name) {
814
+ return validIdentifierRegex3.test(name) ? name : `'${name}'`;
815
+ }
596
816
  function generateRouteSchemaName2(path, method, suffix) {
597
817
  const pathParts = path.split("/").filter((p) => p).map((p) => {
598
818
  if (p.startsWith("{") && p.endsWith("}")) {
@@ -606,68 +826,138 @@ function generateRouteSchemaName2(path, method, suffix) {
606
826
  const parts = [methodPrefix, ...pathParts, suffix];
607
827
  return parts.join("");
608
828
  }
609
- function generateRouteSchemas(routes, convertSchema) {
610
- const lines = [];
611
- const schemaNames = /* @__PURE__ */ new Set();
829
+ function generateRouteSchemas(routes, convertSchema, registry) {
830
+ const declarations = [];
831
+ const schemaNameToCanonical = /* @__PURE__ */ new Map();
832
+ const generatedNames = /* @__PURE__ */ new Set();
612
833
  for (const route of routes) {
613
834
  const names = generateRouteSchemaNames(route);
614
835
  const pathParams = route.parameters.filter((p) => p.in === "path");
615
836
  const queryParams = route.parameters.filter((p) => p.in === "query");
616
837
  const headerParams = route.parameters.filter((p) => p.in === "header");
617
838
  if (names.paramsSchemaName && pathParams.length > 0) {
618
- if (!schemaNames.has(names.paramsSchemaName)) {
619
- schemaNames.add(names.paramsSchemaName);
839
+ const paramsSchema = {
840
+ type: "object",
841
+ properties: Object.fromEntries(
842
+ pathParams.map((p) => [p.name, p.schema])
843
+ ),
844
+ required: pathParams.filter((p) => p.required).map((p) => p.name)
845
+ };
846
+ const { isNew, canonicalName } = registerSchema(
847
+ registry,
848
+ names.paramsSchemaName,
849
+ paramsSchema
850
+ );
851
+ schemaNameToCanonical.set(names.paramsSchemaName, canonicalName);
852
+ if (isNew && !generatedNames.has(names.paramsSchemaName)) {
853
+ generatedNames.add(names.paramsSchemaName);
620
854
  const properties = [];
621
- const required = [];
622
855
  for (const param of pathParams) {
623
856
  const zodExpr = convertSchema(param.schema);
624
- properties.push(`${quotePropertyName2(param.name)}: ${zodExpr}`);
625
- if (param.required) {
626
- required.push(param.name);
627
- }
857
+ properties.push(`${quotePropertyName3(param.name)}: ${zodExpr}`);
628
858
  }
629
- lines.push(
859
+ declarations.push(
630
860
  `export const ${names.paramsSchemaName} = z.object({ ${properties.join(", ")} });`
631
861
  );
862
+ } else if (!isNew && names.paramsSchemaName !== canonicalName) {
863
+ if (!generatedNames.has(names.paramsSchemaName)) {
864
+ generatedNames.add(names.paramsSchemaName);
865
+ declarations.push(
866
+ `export const ${names.paramsSchemaName} = ${canonicalName};`
867
+ );
868
+ }
632
869
  }
633
870
  }
634
871
  if (names.querySchemaName && queryParams.length > 0) {
635
- if (!schemaNames.has(names.querySchemaName)) {
636
- schemaNames.add(names.querySchemaName);
872
+ const querySchema = {
873
+ type: "object",
874
+ properties: Object.fromEntries(
875
+ queryParams.map((p) => [p.name, p.schema])
876
+ ),
877
+ required: queryParams.filter((p) => p.required).map((p) => p.name)
878
+ };
879
+ const { isNew, canonicalName } = registerSchema(
880
+ registry,
881
+ names.querySchemaName,
882
+ querySchema
883
+ );
884
+ schemaNameToCanonical.set(names.querySchemaName, canonicalName);
885
+ if (isNew && !generatedNames.has(names.querySchemaName)) {
886
+ generatedNames.add(names.querySchemaName);
637
887
  const properties = [];
638
888
  for (const param of queryParams) {
639
889
  let zodExpr = convertSchema(param.schema);
640
890
  if (!param.required) {
641
891
  zodExpr += ".optional()";
642
892
  }
643
- properties.push(`${quotePropertyName2(param.name)}: ${zodExpr}`);
893
+ properties.push(`${quotePropertyName3(param.name)}: ${zodExpr}`);
644
894
  }
645
- lines.push(
895
+ declarations.push(
646
896
  `export const ${names.querySchemaName} = z.object({ ${properties.join(", ")} });`
647
897
  );
898
+ } else if (!isNew && names.querySchemaName !== canonicalName) {
899
+ if (!generatedNames.has(names.querySchemaName)) {
900
+ generatedNames.add(names.querySchemaName);
901
+ declarations.push(
902
+ `export const ${names.querySchemaName} = ${canonicalName};`
903
+ );
904
+ }
648
905
  }
649
906
  }
650
907
  if (names.headersSchemaName && headerParams.length > 0) {
651
- if (!schemaNames.has(names.headersSchemaName)) {
652
- schemaNames.add(names.headersSchemaName);
908
+ const headersSchema = {
909
+ type: "object",
910
+ properties: Object.fromEntries(
911
+ headerParams.map((p) => [p.name, p.schema])
912
+ ),
913
+ required: headerParams.filter((p) => p.required).map((p) => p.name)
914
+ };
915
+ const { isNew, canonicalName } = registerSchema(
916
+ registry,
917
+ names.headersSchemaName,
918
+ headersSchema
919
+ );
920
+ schemaNameToCanonical.set(names.headersSchemaName, canonicalName);
921
+ if (isNew && !generatedNames.has(names.headersSchemaName)) {
922
+ generatedNames.add(names.headersSchemaName);
653
923
  const properties = [];
654
924
  for (const param of headerParams) {
655
925
  let zodExpr = convertSchema(param.schema);
656
926
  if (!param.required) {
657
927
  zodExpr += ".optional()";
658
928
  }
659
- properties.push(`${quotePropertyName2(param.name)}: ${zodExpr}`);
929
+ properties.push(`${quotePropertyName3(param.name)}: ${zodExpr}`);
660
930
  }
661
- lines.push(
931
+ declarations.push(
662
932
  `export const ${names.headersSchemaName} = z.object({ ${properties.join(", ")} });`
663
933
  );
934
+ } else if (!isNew && names.headersSchemaName !== canonicalName) {
935
+ if (!generatedNames.has(names.headersSchemaName)) {
936
+ generatedNames.add(names.headersSchemaName);
937
+ declarations.push(
938
+ `export const ${names.headersSchemaName} = ${canonicalName};`
939
+ );
940
+ }
664
941
  }
665
942
  }
666
943
  if (names.bodySchemaName && route.requestBody) {
667
- if (!schemaNames.has(names.bodySchemaName)) {
668
- schemaNames.add(names.bodySchemaName);
944
+ const { isNew, canonicalName } = registerSchema(
945
+ registry,
946
+ names.bodySchemaName,
947
+ route.requestBody
948
+ );
949
+ schemaNameToCanonical.set(names.bodySchemaName, canonicalName);
950
+ if (isNew && !generatedNames.has(names.bodySchemaName)) {
951
+ generatedNames.add(names.bodySchemaName);
669
952
  const zodExpr = convertSchema(route.requestBody);
670
- lines.push(`export const ${names.bodySchemaName} = ${zodExpr};`);
953
+ declarations.push(`export const ${names.bodySchemaName} = ${zodExpr};`);
954
+ } else if (!isNew && names.bodySchemaName !== canonicalName) {
955
+ if (!generatedNames.has(names.bodySchemaName)) {
956
+ generatedNames.add(names.bodySchemaName);
957
+ declarations.push(
958
+ `export const ${names.bodySchemaName} = ${canonicalName};`
959
+ );
960
+ }
671
961
  }
672
962
  }
673
963
  for (const [statusCode, responseSchema] of Object.entries(
@@ -681,19 +971,35 @@ function generateRouteSchemas(routes, convertSchema) {
681
971
  route.method,
682
972
  suffix
683
973
  );
684
- if (!schemaNames.has(responseSchemaName)) {
685
- schemaNames.add(responseSchemaName);
974
+ const { isNew, canonicalName } = registerSchema(
975
+ registry,
976
+ responseSchemaName,
977
+ responseSchema
978
+ );
979
+ schemaNameToCanonical.set(responseSchemaName, canonicalName);
980
+ if (isNew && !generatedNames.has(responseSchemaName)) {
981
+ generatedNames.add(responseSchemaName);
686
982
  const zodExpr = convertSchema(responseSchema);
687
- lines.push(`export const ${responseSchemaName} = ${zodExpr};`);
983
+ declarations.push(`export const ${responseSchemaName} = ${zodExpr};`);
984
+ } else if (!isNew && responseSchemaName !== canonicalName) {
985
+ if (!generatedNames.has(responseSchemaName)) {
986
+ generatedNames.add(responseSchemaName);
987
+ declarations.push(
988
+ `export const ${responseSchemaName} = ${canonicalName};`
989
+ );
990
+ }
688
991
  }
689
992
  }
690
993
  }
691
- return lines;
994
+ return { declarations, schemaNameToCanonical };
692
995
  }
693
- function generateRequestResponseObjects(routes) {
996
+ function generateRequestResponseObjects(routes, schemaNameToCanonical) {
694
997
  const lines = [];
695
998
  const requestPaths = {};
696
999
  const responsePaths = {};
1000
+ const resolveSchemaName = (name) => {
1001
+ return schemaNameToCanonical.get(name) ?? name;
1002
+ };
697
1003
  for (const route of routes) {
698
1004
  const names = generateRouteSchemaNames(route);
699
1005
  const pathParams = route.parameters.filter((p) => p.in === "path");
@@ -708,16 +1014,20 @@ function generateRequestResponseObjects(routes) {
708
1014
  }
709
1015
  const requestParts = [];
710
1016
  if (names.paramsSchemaName && pathParams.length > 0) {
711
- requestParts.push(`params: ${names.paramsSchemaName}`);
1017
+ requestParts.push(
1018
+ `params: ${resolveSchemaName(names.paramsSchemaName)}`
1019
+ );
712
1020
  }
713
1021
  if (names.querySchemaName && queryParams.length > 0) {
714
- requestParts.push(`query: ${names.querySchemaName}`);
1022
+ requestParts.push(`query: ${resolveSchemaName(names.querySchemaName)}`);
715
1023
  }
716
1024
  if (names.headersSchemaName && headerParams.length > 0) {
717
- requestParts.push(`headers: ${names.headersSchemaName}`);
1025
+ requestParts.push(
1026
+ `headers: ${resolveSchemaName(names.headersSchemaName)}`
1027
+ );
718
1028
  }
719
1029
  if (names.bodySchemaName && route.requestBody) {
720
- requestParts.push(`body: ${names.bodySchemaName}`);
1030
+ requestParts.push(`body: ${resolveSchemaName(names.bodySchemaName)}`);
721
1031
  }
722
1032
  if (requestParts.length > 0) {
723
1033
  requestMethodObj[route.method] = requestParts;
@@ -740,7 +1050,7 @@ function generateRequestResponseObjects(routes) {
740
1050
  route.method,
741
1051
  suffix
742
1052
  );
743
- responseMethodObj[route.method][statusCode] = responseSchemaName;
1053
+ responseMethodObj[route.method][statusCode] = resolveSchemaName(responseSchemaName);
744
1054
  }
745
1055
  }
746
1056
  lines.push("export const Request = {");
@@ -780,6 +1090,25 @@ function generateRequestResponseObjects(routes) {
780
1090
  lines.push("} as const;");
781
1091
  return lines;
782
1092
  }
1093
+ function collectRouteSchemas(routes) {
1094
+ const collected = [];
1095
+ for (const route of routes) {
1096
+ for (const [statusCode, responseSchema] of Object.entries(
1097
+ route.responses
1098
+ )) {
1099
+ if (!responseSchema) continue;
1100
+ const isSuccess = statusCode.startsWith("2");
1101
+ const suffix = isSuccess ? `${statusCode}Response` : `${statusCode}ErrorResponse`;
1102
+ const responseSchemaName = generateRouteSchemaName2(
1103
+ route.path,
1104
+ route.method,
1105
+ suffix
1106
+ );
1107
+ collected.push({ name: responseSchemaName, schema: responseSchema });
1108
+ }
1109
+ }
1110
+ return collected;
1111
+ }
783
1112
  var openApiToZodTsCode = (openapi, customImportLines, options) => {
784
1113
  const components = openapi["components"];
785
1114
  const schemas = components?.["schemas"] ?? {};
@@ -792,29 +1121,58 @@ var openApiToZodTsCode = (openapi, customImportLines, options) => {
792
1121
  lines.push("import { z } from 'zod';");
793
1122
  lines.push(...customImportLines ?? []);
794
1123
  lines.push("");
1124
+ lines.push("// Type assertion helper - verifies interface matches schema at compile time");
1125
+ lines.push("type _AssertEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : never) : never;");
1126
+ lines.push("");
1127
+ const registry = createSchemaRegistry();
795
1128
  const sortedSchemaNames = topologicalSortSchemas(schemas);
1129
+ const typeAssertions = [];
796
1130
  for (const name of sortedSchemaNames) {
797
1131
  const schema = schemas[name];
798
1132
  if (schema) {
799
1133
  const zodExpr = convertSchemaToZodString(schema);
800
1134
  const schemaName = `${name}Schema`;
801
1135
  const typeName = name;
802
- lines.push(`export const ${schemaName} = ${zodExpr};`);
803
- lines.push(`export type ${typeName} = z.infer<typeof ${schemaName}>;`);
1136
+ lines.push(generateInterface(typeName, schema));
1137
+ lines.push(`export const ${schemaName}: z.ZodType<${typeName}> = ${zodExpr};`);
804
1138
  lines.push("");
1139
+ typeAssertions.push(`type _Assert${typeName} = _AssertEqual<${typeName}, z.infer<typeof ${schemaName}>>;`);
1140
+ const fingerprint = getSchemaFingerprint(schema);
1141
+ preRegisterSchema(registry, schemaName, fingerprint);
805
1142
  }
806
1143
  }
1144
+ if (typeAssertions.length > 0) {
1145
+ lines.push("// Compile-time type assertions - ensure interfaces match schemas");
1146
+ lines.push(typeAssertions.join("\n"));
1147
+ lines.push("");
1148
+ }
807
1149
  if (options?.includeRoutes) {
808
1150
  const routes = parseOpenApiPaths(openapi);
809
1151
  if (routes.length > 0) {
810
- const routeSchemas = generateRouteSchemas(
1152
+ const routeSchemaList = collectRouteSchemas(routes);
1153
+ const commonSchemas = findCommonSchemas(routeSchemaList, 2);
1154
+ if (commonSchemas.length > 0) {
1155
+ lines.push("// Common Error Schemas (deduplicated)");
1156
+ for (const common of commonSchemas) {
1157
+ const zodExpr = convertSchemaToZodString(common.schema);
1158
+ lines.push(`export const ${common.name} = ${zodExpr};`);
1159
+ preRegisterSchema(registry, common.name, common.fingerprint);
1160
+ }
1161
+ lines.push("");
1162
+ }
1163
+ const { declarations, schemaNameToCanonical } = generateRouteSchemas(
811
1164
  routes,
812
- convertSchemaToZodString
1165
+ convertSchemaToZodString,
1166
+ registry
813
1167
  );
814
- if (routeSchemas.length > 0) {
815
- lines.push(...routeSchemas);
1168
+ if (declarations.length > 0) {
1169
+ lines.push("// Route Schemas");
1170
+ lines.push(...declarations);
816
1171
  lines.push("");
817
- const requestResponseObjs = generateRequestResponseObjects(routes);
1172
+ const requestResponseObjs = generateRequestResponseObjects(
1173
+ routes,
1174
+ schemaNameToCanonical
1175
+ );
818
1176
  lines.push(...requestResponseObjs);
819
1177
  }
820
1178
  }
@@ -825,11 +1183,13 @@ export {
825
1183
  SUPPORTED_STRING_FORMATS,
826
1184
  clearZodSchemaToOpenApiSchemaRegistry,
827
1185
  convertSchemaToZodString,
1186
+ generateInterface,
828
1187
  generateRouteSchemaNames,
829
1188
  getSchemaExportedVariableNameForStringFormat,
830
1189
  openApiToZodTsCode,
831
1190
  parseOpenApiPaths,
832
1191
  registerZodSchemaToOpenApiSchema,
833
- schemaRegistry
1192
+ schemaRegistry,
1193
+ schemaToTypeString
834
1194
  };
835
1195
  //# sourceMappingURL=index.js.map